diff --git a/.codex/agents/code-quality.md b/.codex/agents/code-quality.md new file mode 100644 index 0000000..92ac4dc --- /dev/null +++ b/.codex/agents/code-quality.md @@ -0,0 +1,65 @@ +--- +name: code-quality +model: composer-1.5 +description: Code quality specialist that runs build and tests, then fixes any errors it finds. Use proactively after code changes to verify nothing is broken. +--- + +You are a code quality verifier for the bitsocial-react-hooks project. You run the project's quality checks, fix any issues found, and report results back to the parent agent. + +## Workflow + +### Step 1: Run Quality Checks + +Execute these commands and capture all output: + +```bash +yarn build 2>&1 +yarn test 2>&1 +``` + +### Step 2: Analyze Failures + +If any check fails, read the error output carefully: + +- Identify the file(s) and line(s) causing the failure +- Determine the root cause (not just the symptom) +- Prioritize: build errors > test failures + +### Step 3: Fix Issues + +For each failure: + +1. Read the affected file to understand context +2. Check git history for the affected lines (`git log --oneline -5 -- `) to avoid reverting intentional code +3. Apply the minimal fix that resolves the error +4. Follow project patterns from AGENTS.md (Zustand stores for state, thin hook wrappers, derive state during render) + +### Step 4: Re-verify + +After fixing, re-run the failed check(s) to confirm resolution. If new errors appear, fix those too. Loop until all checks pass or you've exhausted reasonable attempts (max 3 loops). + +### Step 5: Report Back + +Return a structured report: + +``` +## Quality Check Results + +### Build: PASS/FAIL +### Tests: PASS/FAIL + +### Fixes Applied +- `path/to/file.ts` — description of fix + +### Remaining Issues (if any) +- description of issue that couldn't be auto-fixed + +### Status: SUCCESS / PARTIAL / FAILED +``` + +## Constraints + +- Only fix issues surfaced by the quality checks — don't refactor unrelated code +- Pin exact package versions if dependency changes are needed (no carets) +- Use `yarn`, not `npm` +- If a fix is unclear or risky, report it as a remaining issue instead of guessing diff --git a/.codex/agents/plan-implementer.md b/.codex/agents/plan-implementer.md new file mode 100644 index 0000000..6f48206 --- /dev/null +++ b/.codex/agents/plan-implementer.md @@ -0,0 +1,71 @@ +--- +name: plan-implementer +model: composer-1.5 +description: Implements assigned tasks from a plan. Receives specific tasks from the parent agent, implements them sequentially, verifies with a build check, and reports back. The parent agent handles parallelization by spawning multiple plan-implementer subagents with different task subsets. +--- + +You are a plan implementer for the bitsocial-react-hooks project. You receive specific tasks from the parent agent and implement them. The parent agent handles parallelization by spawning multiple instances of you with different task subsets. + +## Required Input + +You MUST receive from the parent agent: + +1. **One or more specific tasks** with enough detail to implement independently +2. **Context**: file paths, requirements, expected behavior + +If the task description is too vague to act on, report back asking for clarification. + +## Workflow + +### Step 1: Understand the Tasks + +Read the task description(s) carefully. For each task: + +- Identify the file(s) to modify or create +- Understand the expected behavior +- Note any constraints + +### Step 2: Implement + +For each task: + +1. Read the affected file(s) to understand current state +2. Check git history for affected lines (`git log --oneline -5 -- `) to avoid reverting intentional code +3. Apply changes following project patterns from AGENTS.md +4. Verify the change makes sense in context + +### Step 3: Verify + +After implementing all assigned tasks: + +```bash +yarn build 2>&1 +``` + +If build errors relate to your changes, fix them and re-run. Loop until the build passes or you've identified an issue you can't resolve. + +### Step 4: Report Back + +``` +## Implementation Report + +### Tasks Completed +- [x] Task description — files modified + +### Tasks Failed (if any) +- [ ] Task description — reason for failure + +### Verification +- Build: PASS/FAIL + +### Status: SUCCESS / PARTIAL / FAILED +``` + +## Constraints + +- Implement only the tasks assigned to you — don't expand scope +- Follow project patterns from AGENTS.md +- If a task conflicts with existing code, report the conflict instead of guessing +- Pin exact package versions if dependency changes are needed (no carets) +- Use `yarn`, not `npm` +- Do not rename `plebbit`/`subplebbit` terms — rebranding is not yet in scope diff --git a/.codex/agents/react-patterns-enforcer.md b/.codex/agents/react-patterns-enforcer.md new file mode 100644 index 0000000..575b7cd --- /dev/null +++ b/.codex/agents/react-patterns-enforcer.md @@ -0,0 +1,77 @@ +--- +name: react-patterns-enforcer +model: composer-1.5 +description: Reviews React hooks and store code for anti-pattern violations specific to bitsocial-react-hooks (effect misuse, store patterns, public API consistency) and fixes them. Use after writing or modifying hooks, stores, or the public API. +--- + +You are a React patterns reviewer for the bitsocial-react-hooks project. You review recent code changes for anti-pattern violations defined in AGENTS.md and fix them. + +## Workflow + +### Step 1: Identify Changed Files + +Check what was recently modified (the parent agent may specify files, or use): + +```bash +git diff --name-only HEAD~1 -- '*.ts' +``` + +Focus on files in `src/hooks/`, `src/stores/`, `src/lib/`. + +### Step 2: Review for Violations + +Read each changed file and check for these project-critical anti-patterns: + +| Violation | Fix | +|-----------|-----| +| `useEffect` for data fetching inside a hook | Use store subscriptions and event listeners instead | +| `useEffect` syncing derived state | Calculate during render | +| Hook does too many things | Split into focused composable hooks | +| Store action mixed with store state selection | Separate actions from selectors | +| Missing public API export | Add to `src/index.ts` | +| Type defined inline instead of in `src/types.ts` | Move cross-module types to `src/types.ts` | +| Unnecessary `any` casts | Fix the underlying type | +| Effects without cleanup | Add cleanup function | + +### Step 3: Fix Violations + +For each violation: + +1. Read enough surrounding context to understand the module's purpose +2. Check git history (`git log --oneline -5 -- `) to avoid reverting intentional code +3. Apply the minimal fix from the table above +4. Ensure the fix doesn't break existing behavior + +### Step 4: Verify + +```bash +yarn build 2>&1 +``` + +If the build breaks due to your changes, fix and re-run. + +### Step 5: Report Back + +``` +## React Patterns Review + +### Files Reviewed +- `path/to/file.ts` + +### Violations Found & Fixed +- `file.ts:42` — useEffect syncing derived state → computed during render + +### Violations Found (unfixed) +- `file.ts:100` — description and why it wasn't auto-fixed + +### Build: PASS/FAIL +### Status: SUCCESS / PARTIAL / FAILED +``` + +## Constraints + +- Only fix pattern violations — don't refactor unrelated code +- Follow patterns defined in AGENTS.md +- If a fix would require significant restructuring, report it instead of applying it +- Use `yarn`, not `npm` +- Do not rename `plebbit`/`subplebbit` terms diff --git a/.codex/hooks.json b/.codex/hooks.json new file mode 100644 index 0000000..e779144 --- /dev/null +++ b/.codex/hooks.json @@ -0,0 +1,25 @@ +{ + "version": 1, + "hooks": { + "afterFileEdit": [ + { + "command": ".codex/hooks/format.sh", + "timeout": 10 + }, + { + "command": ".codex/hooks/yarn-install.sh", + "timeout": 120 + } + ], + "stop": [ + { + "command": ".codex/hooks/sync-git-branches.sh", + "timeout": 60 + }, + { + "command": ".codex/hooks/verify.sh", + "timeout": 60 + } + ] + } +} diff --git a/.codex/hooks/format.sh b/.codex/hooks/format.sh new file mode 100755 index 0000000..45842f7 --- /dev/null +++ b/.codex/hooks/format.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# afterFileEdit hook: Auto-format files after AI edits them +# Receives JSON via stdin: {"file_path": "...", "edits": [...]} + +# Read stdin (required for hooks) +input=$(cat) + +# Extract file_path using grep/sed (jq-free for portability) +file_path=$(echo "$input" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/') + +# Exit if no file path found +if [ -z "$file_path" ]; then + exit 0 +fi + +# Only format JS/TS files +case "$file_path" in + *.js|*.ts|*.tsx|*.mjs|*.cjs) + npx prettier --config config/prettier.config.js --write "$file_path" 2>/dev/null || true + ;; +esac + +exit 0 diff --git a/.codex/hooks/sync-git-branches.sh b/.codex/hooks/sync-git-branches.sh new file mode 100755 index 0000000..f365e03 --- /dev/null +++ b/.codex/hooks/sync-git-branches.sh @@ -0,0 +1,109 @@ +#!/bin/bash +# stop hook: prune stale remote refs and remove integrated temporary local branches +# This is informational - always exits 0 + +# Consume stdin (required for hooks) +cat > /dev/null + +cd "$(dirname "$0")/../.." || exit 0 + +if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + exit 0 +fi + +default_branch="$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | sed 's#^origin/##')" +if [ -z "$default_branch" ]; then + default_branch="master" +fi + +current_branch="$(git branch --show-current 2>/dev/null || true)" + +branch_looks_temporary() { + case "$1" in + pr/*|feature/*|fix/*|docs/*|chore/*) return 0 ;; + *) return 1 ;; + esac +} + +branch_is_integrated() { + local branch="$1" + local cherry_output + + cherry_output="$(git cherry "$default_branch" "$branch" 2>/dev/null || true)" + if echo "$cherry_output" | grep -q '^+'; then + return 1 + fi + + return 0 +} + +branch_has_live_upstream() { + local upstream="$1" + [ -n "$upstream" ] && git show-ref --verify --quiet "refs/remotes/$upstream" +} + +merged_pr_number_for_branch() { + local branch="$1" + local pr_number="" + + if ! command -v gh >/dev/null 2>&1; then + return 0 + fi + + case "$branch" in + pr/*) + pr_number="${branch#pr/}" + gh pr view "$pr_number" --repo bitsocialnet/bitsocial-react-hooks --json mergedAt --jq 'select(.mergedAt != null) | .mergedAt' >/dev/null 2>&1 || return 0 + echo "$pr_number" + return 0 + ;; + esac + + gh pr list --repo bitsocialnet/bitsocial-react-hooks --state merged --head "$branch" --json number --jq '.[0].number // empty' 2>/dev/null || true +} + +echo "Syncing git refs and temporary branches..." +echo "" + +echo "=== git config --local fetch.prune true ===" +git config --local fetch.prune true 2>&1 || true +echo "" + +echo "=== git config --local remote.origin.prune true ===" +git config --local remote.origin.prune true 2>&1 || true +echo "" + +echo "=== git fetch --prune origin ===" +git fetch --prune origin 2>&1 || true +echo "" + +while IFS='|' read -r branch upstream; do + local_pr_number="" + + [ -z "$branch" ] && continue + [ "$branch" = "$current_branch" ] && continue + [ "$branch" = "$default_branch" ] && continue + + branch_looks_temporary "$branch" || continue + local_pr_number="$(merged_pr_number_for_branch "$branch")" + + if branch_has_live_upstream "$upstream"; then + continue + fi + + if ! branch_is_integrated "$branch" && [ -z "$local_pr_number" ]; then + continue + fi + + if [ -n "$local_pr_number" ]; then + echo "=== merged PR #$local_pr_number allows deleting $branch ===" + echo "" + fi + + echo "=== git branch -D $branch ===" + git branch -D "$branch" 2>&1 || true + echo "" +done < <(git for-each-ref --format='%(refname:short)|%(upstream:short)' refs/heads) + +echo "Git ref sync complete." +exit 0 diff --git a/.codex/hooks/verify.sh b/.codex/hooks/verify.sh new file mode 100755 index 0000000..556ffb9 --- /dev/null +++ b/.codex/hooks/verify.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# stop hook: Run build and tests when agent finishes +# This is informational - always exits 0 + +# Consume stdin (required for hooks) +cat > /dev/null + +# Change to project directory +cd "$(dirname "$0")/../.." || exit 0 + +DIST_STATUS_BEFORE="" +if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && + git ls-files --error-unmatch dist >/dev/null 2>&1; then + # Only clean up dist when verification started from a clean baseline. + DIST_STATUS_BEFORE="$(git status --porcelain=v1 --ignored=matching --untracked-files=all -- dist 2>/dev/null || true)" +fi + +restore_dist_worktree() { + if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + return + fi + + if ! git ls-files --error-unmatch dist >/dev/null 2>&1; then + return + fi + + if [ -n "$DIST_STATUS_BEFORE" ]; then + return + fi + + local dist_status_after="" + dist_status_after="$(git status --porcelain=v1 --ignored=matching --untracked-files=all -- dist 2>/dev/null || true)" + if [ -z "$dist_status_after" ]; then + return + fi + + echo "=== git restore --worktree dist ===" + git restore --worktree -- dist 2>&1 || true + git clean -fdX -- dist 2>&1 || true + echo "" +} + +echo "Running build and tests..." +echo "" + +# Run build (catches compilation errors) +echo "=== yarn build ===" +yarn build 2>&1 || true +echo "" + +# Run tests +echo "=== yarn test ===" +yarn test 2>&1 || true +echo "" + +restore_dist_worktree + +echo "Verification complete." +exit 0 diff --git a/.codex/hooks/yarn-install.sh b/.codex/hooks/yarn-install.sh new file mode 100755 index 0000000..7e91722 --- /dev/null +++ b/.codex/hooks/yarn-install.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# afterFileEdit hook: Run yarn install when package.json is changed +# Receives JSON via stdin: {"file_path": "...", "edits": [...]} + +# Read stdin (required for hooks) +input=$(cat) + +# Extract file_path using grep/sed (jq-free for portability) +file_path=$(echo "$input" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/') + +# Exit if no file path found +if [ -z "$file_path" ]; then + exit 0 +fi + +# Only run yarn install if package.json was changed +if [ "$file_path" = "package.json" ]; then + # Change to project directory + cd "$(dirname "$0")/../.." || exit 0 + + echo "package.json changed - running yarn install to update yarn.lock..." + yarn install +fi + +exit 0 diff --git a/.codex/skills/commit-format/SKILL.md b/.codex/skills/commit-format/SKILL.md new file mode 100644 index 0000000..94c765e --- /dev/null +++ b/.codex/skills/commit-format/SKILL.md @@ -0,0 +1,55 @@ +--- +name: commit-format +description: Formats GitHub commit messages following Conventional Commits style with title and optional description. Use when proposing or implementing code changes, writing commit messages, or when the user asks for commit message suggestions. +--- + +# Commit Format + +## Template (copy this structure exactly) + +Title only — raw markdown: +``` +> **Commit title:** `type: short description here` +``` + +Title with description — raw markdown: +``` +> **Commit title:** `type: short description here` +> +> Description sentence one. Description sentence two with `codeRef()` references. +``` + +## Rules + +1. Use markdown blockquote (`>` prefix) — no exceptions +2. Title goes after `**Commit title:**` wrapped in exactly ONE backtick pair +3. NEVER put backticks inside the title — the whole title is one code span, no nesting +4. Description uses backticks for code references — title does NOT +5. Conventional Commits types: `fix:`, `feat:`, `perf:`, `refactor:`, `docs:`, `chore:` +6. Use `perf:` for performance optimizations (not `fix:`) +7. Description: 2-3 sentences about the solution, no bullet points, only if title isn't enough + +## Wrong vs Right + +❌ WRONG — missing backticks around title: +``` +> **Commit title:** refactor: simplify feeds store +``` + +❌ WRONG — backticks around individual words instead of whole title: +``` +> **Commit title:** refactor: simplify `feeds` store +``` + +✅ CORRECT — entire title in one backtick pair, no backticks inside: +``` +> **Commit title:** `refactor: simplify feeds store` +``` + +## Self-check + +Before outputting, verify: +- [ ] Lines start with `>` +- [ ] Title is wrapped in exactly one backtick pair: `` `like this` `` +- [ ] No backticks inside the title text +- [ ] Code references in description (not title) use backticks diff --git a/.codex/skills/commit/SKILL.md b/.codex/skills/commit/SKILL.md new file mode 100644 index 0000000..4c36118 --- /dev/null +++ b/.codex/skills/commit/SKILL.md @@ -0,0 +1,57 @@ +--- +name: commit +description: Commit current work by reviewing diffs, splitting into logical commits, and writing standardized messages. Use when the user says "commit", "commit this", "commit current work", or asks to create a git commit. +disable-model-invocation: true +--- + +# Commit Current Work + +## Workflow + +1. **Review all uncommitted changes** + + ```bash + git status + git diff + git diff --cached + ``` + + Read every changed file's diff to understand the full scope of changes. + +2. **Group changes into logical commits** + + If diffs are unrelated, split into multiple commits. Each commit should cover one logical unit of work. + + Example — two unrelated changes in the working tree: + - Modified `src/hooks/comments.ts` (bug fix) + - Modified `src/stores/feeds/feeds-store.ts` (new feature) + + These should be two separate commits, not one. + +3. **Stage and commit each group** + + For each logical group: + ```bash + git add + git commit -m "title here" + ``` + +4. **Display the commit title to the user** wrapped in backticks (inline code). + +## Commit Message Rules + +- **Title format:** Conventional Commits with a **required scope**. The scope should be a short, human-readable name for the area of the codebase affected. + + | Pattern | Example | + |---------|---------| + | `type(scope): description` | `fix(feeds): handle empty subplebbit addresses array` | + +- **Never omit the scope.** `feat: add hook` is wrong. `feat(replies): add flat mode to useReplies` is correct. +- **Keep titles short.** If more context is needed, add a commit body — but don't repeat the title. +- **Use `perf:` for performance optimizations**, not `fix:`. + +## Constraints + +- Only commit when instructed. Do not commit subsequent changes unless explicitly told to. +- Never push — only commit locally. +- Never amend commits that have been pushed to a remote. diff --git a/.codex/skills/context7/SKILL.md b/.codex/skills/context7/SKILL.md new file mode 100644 index 0000000..9e1b91d --- /dev/null +++ b/.codex/skills/context7/SKILL.md @@ -0,0 +1,67 @@ +--- +name: context7 +description: Retrieve up-to-date documentation for software libraries, frameworks, and components via the Context7 API. This skill should be used when looking up documentation for any programming library or framework, finding code examples for specific APIs or features, verifying correct usage of library functions, or obtaining current information about library APIs that may have changed since training. +--- + +# Context7 + +## Overview + +This skill enables retrieval of current documentation for software libraries and components by querying the Context7 API via curl. Use it instead of relying on potentially outdated training data. + +## Workflow + +### Step 1: Search for the Library + +To find the Context7 library ID, query the search endpoint: + +```bash +curl -s "https://context7.com/api/v2/libs/search?libraryName=LIBRARY_NAME&query=TOPIC" | jq '.results[0]' +``` + +**Parameters:** +- `libraryName` (required): The library name to search for (e.g., "react", "zustand", "vitest", "ethers") +- `query` (required): A description of the topic for relevance ranking + +**Response fields:** +- `id`: Library identifier for the context endpoint +- `title`: Human-readable library name +- `description`: Brief description of the library +- `totalSnippets`: Number of documentation snippets available + +### Step 2: Fetch Documentation + +To retrieve documentation, use the library ID from step 1: + +```bash +curl -s "https://context7.com/api/v2/context?libraryId=LIBRARY_ID&query=TOPIC&type=txt" +``` + +**Parameters:** +- `libraryId` (required): The library ID from search results +- `query` (required): The specific topic to retrieve documentation for +- `type` (optional): Response format - `json` (default) or `txt` (plain text, more readable) + +## Examples + +### Zustand store documentation + +```bash +curl -s "https://context7.com/api/v2/libs/search?libraryName=zustand&query=store" | jq '.results[0].id' +curl -s "https://context7.com/api/v2/context?libraryId=/pmndrs/zustand&query=create+store&type=txt" +``` + +### Vitest testing + +```bash +curl -s "https://context7.com/api/v2/libs/search?libraryName=vitest&query=testing" | jq '.results[0].id' +curl -s "https://context7.com/api/v2/context?libraryId=/vitest-dev/vitest&query=mock+functions&type=txt" +``` + +## Tips + +- Use `type=txt` for more readable output +- Use `jq` to filter and format JSON responses +- Be specific with the `query` parameter to improve relevance ranking +- URL-encode query parameters containing spaces (use `+` or `%20`) +- No API key is required for basic usage (rate-limited) diff --git a/.codex/skills/deslop/SKILL.md b/.codex/skills/deslop/SKILL.md new file mode 100644 index 0000000..40d55a5 --- /dev/null +++ b/.codex/skills/deslop/SKILL.md @@ -0,0 +1,75 @@ +--- +name: deslop +description: Scan recent changes for AI-generated code slop and remove it. Use when the user says "deslop", "remove slop", "clean up AI code", or asks to remove AI-generated artifacts from the codebase. +disable-model-invocation: true +--- + +# Remove AI Code Slop + +Scan the diff against main and remove AI-generated slop introduced in this branch. + +## Workflow + +1. **Get the diff** + + ```bash + git diff main...HEAD + ``` + + If there are also uncommitted changes, include them: + ```bash + git diff main + ``` + +2. **Scan each changed file** for the slop categories below +3. **Fix** each instance — remove or rewrite to match the surrounding code style +4. **Verify** the build still passes: + ```bash + yarn build && yarn test + ``` +5. **Report** a 1-3 sentence summary of what you changed + +## Slop Categories + +### Unnecessary comments + +AI loves adding comments that restate the code. Remove comments that a human wouldn't write. Keep comments that explain *why* — domain reasoning, constraints, trade-offs, or non-obvious intent. + +```typescript +// ❌ Slop — restates the code +const [count, setCount] = useState(0); // Initialize count state to 0 + +// ❌ Slop — obvious from context +// Get the comment from store +const comment = commentsStore.getState().comments[commentCid]; + +// ✅ Keep — explains non-obvious intent +// plebbit-js returns undefined while loading, null if not found +const isLoading = comment === undefined; +``` + +### Excessive defensive checks + +AI adds try/catch blocks and null guards everywhere, even on trusted codepaths. Remove guards that the surrounding code doesn't need. + +### `as any` casts + +AI casts to `any` to bypass type errors instead of fixing the actual types. Remove the cast and fix the underlying type issue. + +### Inconsistent style + +Any pattern that doesn't match the rest of the file: different naming conventions, different import ordering, unnecessary abstractions, or overly verbose code where the file is concise. + +### Over-engineering + +AI tends to add unnecessary abstractions, utility functions, or wrapper layers that obscure simple logic. If a one-liner was wrapped in a helper, unwrap it. + +## Judgment Call: When to Keep Comments + +Comments are necessary when code expresses: +- Non-obvious intent or domain-specific reasoning +- Constraints that aren't apparent from the implementation +- Trade-offs or "why not X" decisions +- Workarounds with context on when they can be removed + +When in doubt, check if similar code nearby has comments. Match the file's existing comment density. diff --git a/.codex/skills/find-skills/SKILL.md b/.codex/skills/find-skills/SKILL.md new file mode 100644 index 0000000..0fce7a2 --- /dev/null +++ b/.codex/skills/find-skills/SKILL.md @@ -0,0 +1,35 @@ +--- +name: find-skills +description: Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill for X". Use this to search the open skill ecosystem. +--- + +# Find Skills + +Search the open skill ecosystem for useful agent skills. + +## How to Search + +```bash +npx skills search "QUERY" +``` + +## How to Install + +```bash +npx skills add REPO_URL --skill SKILL_NAME +``` + +## Examples + +```bash +npx skills search "code review" +npx skills search "testing" +npx skills search "documentation" +npx skills add https://github.com/intellectronica/agent-skills --skill context7 +``` + +## Tips + +- Search with broad terms first, then narrow down +- Skills are installed to `.cursor/skills/SKILL_NAME/` +- After installing, the skill is available in the current project diff --git a/.codex/skills/fix-merge-conflicts/SKILL.md b/.codex/skills/fix-merge-conflicts/SKILL.md new file mode 100644 index 0000000..95d4b05 --- /dev/null +++ b/.codex/skills/fix-merge-conflicts/SKILL.md @@ -0,0 +1,83 @@ +--- +name: fix-merge-conflicts +description: Resolve all merge conflicts on the current branch non-interactively, validate the build, and commit. Use when the user says "fix merge conflicts", "resolve conflicts", or when git status shows conflicting files. +disable-model-invocation: true +--- + +# Fix Merge Conflicts + +Resolve all merge conflicts on the current branch non-interactively and leave the repo buildable. + +## Constraints + +- Do not ask the user for input. Make best-effort decisions and explain them in a summary. +- Prefer minimal changes that preserve both sides' intent. +- Do not push or tag — only commit locally. + +## Workflow + +### 1. Detect conflicts + +```bash +git status --porcelain +``` + +Collect files with `U` statuses or containing `<<<<<<<` / `=======` / `>>>>>>>` markers. + +### 2. Resolve conflicts per file + +Open each conflicting file and remove conflict markers. Merge both sides logically when feasible. + +**When sides are mutually exclusive**, pick the variant that: +1. Compiles and passes type checks +2. Preserves existing public APIs and behavior + +**File-type strategies:** + +| File type | Strategy | +|-----------|----------| +| `package.json` | Merge keys conservatively, then `yarn install` to regenerate `yarn.lock` | +| `yarn.lock` | Never manually edit — regenerate with `yarn install` | +| Config files (`.json`, `.yaml`) | Preserve union of safe settings; don't delete required fields | +| Markdown / text | Include both unique sections, deduplicate headings | +| Binary files | Prefer current branch (ours) | +| Generated / build artifacts (`dist/`) | Prefer current branch (ours), or regenerate with `yarn build` | + +### 3. Validate + +Run build check. Fix any failures before proceeding. + +```bash +yarn build +``` + +If `package.json` was modified, run `yarn install` first. + +### 4. Verify no remaining markers + +```bash +rg '<<<<<<<|=======|>>>>>>>' --type ts --type json +``` + +If any markers remain, go back and resolve them. + +### 5. Finalize + +```bash +git add -A +git commit -m "chore: resolve merge conflicts" +``` + +## Operational Guidance + +- If a resolution is ambiguous and blocks the build, prefer the variant that compiles. +- For large refactors causing conflicts, keep consistent imports, types, and module boundaries. +- Keep edits minimal — don't reformat unrelated code. +- Format resolved files with `yarn prettier` after changes. + +## Deliverables + +- Clean working tree with all conflicts resolved +- Passing `yarn build` +- One local commit: `chore: resolve merge conflicts` +- Brief summary of files touched and notable resolution choices diff --git a/.codex/skills/implement-plan/SKILL.md b/.codex/skills/implement-plan/SKILL.md new file mode 100644 index 0000000..e7581f3 --- /dev/null +++ b/.codex/skills/implement-plan/SKILL.md @@ -0,0 +1,93 @@ +--- +name: implement-plan +description: Orchestrates implementation of a multi-task plan by spawning plan-implementer subagents in parallel. Use when the user provides a plan file or plan text and asks to implement it, execute it, or says "implement plan", "run plan", "execute plan". +--- + +# Implement Plan + +You are the **orchestrator**. Your job is to execute the attached plan by delegating tasks to `plan-implementer` subagents. Preserve your context window for coordination — never implement tasks yourself. + +## Workflow + +### 1. Analyze the Plan + +Read the plan the user attached. Identify: + +- All discrete tasks/steps +- Dependencies between tasks (which must run sequentially vs. can run in parallel) +- Any ambiguous items that need clarification before starting + +If anything is unclear, ask the user before proceeding. + +### 2. Group Tasks for Parallelization + +Partition tasks into **parallel batches** based on dependencies: + +``` +Batch 1 (parallel): [tasks with no dependencies] +Batch 2 (parallel): [tasks that depend on batch 1] +Batch 3 (parallel): [tasks that depend on batch 2] +... +``` + +**Rules:** + +- Max 4 concurrent subagents (tool limitation) +- Tasks touching the same file(s) go in the same subagent or sequential batches — never parallel +- Small related tasks can be grouped into one subagent to reduce overhead +- Large independent tasks get their own subagent + +### 3. Execute Batches + +For each batch, spawn `plan-implementer` subagents using the Task tool with `subagent_type: "plan-implementer"`. + +Each subagent prompt must include: + +- **Exact tasks** to implement (copy from the plan, don't paraphrase loosely) +- **File paths** and context needed to work independently +- **Constraints** or edge cases from the plan + +Use `model: "fast"` for straightforward tasks. Omit model for complex ones. + +Wait for all subagents in a batch to complete before starting the next batch. + +### 4. Handle Failures + +When a subagent reports PARTIAL or FAILED: + +- Read its report to understand what failed and why +- Decide: retry with more context, reassign to a different batch, or implement the fix yourself if trivial +- Don't retry blindly — adjust the prompt or approach + +### 5. Verify + +After all batches complete: + +1. Run `yarn build` to confirm everything compiles +2. Run `yarn test` to confirm tests pass + +### 6. Report + +Summarize to the user: + +``` +## Plan Execution Summary + +### Completed +- Task 1 — files modified +- Task 2 — files modified + +### Failed (if any) +- Task N — reason, what was tried + +### Verification +- Build: PASS/FAIL +- Tests: PASS/FAIL +``` + +## Key Principles + +- **You orchestrate, subagents implement.** Don't code changes yourself unless it's a trivial one-liner fix for a subagent failure. +- **Context is precious.** Every build log and file read you do in the main thread is context you can't get back. Delegate liberally. +- **Parallelize aggressively.** The faster batches finish, the faster the plan is done. Only serialize when dependencies demand it. +- **Verify at the end, not in between.** Subagents run their own build checks. You do a final holistic verification. diff --git a/.codex/skills/issue-format/SKILL.md b/.codex/skills/issue-format/SKILL.md new file mode 100644 index 0000000..73d1082 --- /dev/null +++ b/.codex/skills/issue-format/SKILL.md @@ -0,0 +1,30 @@ +--- +name: issue-format +description: Formats GitHub issue suggestions with a short title and informal problem description. Use when proposing GitHub issues for bugs, features, or improvements discovered during work. +--- + +# Issue Format + +## Template + +``` +> **GitHub issue:** +> - **Title:** `short issue title here` +> - **Description:** 2-3 sentences describing the problem as if unresolved. +``` + +## Rules + +1. Title is as short as possible, wrapped in backticks +2. Description describes the **problem**, not the solution +3. Write as if the issue hasn't been fixed yet +4. Use backticks for code references in the description +5. Keep it informal and concise — 2-3 sentences max + +## Example + +``` +> **GitHub issue:** +> - **Title:** `useReplies returns stale data after reset` +> - **Description:** When calling `reset()` on `useReplies`, subsequent `loadMore` calls return previously cached replies instead of fetching fresh data. This causes the UI to show outdated replies after a user manually refreshes a thread. +``` diff --git a/.codex/skills/make-closed-issue/SKILL.md b/.codex/skills/make-closed-issue/SKILL.md new file mode 100644 index 0000000..4fc1079 --- /dev/null +++ b/.codex/skills/make-closed-issue/SKILL.md @@ -0,0 +1,173 @@ +--- +name: make-closed-issue +description: Create a GitHub issue from recent changes, commit only relevant diffs on a short-lived task branch, push that branch, and open a PR into master that will close the issue on merge. Use when the user says "make closed issue", "close issue", or wants to create a tracked, already-resolved GitHub issue for completed work. +--- + +# Make Closed Issue + +Creates a GitHub issue, commits relevant changes on a review branch, pushes the branch, and opens a PR into `master` that closes the issue when merged. + +## Inputs + +- What changed and why (from prior conversation context) +- Uncommitted or staged git changes in the working tree + +## Workflow + +### 1. Determine label(s) + +Ask the user using AskQuestion (multi-select): + +| Option | When | +|--------|------| +| `bug` | Bug fix | +| `enhancement` | New feature | +| `bug` + `enhancement` | New feature that also fixes a bug | +| `documentation` | README, AGENTS.md, docs-only changes | + +### 2. Ensure branch workflow is reviewable + +- If already on a short-lived task branch such as `feature/*`, `fix/*`, `docs/*`, or `chore/*`, stay on it. +- If on `master`, create a task branch before staging or committing. +- Do **not** commit the work directly on `master` when PR review bots or human review are expected. + +Suggested naming: + +- `feature/short-slug` +- `fix/short-slug` +- `docs/short-slug` +- `chore/short-slug` + +Example: + +```bash +git switch -c fix/replies-cache-reset +``` + +### 3. Review diffs for relevance + +```bash +git status +git diff +git diff --cached +``` + +Identify which files relate to the work done in this conversation. Only relevant changes get committed. Unrelated files must be excluded from staging. + +**Important**: `git add -p` and `git add -i` are not available (interactive mode unsupported). If a file has mixed relevant/irrelevant changes, include the entire file and note the caveat to the user. + +### 4. Generate issue title and description + +From the conversation context: + +- **Title**: Short, present-tense, describes the **problem** (not the solution). Use backticks for hooks, stores, commands, or literal strings when helpful. +- **Description**: 2-3 sentences about the problem. Use backticks for code references or literal names. Write as if the issue hasn't been fixed yet. + +### 5. Create the issue + +```bash +gh issue create \ + --repo bitsocialnet/bitsocial-react-hooks \ + --title "ISSUE_TITLE" \ + --body "ISSUE_DESCRIPTION" \ + --label "LABEL1,LABEL2" \ + --assignee plebe1us +``` + +Capture the issue number from the output. + +### 6. Commit relevant changes + +Stage only the relevant files: + +```bash +git add file1.ts file2.ts ... +``` + +Commit using Conventional Commits with scope: + +```bash +git commit -m "$(cat <<'EOF' +type(scope): concise title + +Optional 1-sentence description only if the title isn't self-explanatory. +EOF +)" +``` + +- **Types**: `fix`, `feat`, `perf`, `refactor`, `docs`, `chore` +- **Scope**: area of the codebase (for example `feeds`, `replies`, `accounts`, `agent-workflow`) +- Prefer title-only commits when the title already says enough + +### 7. Push branch and open PR + +Push the current task branch to origin and open a PR into `master`. + +Use `Closes #ISSUE_NUMBER` in the PR body so the issue closes automatically when the PR is merged. + +```bash +COMMIT_HASH=$(git rev-parse HEAD) +BRANCH_NAME=$(git branch --show-current) +git push -u origin "$BRANCH_NAME" + +gh pr create \ + --repo bitsocialnet/bitsocial-react-hooks \ + --base master \ + --head "$BRANCH_NAME" \ + --title "PR_TITLE" \ + --body "$(cat <..HEAD +``` + +If there are no commits since the tag, stop — nothing to update. + +### 3. Analyze the commits + +Categorize by Conventional Commits prefix: + +| Prefix | Category | +|--------|----------| +| `feat:` | New features | +| `fix:` | Bug fixes | +| `perf:` | Performance improvements | +| `refactor:` | Refactors / internal changes | +| `chore:`, `docs:`, `ci:` | Maintenance (mention only if significant) | +| No prefix | Read the title to infer category | + +### 4. Write the summary + +Compose a concise release description. Rules: + +- **Start with** "This version..." or "This release..." +- **Be concise** — a few sentences, not a full changelog +- **Highlight the most impactful changes** — lead with the biggest features or fixes +- **Group similar changes** — e.g. "several bug fixes" instead of listing each one +- **Use plain language** — this is user-facing, not developer-facing + +### 5. Report + +Display the release description to the user for review. diff --git a/.codex/skills/review-and-merge-pr/SKILL.md b/.codex/skills/review-and-merge-pr/SKILL.md new file mode 100644 index 0000000..7398a2c --- /dev/null +++ b/.codex/skills/review-and-merge-pr/SKILL.md @@ -0,0 +1,175 @@ +--- +name: review-and-merge-pr +description: Review an open GitHub pull request, inspect feedback from CI, review bots, and human reviewers, decide which findings are valid, implement fixes on the PR branch, merge the PR into master when it is ready, and finalize the linked GitHub issue and project status after merge. Use when the user says "check the PR", "address review comments", "review PR feedback", or "merge this PR". +--- + +# Review And Merge Pr + +## Overview + +Use this skill after a feature branch already has an open PR into `master`. +Stay on the PR branch, treat review bots as input rather than authority, and only merge once the branch is verified and the remaining comments are either fixed or explicitly declined with a reason. + +## Workflow + +### 1. Identify the target PR + +Prefer the PR for the current branch when the branch is not `master`. +If the current branch is `master`, inspect open PRs and choose the one that matches the user request. +If there is no open PR yet, stop and use `make-closed-issue` first. + +Useful commands: + +```bash +gh pr status +gh pr list --repo bitsocialnet/bitsocial-react-hooks --state open +gh pr view --repo bitsocialnet/bitsocial-react-hooks --json number,title,url,headRefName,baseRefName,isDraft,reviewDecision,mergeStateStatus +``` + +### 2. Gather all review signals before changing code + +Read the PR state, checks, issue comments, review summaries, and inline review comments before deciding what to change. +Do not merge based only on the top-level review verdict. + +Useful commands: + +```bash +gh pr view --repo bitsocialnet/bitsocial-react-hooks --json number,title,url,headRefName,baseRefName,isDraft,reviewDecision,mergeStateStatus +gh pr checks +gh api "repos/bitsocialnet/bitsocial-react-hooks/issues//comments?per_page=100" +gh api "repos/bitsocialnet/bitsocial-react-hooks/pulls//reviews?per_page=100" +gh api "repos/bitsocialnet/bitsocial-react-hooks/pulls//comments?per_page=100" +``` + +Focus on comments from: + +- CodeRabbit or other review bots +- human reviewers +- failing CI checks + +### 3. Triage findings instead of blindly applying them + +Sort feedback into these buckets: + +- `must-fix`: correctness bugs, broken behavior, crashes, security issues, test failures, reproducible regressions +- `should-fix`: clear maintainability or edge-case issues with concrete evidence +- `decline`: false positives, stale comments, duplicate findings, speculative style-only suggestions, or feedback already addressed in newer commits + +Rules: + +- Never merge with unresolved `must-fix` findings. +- Do not accept a bot finding without reading the relevant code and diff. +- If a finding is ambiguous but high-risk, ask the user before merging. +- If a comment is wrong or stale, explain why in the PR rather than silently ignoring it. + +### 4. Work on the PR branch and keep the PR updated + +Switch to the PR branch if needed, apply the valid fixes, and push new commits to the same branch. +Do not open a replacement PR unless the user explicitly asks for that. + +Useful commands: + +```bash +git switch +git fetch origin +git status --short --branch +git add +git commit -m "fix(scope): address review feedback" +git push +``` + +After code changes, follow repo verification rules from `AGENTS.md`: + +- run `yarn build` +- run `yarn test` after adding or changing tests +- if hooks or stores changed, run the coverage command and `node scripts/verify-hooks-stores-coverage.mjs` +- run `yarn prettier` before the final review-driven commit +- if local verification dirties tracked `dist/` output, restore it before committing + +### 5. Report back on the PR before merging + +Summarize what was fixed and what was declined. +Use `gh pr comment` for a concise PR update when the branch changed because of review feedback. + +Example: + +```bash +gh pr comment --repo bitsocialnet/bitsocial-react-hooks --body "Addressed the valid review findings in the latest commit. Remaining comments are stale or not applicable for the reasons checked locally." +``` + +### 6. Merge only when the PR is actually ready + +Merge only if all of these are true: + +- the PR is not draft +- required checks are passing +- the branch is mergeable into `master` +- no unresolved `must-fix` reviewer findings remain +- the latest code was verified locally after the last review-driven change + +Preferred merge command: + +```bash +gh pr merge --repo bitsocialnet/bitsocial-react-hooks --squash --delete-branch +``` + +### 7. Finalize the linked issue and project item + +After merge, inspect the PR's linked closing issue. +If the merge did not close the issue automatically, close it manually. +Then ensure the linked issue is on the `bitsocial-react-hooks` project and its status is `Done`. + +Useful commands: + +```bash +ISSUE_NUMBER=$(gh pr view --repo bitsocialnet/bitsocial-react-hooks --json closingIssuesReferences --jq '.closingIssuesReferences[0].number // empty') + +if [ -n "$ISSUE_NUMBER" ]; then + ISSUE_STATE=$(gh issue view "$ISSUE_NUMBER" --repo bitsocialnet/bitsocial-react-hooks --json state --jq '.state') + if [ "$ISSUE_STATE" != "CLOSED" ]; then + gh issue close "$ISSUE_NUMBER" --repo bitsocialnet/bitsocial-react-hooks + fi + + ITEM_ID=$(gh project item-list 6 --owner bitsocialnet --limit 1000 --format json --jq ".items[] | select(.content.number == $ISSUE_NUMBER) | .id" | head -n1) + if [ -z "$ITEM_ID" ]; then + ITEM_JSON=$(gh project item-add 6 --owner bitsocialnet --url "https://github.com/bitsocialnet/bitsocial-react-hooks/issues/$ISSUE_NUMBER" --format json) + ITEM_ID=$(echo "$ITEM_JSON" | jq -r '.id') + fi + + FIELD_JSON=$(gh project field-list 6 --owner bitsocialnet --format json) + STATUS_FIELD_ID=$(echo "$FIELD_JSON" | jq -r '.fields[] | select(.name=="Status") | .id') + DONE_OPTION_ID=$(echo "$FIELD_JSON" | jq -r '.fields[] | select(.name=="Status") | .options[] | select(.name=="Done") | .id') + + gh project item-edit --id "$ITEM_ID" --project-id PVT_kwDODohK7M4BQoZJ --field-id "$STATUS_FIELD_ID" --single-select-option-id "$DONE_OPTION_ID" +fi +``` + +### 8. Clean up local state after merge + +After the PR is merged: + +```bash +git switch master +git pull --ff-only +git branch -D 2>/dev/null || true +git branch -D "pr/" 2>/dev/null || true +``` + +If the PR branch lived in a dedicated worktree, remove that worktree after leaving it: + +```bash +git worktree list +git worktree remove /path/to/worktree +``` + +### 9. Report the outcome + +Tell the user: + +- which findings were fixed +- which findings were declined and why +- which verification commands ran +- whether the PR was merged +- whether the linked issue was confirmed closed +- whether the linked project item was confirmed `Done` +- whether the feature branch, local `pr/` alias, and any worktree were cleaned up diff --git a/.codex/skills/you-might-not-need-an-effect/SKILL.md b/.codex/skills/you-might-not-need-an-effect/SKILL.md new file mode 100644 index 0000000..0091f9e --- /dev/null +++ b/.codex/skills/you-might-not-need-an-effect/SKILL.md @@ -0,0 +1,121 @@ +--- +name: you-might-not-need-an-effect +description: Analyze code for useEffect anti-patterns and refactor to simpler alternatives. Use when the user says "you might not need an effect", "check effects", "useEffect audit", or asks to review useEffect usage. +disable-model-invocation: true +--- + +# You Might Not Need an Effect + +Analyze code for `useEffect` anti-patterns and refactor to simpler, more correct alternatives. + +Based on https://react.dev/learn/you-might-not-need-an-effect + +## Arguments + +- **scope**: what to analyze (default: uncommitted changes). Examples: `diff to main`, `src/hooks/`, `whole codebase` +- **fix**: whether to apply fixes (default: `true`). Set to `false` to only propose changes. + +## Workflow + +1. **Determine scope** — get the relevant code: + - Default: `git diff` for uncommitted changes + - If a directory/file is specified, read those files + - If "whole codebase": search all `.ts` files for `useEffect` + +2. **Scan for anti-patterns** — check each `useEffect` against the patterns below + +3. **Fix or propose** — depending on the `fix` argument: + - `fix=true`: apply the refactors, then verify with `yarn build` + - `fix=false`: list each anti-pattern found with a before/after code suggestion + +4. **Report** — summarize what was found and changed + +## Anti-Patterns to Catch + +### 1. Deriving state during render (no effect needed) + +If you're computing something from existing props or state, calculate it during render. + +```typescript +// ❌ Anti-pattern +const [fullName, setFullName] = useState(''); +useEffect(() => { + setFullName(firstName + ' ' + lastName); +}, [firstName, lastName]); + +// ✅ Fix — derive during render +const fullName = firstName + ' ' + lastName; +``` + +### 2. Caching expensive calculations (useMemo, not useEffect) + +```typescript +// ❌ Anti-pattern +const [filtered, setFiltered] = useState([]); +useEffect(() => { + setFiltered(items.filter(item => item.active)); +}, [items]); + +// ✅ Fix — calculate during render (useMemo only if profiling shows it's needed) +const filtered = items.filter(item => item.active); +``` + +### 3. Resetting state when props change (use key, not useEffect) + +```typescript +// ❌ Anti-pattern +useEffect(() => { + setComment(''); +}, [postCid]); + +// ✅ Fix — use key on the component to reset state + +``` + +### 4. Syncing with external stores (use Zustand selectors) + +```typescript +// ❌ Anti-pattern +const [data, setData] = useState(null); +useEffect(() => { + const unsub = someStore.subscribe((s) => setData(s.data)); + return unsub; +}, []); + +// ✅ Fix — use the Zustand store directly +const data = useSomeStore((s) => s.data); +``` + +### 5. Initializing global singletons (use module scope or lazy init) + +```typescript +// ❌ Anti-pattern +useEffect(() => { + initializeSomething(); +}, []); + +// ✅ Fix — module-level init (runs once on import) +if (typeof window !== 'undefined') { + initializeSomething(); +} +``` + +## Project-Specific Context + +This is a hooks library, not an app. Effects in this codebase are more likely to be legitimate (subscribing to plebbit-js events, managing store listeners) than in a typical React app. Be extra careful before removing effects that manage subscriptions or event listeners with cleanup functions. + +| Pattern | Likely legitimate | +|---------|------------------| +| Store subscription with cleanup | Yes — keep | +| plebbit-js event listener with cleanup | Yes — keep | +| Deriving state from other state | No — compute during render | +| Setting state from props | No — derive or use key | +| One-time initialization | Maybe — consider module scope | + +## When useEffect IS Appropriate + +Not every effect is wrong. Keep `useEffect` for: +- Subscribing to plebbit-js events with proper cleanup +- Managing Zustand store subscriptions with cleanup +- Synchronizing with browser APIs (resize, intersection observer, etc.) +- Running code on mount that genuinely has no alternative diff --git a/.cursor/agents/code-quality.md b/.cursor/agents/code-quality.md new file mode 100644 index 0000000..92ac4dc --- /dev/null +++ b/.cursor/agents/code-quality.md @@ -0,0 +1,65 @@ +--- +name: code-quality +model: composer-1.5 +description: Code quality specialist that runs build and tests, then fixes any errors it finds. Use proactively after code changes to verify nothing is broken. +--- + +You are a code quality verifier for the bitsocial-react-hooks project. You run the project's quality checks, fix any issues found, and report results back to the parent agent. + +## Workflow + +### Step 1: Run Quality Checks + +Execute these commands and capture all output: + +```bash +yarn build 2>&1 +yarn test 2>&1 +``` + +### Step 2: Analyze Failures + +If any check fails, read the error output carefully: + +- Identify the file(s) and line(s) causing the failure +- Determine the root cause (not just the symptom) +- Prioritize: build errors > test failures + +### Step 3: Fix Issues + +For each failure: + +1. Read the affected file to understand context +2. Check git history for the affected lines (`git log --oneline -5 -- `) to avoid reverting intentional code +3. Apply the minimal fix that resolves the error +4. Follow project patterns from AGENTS.md (Zustand stores for state, thin hook wrappers, derive state during render) + +### Step 4: Re-verify + +After fixing, re-run the failed check(s) to confirm resolution. If new errors appear, fix those too. Loop until all checks pass or you've exhausted reasonable attempts (max 3 loops). + +### Step 5: Report Back + +Return a structured report: + +``` +## Quality Check Results + +### Build: PASS/FAIL +### Tests: PASS/FAIL + +### Fixes Applied +- `path/to/file.ts` — description of fix + +### Remaining Issues (if any) +- description of issue that couldn't be auto-fixed + +### Status: SUCCESS / PARTIAL / FAILED +``` + +## Constraints + +- Only fix issues surfaced by the quality checks — don't refactor unrelated code +- Pin exact package versions if dependency changes are needed (no carets) +- Use `yarn`, not `npm` +- If a fix is unclear or risky, report it as a remaining issue instead of guessing diff --git a/.cursor/agents/plan-implementer.md b/.cursor/agents/plan-implementer.md new file mode 100644 index 0000000..6f48206 --- /dev/null +++ b/.cursor/agents/plan-implementer.md @@ -0,0 +1,71 @@ +--- +name: plan-implementer +model: composer-1.5 +description: Implements assigned tasks from a plan. Receives specific tasks from the parent agent, implements them sequentially, verifies with a build check, and reports back. The parent agent handles parallelization by spawning multiple plan-implementer subagents with different task subsets. +--- + +You are a plan implementer for the bitsocial-react-hooks project. You receive specific tasks from the parent agent and implement them. The parent agent handles parallelization by spawning multiple instances of you with different task subsets. + +## Required Input + +You MUST receive from the parent agent: + +1. **One or more specific tasks** with enough detail to implement independently +2. **Context**: file paths, requirements, expected behavior + +If the task description is too vague to act on, report back asking for clarification. + +## Workflow + +### Step 1: Understand the Tasks + +Read the task description(s) carefully. For each task: + +- Identify the file(s) to modify or create +- Understand the expected behavior +- Note any constraints + +### Step 2: Implement + +For each task: + +1. Read the affected file(s) to understand current state +2. Check git history for affected lines (`git log --oneline -5 -- `) to avoid reverting intentional code +3. Apply changes following project patterns from AGENTS.md +4. Verify the change makes sense in context + +### Step 3: Verify + +After implementing all assigned tasks: + +```bash +yarn build 2>&1 +``` + +If build errors relate to your changes, fix them and re-run. Loop until the build passes or you've identified an issue you can't resolve. + +### Step 4: Report Back + +``` +## Implementation Report + +### Tasks Completed +- [x] Task description — files modified + +### Tasks Failed (if any) +- [ ] Task description — reason for failure + +### Verification +- Build: PASS/FAIL + +### Status: SUCCESS / PARTIAL / FAILED +``` + +## Constraints + +- Implement only the tasks assigned to you — don't expand scope +- Follow project patterns from AGENTS.md +- If a task conflicts with existing code, report the conflict instead of guessing +- Pin exact package versions if dependency changes are needed (no carets) +- Use `yarn`, not `npm` +- Do not rename `plebbit`/`subplebbit` terms — rebranding is not yet in scope diff --git a/.cursor/agents/react-patterns-enforcer.md b/.cursor/agents/react-patterns-enforcer.md new file mode 100644 index 0000000..575b7cd --- /dev/null +++ b/.cursor/agents/react-patterns-enforcer.md @@ -0,0 +1,77 @@ +--- +name: react-patterns-enforcer +model: composer-1.5 +description: Reviews React hooks and store code for anti-pattern violations specific to bitsocial-react-hooks (effect misuse, store patterns, public API consistency) and fixes them. Use after writing or modifying hooks, stores, or the public API. +--- + +You are a React patterns reviewer for the bitsocial-react-hooks project. You review recent code changes for anti-pattern violations defined in AGENTS.md and fix them. + +## Workflow + +### Step 1: Identify Changed Files + +Check what was recently modified (the parent agent may specify files, or use): + +```bash +git diff --name-only HEAD~1 -- '*.ts' +``` + +Focus on files in `src/hooks/`, `src/stores/`, `src/lib/`. + +### Step 2: Review for Violations + +Read each changed file and check for these project-critical anti-patterns: + +| Violation | Fix | +|-----------|-----| +| `useEffect` for data fetching inside a hook | Use store subscriptions and event listeners instead | +| `useEffect` syncing derived state | Calculate during render | +| Hook does too many things | Split into focused composable hooks | +| Store action mixed with store state selection | Separate actions from selectors | +| Missing public API export | Add to `src/index.ts` | +| Type defined inline instead of in `src/types.ts` | Move cross-module types to `src/types.ts` | +| Unnecessary `any` casts | Fix the underlying type | +| Effects without cleanup | Add cleanup function | + +### Step 3: Fix Violations + +For each violation: + +1. Read enough surrounding context to understand the module's purpose +2. Check git history (`git log --oneline -5 -- `) to avoid reverting intentional code +3. Apply the minimal fix from the table above +4. Ensure the fix doesn't break existing behavior + +### Step 4: Verify + +```bash +yarn build 2>&1 +``` + +If the build breaks due to your changes, fix and re-run. + +### Step 5: Report Back + +``` +## React Patterns Review + +### Files Reviewed +- `path/to/file.ts` + +### Violations Found & Fixed +- `file.ts:42` — useEffect syncing derived state → computed during render + +### Violations Found (unfixed) +- `file.ts:100` — description and why it wasn't auto-fixed + +### Build: PASS/FAIL +### Status: SUCCESS / PARTIAL / FAILED +``` + +## Constraints + +- Only fix pattern violations — don't refactor unrelated code +- Follow patterns defined in AGENTS.md +- If a fix would require significant restructuring, report it instead of applying it +- Use `yarn`, not `npm` +- Do not rename `plebbit`/`subplebbit` terms diff --git a/.cursor/hooks.json b/.cursor/hooks.json new file mode 100644 index 0000000..98f9236 --- /dev/null +++ b/.cursor/hooks.json @@ -0,0 +1,25 @@ +{ + "version": 1, + "hooks": { + "afterFileEdit": [ + { + "command": ".cursor/hooks/format.sh", + "timeout": 10 + }, + { + "command": ".cursor/hooks/yarn-install.sh", + "timeout": 120 + } + ], + "stop": [ + { + "command": ".cursor/hooks/sync-git-branches.sh", + "timeout": 60 + }, + { + "command": ".cursor/hooks/verify.sh", + "timeout": 60 + } + ] + } +} diff --git a/.cursor/hooks/format.sh b/.cursor/hooks/format.sh new file mode 100755 index 0000000..45842f7 --- /dev/null +++ b/.cursor/hooks/format.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# afterFileEdit hook: Auto-format files after AI edits them +# Receives JSON via stdin: {"file_path": "...", "edits": [...]} + +# Read stdin (required for hooks) +input=$(cat) + +# Extract file_path using grep/sed (jq-free for portability) +file_path=$(echo "$input" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/') + +# Exit if no file path found +if [ -z "$file_path" ]; then + exit 0 +fi + +# Only format JS/TS files +case "$file_path" in + *.js|*.ts|*.tsx|*.mjs|*.cjs) + npx prettier --config config/prettier.config.js --write "$file_path" 2>/dev/null || true + ;; +esac + +exit 0 diff --git a/.cursor/hooks/sync-git-branches.sh b/.cursor/hooks/sync-git-branches.sh new file mode 100755 index 0000000..f365e03 --- /dev/null +++ b/.cursor/hooks/sync-git-branches.sh @@ -0,0 +1,109 @@ +#!/bin/bash +# stop hook: prune stale remote refs and remove integrated temporary local branches +# This is informational - always exits 0 + +# Consume stdin (required for hooks) +cat > /dev/null + +cd "$(dirname "$0")/../.." || exit 0 + +if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + exit 0 +fi + +default_branch="$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | sed 's#^origin/##')" +if [ -z "$default_branch" ]; then + default_branch="master" +fi + +current_branch="$(git branch --show-current 2>/dev/null || true)" + +branch_looks_temporary() { + case "$1" in + pr/*|feature/*|fix/*|docs/*|chore/*) return 0 ;; + *) return 1 ;; + esac +} + +branch_is_integrated() { + local branch="$1" + local cherry_output + + cherry_output="$(git cherry "$default_branch" "$branch" 2>/dev/null || true)" + if echo "$cherry_output" | grep -q '^+'; then + return 1 + fi + + return 0 +} + +branch_has_live_upstream() { + local upstream="$1" + [ -n "$upstream" ] && git show-ref --verify --quiet "refs/remotes/$upstream" +} + +merged_pr_number_for_branch() { + local branch="$1" + local pr_number="" + + if ! command -v gh >/dev/null 2>&1; then + return 0 + fi + + case "$branch" in + pr/*) + pr_number="${branch#pr/}" + gh pr view "$pr_number" --repo bitsocialnet/bitsocial-react-hooks --json mergedAt --jq 'select(.mergedAt != null) | .mergedAt' >/dev/null 2>&1 || return 0 + echo "$pr_number" + return 0 + ;; + esac + + gh pr list --repo bitsocialnet/bitsocial-react-hooks --state merged --head "$branch" --json number --jq '.[0].number // empty' 2>/dev/null || true +} + +echo "Syncing git refs and temporary branches..." +echo "" + +echo "=== git config --local fetch.prune true ===" +git config --local fetch.prune true 2>&1 || true +echo "" + +echo "=== git config --local remote.origin.prune true ===" +git config --local remote.origin.prune true 2>&1 || true +echo "" + +echo "=== git fetch --prune origin ===" +git fetch --prune origin 2>&1 || true +echo "" + +while IFS='|' read -r branch upstream; do + local_pr_number="" + + [ -z "$branch" ] && continue + [ "$branch" = "$current_branch" ] && continue + [ "$branch" = "$default_branch" ] && continue + + branch_looks_temporary "$branch" || continue + local_pr_number="$(merged_pr_number_for_branch "$branch")" + + if branch_has_live_upstream "$upstream"; then + continue + fi + + if ! branch_is_integrated "$branch" && [ -z "$local_pr_number" ]; then + continue + fi + + if [ -n "$local_pr_number" ]; then + echo "=== merged PR #$local_pr_number allows deleting $branch ===" + echo "" + fi + + echo "=== git branch -D $branch ===" + git branch -D "$branch" 2>&1 || true + echo "" +done < <(git for-each-ref --format='%(refname:short)|%(upstream:short)' refs/heads) + +echo "Git ref sync complete." +exit 0 diff --git a/.cursor/hooks/verify.sh b/.cursor/hooks/verify.sh new file mode 100755 index 0000000..556ffb9 --- /dev/null +++ b/.cursor/hooks/verify.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# stop hook: Run build and tests when agent finishes +# This is informational - always exits 0 + +# Consume stdin (required for hooks) +cat > /dev/null + +# Change to project directory +cd "$(dirname "$0")/../.." || exit 0 + +DIST_STATUS_BEFORE="" +if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && + git ls-files --error-unmatch dist >/dev/null 2>&1; then + # Only clean up dist when verification started from a clean baseline. + DIST_STATUS_BEFORE="$(git status --porcelain=v1 --ignored=matching --untracked-files=all -- dist 2>/dev/null || true)" +fi + +restore_dist_worktree() { + if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + return + fi + + if ! git ls-files --error-unmatch dist >/dev/null 2>&1; then + return + fi + + if [ -n "$DIST_STATUS_BEFORE" ]; then + return + fi + + local dist_status_after="" + dist_status_after="$(git status --porcelain=v1 --ignored=matching --untracked-files=all -- dist 2>/dev/null || true)" + if [ -z "$dist_status_after" ]; then + return + fi + + echo "=== git restore --worktree dist ===" + git restore --worktree -- dist 2>&1 || true + git clean -fdX -- dist 2>&1 || true + echo "" +} + +echo "Running build and tests..." +echo "" + +# Run build (catches compilation errors) +echo "=== yarn build ===" +yarn build 2>&1 || true +echo "" + +# Run tests +echo "=== yarn test ===" +yarn test 2>&1 || true +echo "" + +restore_dist_worktree + +echo "Verification complete." +exit 0 diff --git a/.cursor/hooks/yarn-install.sh b/.cursor/hooks/yarn-install.sh new file mode 100755 index 0000000..7e91722 --- /dev/null +++ b/.cursor/hooks/yarn-install.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# afterFileEdit hook: Run yarn install when package.json is changed +# Receives JSON via stdin: {"file_path": "...", "edits": [...]} + +# Read stdin (required for hooks) +input=$(cat) + +# Extract file_path using grep/sed (jq-free for portability) +file_path=$(echo "$input" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/') + +# Exit if no file path found +if [ -z "$file_path" ]; then + exit 0 +fi + +# Only run yarn install if package.json was changed +if [ "$file_path" = "package.json" ]; then + # Change to project directory + cd "$(dirname "$0")/../.." || exit 0 + + echo "package.json changed - running yarn install to update yarn.lock..." + yarn install +fi + +exit 0 diff --git a/.cursor/skills/commit-format/SKILL.md b/.cursor/skills/commit-format/SKILL.md new file mode 100644 index 0000000..94c765e --- /dev/null +++ b/.cursor/skills/commit-format/SKILL.md @@ -0,0 +1,55 @@ +--- +name: commit-format +description: Formats GitHub commit messages following Conventional Commits style with title and optional description. Use when proposing or implementing code changes, writing commit messages, or when the user asks for commit message suggestions. +--- + +# Commit Format + +## Template (copy this structure exactly) + +Title only — raw markdown: +``` +> **Commit title:** `type: short description here` +``` + +Title with description — raw markdown: +``` +> **Commit title:** `type: short description here` +> +> Description sentence one. Description sentence two with `codeRef()` references. +``` + +## Rules + +1. Use markdown blockquote (`>` prefix) — no exceptions +2. Title goes after `**Commit title:**` wrapped in exactly ONE backtick pair +3. NEVER put backticks inside the title — the whole title is one code span, no nesting +4. Description uses backticks for code references — title does NOT +5. Conventional Commits types: `fix:`, `feat:`, `perf:`, `refactor:`, `docs:`, `chore:` +6. Use `perf:` for performance optimizations (not `fix:`) +7. Description: 2-3 sentences about the solution, no bullet points, only if title isn't enough + +## Wrong vs Right + +❌ WRONG — missing backticks around title: +``` +> **Commit title:** refactor: simplify feeds store +``` + +❌ WRONG — backticks around individual words instead of whole title: +``` +> **Commit title:** refactor: simplify `feeds` store +``` + +✅ CORRECT — entire title in one backtick pair, no backticks inside: +``` +> **Commit title:** `refactor: simplify feeds store` +``` + +## Self-check + +Before outputting, verify: +- [ ] Lines start with `>` +- [ ] Title is wrapped in exactly one backtick pair: `` `like this` `` +- [ ] No backticks inside the title text +- [ ] Code references in description (not title) use backticks diff --git a/.cursor/skills/commit/SKILL.md b/.cursor/skills/commit/SKILL.md new file mode 100644 index 0000000..4c36118 --- /dev/null +++ b/.cursor/skills/commit/SKILL.md @@ -0,0 +1,57 @@ +--- +name: commit +description: Commit current work by reviewing diffs, splitting into logical commits, and writing standardized messages. Use when the user says "commit", "commit this", "commit current work", or asks to create a git commit. +disable-model-invocation: true +--- + +# Commit Current Work + +## Workflow + +1. **Review all uncommitted changes** + + ```bash + git status + git diff + git diff --cached + ``` + + Read every changed file's diff to understand the full scope of changes. + +2. **Group changes into logical commits** + + If diffs are unrelated, split into multiple commits. Each commit should cover one logical unit of work. + + Example — two unrelated changes in the working tree: + - Modified `src/hooks/comments.ts` (bug fix) + - Modified `src/stores/feeds/feeds-store.ts` (new feature) + + These should be two separate commits, not one. + +3. **Stage and commit each group** + + For each logical group: + ```bash + git add + git commit -m "title here" + ``` + +4. **Display the commit title to the user** wrapped in backticks (inline code). + +## Commit Message Rules + +- **Title format:** Conventional Commits with a **required scope**. The scope should be a short, human-readable name for the area of the codebase affected. + + | Pattern | Example | + |---------|---------| + | `type(scope): description` | `fix(feeds): handle empty subplebbit addresses array` | + +- **Never omit the scope.** `feat: add hook` is wrong. `feat(replies): add flat mode to useReplies` is correct. +- **Keep titles short.** If more context is needed, add a commit body — but don't repeat the title. +- **Use `perf:` for performance optimizations**, not `fix:`. + +## Constraints + +- Only commit when instructed. Do not commit subsequent changes unless explicitly told to. +- Never push — only commit locally. +- Never amend commits that have been pushed to a remote. diff --git a/.cursor/skills/context7/SKILL.md b/.cursor/skills/context7/SKILL.md new file mode 100644 index 0000000..9e1b91d --- /dev/null +++ b/.cursor/skills/context7/SKILL.md @@ -0,0 +1,67 @@ +--- +name: context7 +description: Retrieve up-to-date documentation for software libraries, frameworks, and components via the Context7 API. This skill should be used when looking up documentation for any programming library or framework, finding code examples for specific APIs or features, verifying correct usage of library functions, or obtaining current information about library APIs that may have changed since training. +--- + +# Context7 + +## Overview + +This skill enables retrieval of current documentation for software libraries and components by querying the Context7 API via curl. Use it instead of relying on potentially outdated training data. + +## Workflow + +### Step 1: Search for the Library + +To find the Context7 library ID, query the search endpoint: + +```bash +curl -s "https://context7.com/api/v2/libs/search?libraryName=LIBRARY_NAME&query=TOPIC" | jq '.results[0]' +``` + +**Parameters:** +- `libraryName` (required): The library name to search for (e.g., "react", "zustand", "vitest", "ethers") +- `query` (required): A description of the topic for relevance ranking + +**Response fields:** +- `id`: Library identifier for the context endpoint +- `title`: Human-readable library name +- `description`: Brief description of the library +- `totalSnippets`: Number of documentation snippets available + +### Step 2: Fetch Documentation + +To retrieve documentation, use the library ID from step 1: + +```bash +curl -s "https://context7.com/api/v2/context?libraryId=LIBRARY_ID&query=TOPIC&type=txt" +``` + +**Parameters:** +- `libraryId` (required): The library ID from search results +- `query` (required): The specific topic to retrieve documentation for +- `type` (optional): Response format - `json` (default) or `txt` (plain text, more readable) + +## Examples + +### Zustand store documentation + +```bash +curl -s "https://context7.com/api/v2/libs/search?libraryName=zustand&query=store" | jq '.results[0].id' +curl -s "https://context7.com/api/v2/context?libraryId=/pmndrs/zustand&query=create+store&type=txt" +``` + +### Vitest testing + +```bash +curl -s "https://context7.com/api/v2/libs/search?libraryName=vitest&query=testing" | jq '.results[0].id' +curl -s "https://context7.com/api/v2/context?libraryId=/vitest-dev/vitest&query=mock+functions&type=txt" +``` + +## Tips + +- Use `type=txt` for more readable output +- Use `jq` to filter and format JSON responses +- Be specific with the `query` parameter to improve relevance ranking +- URL-encode query parameters containing spaces (use `+` or `%20`) +- No API key is required for basic usage (rate-limited) diff --git a/.cursor/skills/deslop/SKILL.md b/.cursor/skills/deslop/SKILL.md new file mode 100644 index 0000000..40d55a5 --- /dev/null +++ b/.cursor/skills/deslop/SKILL.md @@ -0,0 +1,75 @@ +--- +name: deslop +description: Scan recent changes for AI-generated code slop and remove it. Use when the user says "deslop", "remove slop", "clean up AI code", or asks to remove AI-generated artifacts from the codebase. +disable-model-invocation: true +--- + +# Remove AI Code Slop + +Scan the diff against main and remove AI-generated slop introduced in this branch. + +## Workflow + +1. **Get the diff** + + ```bash + git diff main...HEAD + ``` + + If there are also uncommitted changes, include them: + ```bash + git diff main + ``` + +2. **Scan each changed file** for the slop categories below +3. **Fix** each instance — remove or rewrite to match the surrounding code style +4. **Verify** the build still passes: + ```bash + yarn build && yarn test + ``` +5. **Report** a 1-3 sentence summary of what you changed + +## Slop Categories + +### Unnecessary comments + +AI loves adding comments that restate the code. Remove comments that a human wouldn't write. Keep comments that explain *why* — domain reasoning, constraints, trade-offs, or non-obvious intent. + +```typescript +// ❌ Slop — restates the code +const [count, setCount] = useState(0); // Initialize count state to 0 + +// ❌ Slop — obvious from context +// Get the comment from store +const comment = commentsStore.getState().comments[commentCid]; + +// ✅ Keep — explains non-obvious intent +// plebbit-js returns undefined while loading, null if not found +const isLoading = comment === undefined; +``` + +### Excessive defensive checks + +AI adds try/catch blocks and null guards everywhere, even on trusted codepaths. Remove guards that the surrounding code doesn't need. + +### `as any` casts + +AI casts to `any` to bypass type errors instead of fixing the actual types. Remove the cast and fix the underlying type issue. + +### Inconsistent style + +Any pattern that doesn't match the rest of the file: different naming conventions, different import ordering, unnecessary abstractions, or overly verbose code where the file is concise. + +### Over-engineering + +AI tends to add unnecessary abstractions, utility functions, or wrapper layers that obscure simple logic. If a one-liner was wrapped in a helper, unwrap it. + +## Judgment Call: When to Keep Comments + +Comments are necessary when code expresses: +- Non-obvious intent or domain-specific reasoning +- Constraints that aren't apparent from the implementation +- Trade-offs or "why not X" decisions +- Workarounds with context on when they can be removed + +When in doubt, check if similar code nearby has comments. Match the file's existing comment density. diff --git a/.cursor/skills/find-skills/SKILL.md b/.cursor/skills/find-skills/SKILL.md new file mode 100644 index 0000000..0fce7a2 --- /dev/null +++ b/.cursor/skills/find-skills/SKILL.md @@ -0,0 +1,35 @@ +--- +name: find-skills +description: Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill for X". Use this to search the open skill ecosystem. +--- + +# Find Skills + +Search the open skill ecosystem for useful agent skills. + +## How to Search + +```bash +npx skills search "QUERY" +``` + +## How to Install + +```bash +npx skills add REPO_URL --skill SKILL_NAME +``` + +## Examples + +```bash +npx skills search "code review" +npx skills search "testing" +npx skills search "documentation" +npx skills add https://github.com/intellectronica/agent-skills --skill context7 +``` + +## Tips + +- Search with broad terms first, then narrow down +- Skills are installed to `.cursor/skills/SKILL_NAME/` +- After installing, the skill is available in the current project diff --git a/.cursor/skills/fix-merge-conflicts/SKILL.md b/.cursor/skills/fix-merge-conflicts/SKILL.md new file mode 100644 index 0000000..95d4b05 --- /dev/null +++ b/.cursor/skills/fix-merge-conflicts/SKILL.md @@ -0,0 +1,83 @@ +--- +name: fix-merge-conflicts +description: Resolve all merge conflicts on the current branch non-interactively, validate the build, and commit. Use when the user says "fix merge conflicts", "resolve conflicts", or when git status shows conflicting files. +disable-model-invocation: true +--- + +# Fix Merge Conflicts + +Resolve all merge conflicts on the current branch non-interactively and leave the repo buildable. + +## Constraints + +- Do not ask the user for input. Make best-effort decisions and explain them in a summary. +- Prefer minimal changes that preserve both sides' intent. +- Do not push or tag — only commit locally. + +## Workflow + +### 1. Detect conflicts + +```bash +git status --porcelain +``` + +Collect files with `U` statuses or containing `<<<<<<<` / `=======` / `>>>>>>>` markers. + +### 2. Resolve conflicts per file + +Open each conflicting file and remove conflict markers. Merge both sides logically when feasible. + +**When sides are mutually exclusive**, pick the variant that: +1. Compiles and passes type checks +2. Preserves existing public APIs and behavior + +**File-type strategies:** + +| File type | Strategy | +|-----------|----------| +| `package.json` | Merge keys conservatively, then `yarn install` to regenerate `yarn.lock` | +| `yarn.lock` | Never manually edit — regenerate with `yarn install` | +| Config files (`.json`, `.yaml`) | Preserve union of safe settings; don't delete required fields | +| Markdown / text | Include both unique sections, deduplicate headings | +| Binary files | Prefer current branch (ours) | +| Generated / build artifacts (`dist/`) | Prefer current branch (ours), or regenerate with `yarn build` | + +### 3. Validate + +Run build check. Fix any failures before proceeding. + +```bash +yarn build +``` + +If `package.json` was modified, run `yarn install` first. + +### 4. Verify no remaining markers + +```bash +rg '<<<<<<<|=======|>>>>>>>' --type ts --type json +``` + +If any markers remain, go back and resolve them. + +### 5. Finalize + +```bash +git add -A +git commit -m "chore: resolve merge conflicts" +``` + +## Operational Guidance + +- If a resolution is ambiguous and blocks the build, prefer the variant that compiles. +- For large refactors causing conflicts, keep consistent imports, types, and module boundaries. +- Keep edits minimal — don't reformat unrelated code. +- Format resolved files with `yarn prettier` after changes. + +## Deliverables + +- Clean working tree with all conflicts resolved +- Passing `yarn build` +- One local commit: `chore: resolve merge conflicts` +- Brief summary of files touched and notable resolution choices diff --git a/.cursor/skills/implement-plan/SKILL.md b/.cursor/skills/implement-plan/SKILL.md new file mode 100644 index 0000000..e7581f3 --- /dev/null +++ b/.cursor/skills/implement-plan/SKILL.md @@ -0,0 +1,93 @@ +--- +name: implement-plan +description: Orchestrates implementation of a multi-task plan by spawning plan-implementer subagents in parallel. Use when the user provides a plan file or plan text and asks to implement it, execute it, or says "implement plan", "run plan", "execute plan". +--- + +# Implement Plan + +You are the **orchestrator**. Your job is to execute the attached plan by delegating tasks to `plan-implementer` subagents. Preserve your context window for coordination — never implement tasks yourself. + +## Workflow + +### 1. Analyze the Plan + +Read the plan the user attached. Identify: + +- All discrete tasks/steps +- Dependencies between tasks (which must run sequentially vs. can run in parallel) +- Any ambiguous items that need clarification before starting + +If anything is unclear, ask the user before proceeding. + +### 2. Group Tasks for Parallelization + +Partition tasks into **parallel batches** based on dependencies: + +``` +Batch 1 (parallel): [tasks with no dependencies] +Batch 2 (parallel): [tasks that depend on batch 1] +Batch 3 (parallel): [tasks that depend on batch 2] +... +``` + +**Rules:** + +- Max 4 concurrent subagents (tool limitation) +- Tasks touching the same file(s) go in the same subagent or sequential batches — never parallel +- Small related tasks can be grouped into one subagent to reduce overhead +- Large independent tasks get their own subagent + +### 3. Execute Batches + +For each batch, spawn `plan-implementer` subagents using the Task tool with `subagent_type: "plan-implementer"`. + +Each subagent prompt must include: + +- **Exact tasks** to implement (copy from the plan, don't paraphrase loosely) +- **File paths** and context needed to work independently +- **Constraints** or edge cases from the plan + +Use `model: "fast"` for straightforward tasks. Omit model for complex ones. + +Wait for all subagents in a batch to complete before starting the next batch. + +### 4. Handle Failures + +When a subagent reports PARTIAL or FAILED: + +- Read its report to understand what failed and why +- Decide: retry with more context, reassign to a different batch, or implement the fix yourself if trivial +- Don't retry blindly — adjust the prompt or approach + +### 5. Verify + +After all batches complete: + +1. Run `yarn build` to confirm everything compiles +2. Run `yarn test` to confirm tests pass + +### 6. Report + +Summarize to the user: + +``` +## Plan Execution Summary + +### Completed +- Task 1 — files modified +- Task 2 — files modified + +### Failed (if any) +- Task N — reason, what was tried + +### Verification +- Build: PASS/FAIL +- Tests: PASS/FAIL +``` + +## Key Principles + +- **You orchestrate, subagents implement.** Don't code changes yourself unless it's a trivial one-liner fix for a subagent failure. +- **Context is precious.** Every build log and file read you do in the main thread is context you can't get back. Delegate liberally. +- **Parallelize aggressively.** The faster batches finish, the faster the plan is done. Only serialize when dependencies demand it. +- **Verify at the end, not in between.** Subagents run their own build checks. You do a final holistic verification. diff --git a/.cursor/skills/issue-format/SKILL.md b/.cursor/skills/issue-format/SKILL.md new file mode 100644 index 0000000..73d1082 --- /dev/null +++ b/.cursor/skills/issue-format/SKILL.md @@ -0,0 +1,30 @@ +--- +name: issue-format +description: Formats GitHub issue suggestions with a short title and informal problem description. Use when proposing GitHub issues for bugs, features, or improvements discovered during work. +--- + +# Issue Format + +## Template + +``` +> **GitHub issue:** +> - **Title:** `short issue title here` +> - **Description:** 2-3 sentences describing the problem as if unresolved. +``` + +## Rules + +1. Title is as short as possible, wrapped in backticks +2. Description describes the **problem**, not the solution +3. Write as if the issue hasn't been fixed yet +4. Use backticks for code references in the description +5. Keep it informal and concise — 2-3 sentences max + +## Example + +``` +> **GitHub issue:** +> - **Title:** `useReplies returns stale data after reset` +> - **Description:** When calling `reset()` on `useReplies`, subsequent `loadMore` calls return previously cached replies instead of fetching fresh data. This causes the UI to show outdated replies after a user manually refreshes a thread. +``` diff --git a/.cursor/skills/make-closed-issue/SKILL.md b/.cursor/skills/make-closed-issue/SKILL.md new file mode 100644 index 0000000..4fc1079 --- /dev/null +++ b/.cursor/skills/make-closed-issue/SKILL.md @@ -0,0 +1,173 @@ +--- +name: make-closed-issue +description: Create a GitHub issue from recent changes, commit only relevant diffs on a short-lived task branch, push that branch, and open a PR into master that will close the issue on merge. Use when the user says "make closed issue", "close issue", or wants to create a tracked, already-resolved GitHub issue for completed work. +--- + +# Make Closed Issue + +Creates a GitHub issue, commits relevant changes on a review branch, pushes the branch, and opens a PR into `master` that closes the issue when merged. + +## Inputs + +- What changed and why (from prior conversation context) +- Uncommitted or staged git changes in the working tree + +## Workflow + +### 1. Determine label(s) + +Ask the user using AskQuestion (multi-select): + +| Option | When | +|--------|------| +| `bug` | Bug fix | +| `enhancement` | New feature | +| `bug` + `enhancement` | New feature that also fixes a bug | +| `documentation` | README, AGENTS.md, docs-only changes | + +### 2. Ensure branch workflow is reviewable + +- If already on a short-lived task branch such as `feature/*`, `fix/*`, `docs/*`, or `chore/*`, stay on it. +- If on `master`, create a task branch before staging or committing. +- Do **not** commit the work directly on `master` when PR review bots or human review are expected. + +Suggested naming: + +- `feature/short-slug` +- `fix/short-slug` +- `docs/short-slug` +- `chore/short-slug` + +Example: + +```bash +git switch -c fix/replies-cache-reset +``` + +### 3. Review diffs for relevance + +```bash +git status +git diff +git diff --cached +``` + +Identify which files relate to the work done in this conversation. Only relevant changes get committed. Unrelated files must be excluded from staging. + +**Important**: `git add -p` and `git add -i` are not available (interactive mode unsupported). If a file has mixed relevant/irrelevant changes, include the entire file and note the caveat to the user. + +### 4. Generate issue title and description + +From the conversation context: + +- **Title**: Short, present-tense, describes the **problem** (not the solution). Use backticks for hooks, stores, commands, or literal strings when helpful. +- **Description**: 2-3 sentences about the problem. Use backticks for code references or literal names. Write as if the issue hasn't been fixed yet. + +### 5. Create the issue + +```bash +gh issue create \ + --repo bitsocialnet/bitsocial-react-hooks \ + --title "ISSUE_TITLE" \ + --body "ISSUE_DESCRIPTION" \ + --label "LABEL1,LABEL2" \ + --assignee plebe1us +``` + +Capture the issue number from the output. + +### 6. Commit relevant changes + +Stage only the relevant files: + +```bash +git add file1.ts file2.ts ... +``` + +Commit using Conventional Commits with scope: + +```bash +git commit -m "$(cat <<'EOF' +type(scope): concise title + +Optional 1-sentence description only if the title isn't self-explanatory. +EOF +)" +``` + +- **Types**: `fix`, `feat`, `perf`, `refactor`, `docs`, `chore` +- **Scope**: area of the codebase (for example `feeds`, `replies`, `accounts`, `agent-workflow`) +- Prefer title-only commits when the title already says enough + +### 7. Push branch and open PR + +Push the current task branch to origin and open a PR into `master`. + +Use `Closes #ISSUE_NUMBER` in the PR body so the issue closes automatically when the PR is merged. + +```bash +COMMIT_HASH=$(git rev-parse HEAD) +BRANCH_NAME=$(git branch --show-current) +git push -u origin "$BRANCH_NAME" + +gh pr create \ + --repo bitsocialnet/bitsocial-react-hooks \ + --base master \ + --head "$BRANCH_NAME" \ + --title "PR_TITLE" \ + --body "$(cat <..HEAD +``` + +If there are no commits since the tag, stop — nothing to update. + +### 3. Analyze the commits + +Categorize by Conventional Commits prefix: + +| Prefix | Category | +|--------|----------| +| `feat:` | New features | +| `fix:` | Bug fixes | +| `perf:` | Performance improvements | +| `refactor:` | Refactors / internal changes | +| `chore:`, `docs:`, `ci:` | Maintenance (mention only if significant) | +| No prefix | Read the title to infer category | + +### 4. Write the summary + +Compose a concise release description. Rules: + +- **Start with** "This version..." or "This release..." +- **Be concise** — a few sentences, not a full changelog +- **Highlight the most impactful changes** — lead with the biggest features or fixes +- **Group similar changes** — e.g. "several bug fixes" instead of listing each one +- **Use plain language** — this is user-facing, not developer-facing + +### 5. Report + +Display the release description to the user for review. diff --git a/.cursor/skills/review-and-merge-pr/SKILL.md b/.cursor/skills/review-and-merge-pr/SKILL.md new file mode 100644 index 0000000..7398a2c --- /dev/null +++ b/.cursor/skills/review-and-merge-pr/SKILL.md @@ -0,0 +1,175 @@ +--- +name: review-and-merge-pr +description: Review an open GitHub pull request, inspect feedback from CI, review bots, and human reviewers, decide which findings are valid, implement fixes on the PR branch, merge the PR into master when it is ready, and finalize the linked GitHub issue and project status after merge. Use when the user says "check the PR", "address review comments", "review PR feedback", or "merge this PR". +--- + +# Review And Merge Pr + +## Overview + +Use this skill after a feature branch already has an open PR into `master`. +Stay on the PR branch, treat review bots as input rather than authority, and only merge once the branch is verified and the remaining comments are either fixed or explicitly declined with a reason. + +## Workflow + +### 1. Identify the target PR + +Prefer the PR for the current branch when the branch is not `master`. +If the current branch is `master`, inspect open PRs and choose the one that matches the user request. +If there is no open PR yet, stop and use `make-closed-issue` first. + +Useful commands: + +```bash +gh pr status +gh pr list --repo bitsocialnet/bitsocial-react-hooks --state open +gh pr view --repo bitsocialnet/bitsocial-react-hooks --json number,title,url,headRefName,baseRefName,isDraft,reviewDecision,mergeStateStatus +``` + +### 2. Gather all review signals before changing code + +Read the PR state, checks, issue comments, review summaries, and inline review comments before deciding what to change. +Do not merge based only on the top-level review verdict. + +Useful commands: + +```bash +gh pr view --repo bitsocialnet/bitsocial-react-hooks --json number,title,url,headRefName,baseRefName,isDraft,reviewDecision,mergeStateStatus +gh pr checks +gh api "repos/bitsocialnet/bitsocial-react-hooks/issues//comments?per_page=100" +gh api "repos/bitsocialnet/bitsocial-react-hooks/pulls//reviews?per_page=100" +gh api "repos/bitsocialnet/bitsocial-react-hooks/pulls//comments?per_page=100" +``` + +Focus on comments from: + +- CodeRabbit or other review bots +- human reviewers +- failing CI checks + +### 3. Triage findings instead of blindly applying them + +Sort feedback into these buckets: + +- `must-fix`: correctness bugs, broken behavior, crashes, security issues, test failures, reproducible regressions +- `should-fix`: clear maintainability or edge-case issues with concrete evidence +- `decline`: false positives, stale comments, duplicate findings, speculative style-only suggestions, or feedback already addressed in newer commits + +Rules: + +- Never merge with unresolved `must-fix` findings. +- Do not accept a bot finding without reading the relevant code and diff. +- If a finding is ambiguous but high-risk, ask the user before merging. +- If a comment is wrong or stale, explain why in the PR rather than silently ignoring it. + +### 4. Work on the PR branch and keep the PR updated + +Switch to the PR branch if needed, apply the valid fixes, and push new commits to the same branch. +Do not open a replacement PR unless the user explicitly asks for that. + +Useful commands: + +```bash +git switch +git fetch origin +git status --short --branch +git add +git commit -m "fix(scope): address review feedback" +git push +``` + +After code changes, follow repo verification rules from `AGENTS.md`: + +- run `yarn build` +- run `yarn test` after adding or changing tests +- if hooks or stores changed, run the coverage command and `node scripts/verify-hooks-stores-coverage.mjs` +- run `yarn prettier` before the final review-driven commit +- if local verification dirties tracked `dist/` output, restore it before committing + +### 5. Report back on the PR before merging + +Summarize what was fixed and what was declined. +Use `gh pr comment` for a concise PR update when the branch changed because of review feedback. + +Example: + +```bash +gh pr comment --repo bitsocialnet/bitsocial-react-hooks --body "Addressed the valid review findings in the latest commit. Remaining comments are stale or not applicable for the reasons checked locally." +``` + +### 6. Merge only when the PR is actually ready + +Merge only if all of these are true: + +- the PR is not draft +- required checks are passing +- the branch is mergeable into `master` +- no unresolved `must-fix` reviewer findings remain +- the latest code was verified locally after the last review-driven change + +Preferred merge command: + +```bash +gh pr merge --repo bitsocialnet/bitsocial-react-hooks --squash --delete-branch +``` + +### 7. Finalize the linked issue and project item + +After merge, inspect the PR's linked closing issue. +If the merge did not close the issue automatically, close it manually. +Then ensure the linked issue is on the `bitsocial-react-hooks` project and its status is `Done`. + +Useful commands: + +```bash +ISSUE_NUMBER=$(gh pr view --repo bitsocialnet/bitsocial-react-hooks --json closingIssuesReferences --jq '.closingIssuesReferences[0].number // empty') + +if [ -n "$ISSUE_NUMBER" ]; then + ISSUE_STATE=$(gh issue view "$ISSUE_NUMBER" --repo bitsocialnet/bitsocial-react-hooks --json state --jq '.state') + if [ "$ISSUE_STATE" != "CLOSED" ]; then + gh issue close "$ISSUE_NUMBER" --repo bitsocialnet/bitsocial-react-hooks + fi + + ITEM_ID=$(gh project item-list 6 --owner bitsocialnet --limit 1000 --format json --jq ".items[] | select(.content.number == $ISSUE_NUMBER) | .id" | head -n1) + if [ -z "$ITEM_ID" ]; then + ITEM_JSON=$(gh project item-add 6 --owner bitsocialnet --url "https://github.com/bitsocialnet/bitsocial-react-hooks/issues/$ISSUE_NUMBER" --format json) + ITEM_ID=$(echo "$ITEM_JSON" | jq -r '.id') + fi + + FIELD_JSON=$(gh project field-list 6 --owner bitsocialnet --format json) + STATUS_FIELD_ID=$(echo "$FIELD_JSON" | jq -r '.fields[] | select(.name=="Status") | .id') + DONE_OPTION_ID=$(echo "$FIELD_JSON" | jq -r '.fields[] | select(.name=="Status") | .options[] | select(.name=="Done") | .id') + + gh project item-edit --id "$ITEM_ID" --project-id PVT_kwDODohK7M4BQoZJ --field-id "$STATUS_FIELD_ID" --single-select-option-id "$DONE_OPTION_ID" +fi +``` + +### 8. Clean up local state after merge + +After the PR is merged: + +```bash +git switch master +git pull --ff-only +git branch -D 2>/dev/null || true +git branch -D "pr/" 2>/dev/null || true +``` + +If the PR branch lived in a dedicated worktree, remove that worktree after leaving it: + +```bash +git worktree list +git worktree remove /path/to/worktree +``` + +### 9. Report the outcome + +Tell the user: + +- which findings were fixed +- which findings were declined and why +- which verification commands ran +- whether the PR was merged +- whether the linked issue was confirmed closed +- whether the linked project item was confirmed `Done` +- whether the feature branch, local `pr/` alias, and any worktree were cleaned up diff --git a/.cursor/skills/you-might-not-need-an-effect/SKILL.md b/.cursor/skills/you-might-not-need-an-effect/SKILL.md new file mode 100644 index 0000000..0091f9e --- /dev/null +++ b/.cursor/skills/you-might-not-need-an-effect/SKILL.md @@ -0,0 +1,121 @@ +--- +name: you-might-not-need-an-effect +description: Analyze code for useEffect anti-patterns and refactor to simpler alternatives. Use when the user says "you might not need an effect", "check effects", "useEffect audit", or asks to review useEffect usage. +disable-model-invocation: true +--- + +# You Might Not Need an Effect + +Analyze code for `useEffect` anti-patterns and refactor to simpler, more correct alternatives. + +Based on https://react.dev/learn/you-might-not-need-an-effect + +## Arguments + +- **scope**: what to analyze (default: uncommitted changes). Examples: `diff to main`, `src/hooks/`, `whole codebase` +- **fix**: whether to apply fixes (default: `true`). Set to `false` to only propose changes. + +## Workflow + +1. **Determine scope** — get the relevant code: + - Default: `git diff` for uncommitted changes + - If a directory/file is specified, read those files + - If "whole codebase": search all `.ts` files for `useEffect` + +2. **Scan for anti-patterns** — check each `useEffect` against the patterns below + +3. **Fix or propose** — depending on the `fix` argument: + - `fix=true`: apply the refactors, then verify with `yarn build` + - `fix=false`: list each anti-pattern found with a before/after code suggestion + +4. **Report** — summarize what was found and changed + +## Anti-Patterns to Catch + +### 1. Deriving state during render (no effect needed) + +If you're computing something from existing props or state, calculate it during render. + +```typescript +// ❌ Anti-pattern +const [fullName, setFullName] = useState(''); +useEffect(() => { + setFullName(firstName + ' ' + lastName); +}, [firstName, lastName]); + +// ✅ Fix — derive during render +const fullName = firstName + ' ' + lastName; +``` + +### 2. Caching expensive calculations (useMemo, not useEffect) + +```typescript +// ❌ Anti-pattern +const [filtered, setFiltered] = useState([]); +useEffect(() => { + setFiltered(items.filter(item => item.active)); +}, [items]); + +// ✅ Fix — calculate during render (useMemo only if profiling shows it's needed) +const filtered = items.filter(item => item.active); +``` + +### 3. Resetting state when props change (use key, not useEffect) + +```typescript +// ❌ Anti-pattern +useEffect(() => { + setComment(''); +}, [postCid]); + +// ✅ Fix — use key on the component to reset state + +``` + +### 4. Syncing with external stores (use Zustand selectors) + +```typescript +// ❌ Anti-pattern +const [data, setData] = useState(null); +useEffect(() => { + const unsub = someStore.subscribe((s) => setData(s.data)); + return unsub; +}, []); + +// ✅ Fix — use the Zustand store directly +const data = useSomeStore((s) => s.data); +``` + +### 5. Initializing global singletons (use module scope or lazy init) + +```typescript +// ❌ Anti-pattern +useEffect(() => { + initializeSomething(); +}, []); + +// ✅ Fix — module-level init (runs once on import) +if (typeof window !== 'undefined') { + initializeSomething(); +} +``` + +## Project-Specific Context + +This is a hooks library, not an app. Effects in this codebase are more likely to be legitimate (subscribing to plebbit-js events, managing store listeners) than in a typical React app. Be extra careful before removing effects that manage subscriptions or event listeners with cleanup functions. + +| Pattern | Likely legitimate | +|---------|------------------| +| Store subscription with cleanup | Yes — keep | +| plebbit-js event listener with cleanup | Yes — keep | +| Deriving state from other state | No — compute during render | +| Setting state from props | No — derive or use key | +| One-time initialization | Maybe — consider module scope | + +## When useEffect IS Appropriate + +Not every effect is wrong. Keep `useEffect` for: +- Subscribing to plebbit-js events with proper cleanup +- Managing Zustand store subscriptions with cleanup +- Synchronizing with browser APIs (resize, intersection observer, etc.) +- Running code on mount that genuinely has no alternative diff --git a/.gitignore b/.gitignore index b8cdcb2..c169eff 100644 --- a/.gitignore +++ b/.gitignore @@ -28,9 +28,29 @@ packages/example/.next/ yarn-error.log .env -# agent files -.cursor -.codex +# Repo-managed AI tooling +.cursor/* +.cursor/.DS_Store +!.cursor/agents/ +!.cursor/agents/** +!.cursor/hooks/ +!.cursor/hooks/** +!.cursor/hooks.json +!.cursor/skills/ +!.cursor/skills/** +.cursor/**/.DS_Store + +.codex/* +.codex/.DS_Store +!.codex/agents/ +!.codex/agents/** +!.codex/config.toml +!.codex/hooks/ +!.codex/hooks/** +!.codex/hooks.json +!.codex/skills/ +!.codex/skills/** +.codex/**/.DS_Store # generated browser test screenshots test/**/__screenshots__/ diff --git a/AGENTS.md b/AGENTS.md index 65dc9b7..435c64b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -34,6 +34,10 @@ This repo is a temporary fork of [plebbit/plebbit-react-hooks](https://github.co | Public API changed (`src/index.ts`, `src/types.ts`) | Ensure backward compatibility; update README if signatures changed | | User-facing behavior/feature added or changed | Update `README.md` usage/docs in the same task before marking work complete | | Tests added or changed (`src/**/*.test.ts`, `test/`) | Run `yarn test` to verify | +| New reviewable feature/fix/docs/chore started while on `master` | Create a short-lived `feature/*`, `fix/*`, `docs/*`, or `chore/*` branch from `master` before editing; use a separate worktree only for parallel tasks | +| User wants a tracked issue/PR for work already done | Use the `make-closed-issue` skill to create the issue, push the task branch, and open a PR that closes it on merge | +| Open PR needs feedback triage or merge readiness check | Use the `review-and-merge-pr` skill to inspect CI/reviewer feedback, fix valid findings, and merge only after verification | +| Repo AI workflow files changed (`.codex/**`, `.cursor/**`) | Keep the Codex and Cursor copies aligned when they represent the same workflow; update `AGENTS.md` if the default agent policy changes | | GitHub operation needed | Use `gh` CLI, not GitHub MCP | | User asks for commit/issue phrasing | Use `docs/agent-playbooks/commit-issue-format.md` | | Surprising/ambiguous repo behavior encountered | Alert developer and, once confirmed, document in `docs/agent-playbooks/known-surprises.md` | @@ -82,6 +86,16 @@ src/ - Type definitions that cross module boundaries go in `src/types.ts`. - Add comments for complex/non-obvious code; skip obvious comments. +### Git Workflow Rules + +- Keep `master` reviewable. Do not treat `master` as a scratch branch. +- If the user asks for a reviewable feature/fix/docs/chore and the current branch is `master`, create a short-lived task branch before making code changes unless the user explicitly asks to work directly on `master`. +- Name short-lived branches by intent: `feature/*`, `fix/*`, `docs/*`, `chore/*`. +- Open PRs from task branches into `master` so review bots and humans can inspect the actual change before merge. +- Prefer short-lived task branches over a long-lived `develop` branch unless the user explicitly asks for a staging-branch workflow. +- Use worktrees only when parallel tasks need isolated checkouts. One active task branch per worktree. +- After a reviewed branch is merged, prefer deleting it to keep branch drift and merge conflicts low. + ### Documentation Rules - Keep `README.md` current for all user-facing features, behavior changes, and new workflows. @@ -116,6 +130,15 @@ src/ - Do not use browser MCP servers. - If many MCP tools are present in context, warn user and suggest disabling unused MCPs. +### AI Tooling Rules + +- Treat `.codex/` and `.cursor/` as repo-managed contributor tooling, not private scratch space. +- Keep equivalent workflow files aligned across both toolchains when both directories contain the same skill, hook, or agent. +- When changing shared agent behavior, update the relevant files in `.codex/skills/`, `.cursor/skills/`, `.codex/agents/`, `.cursor/agents/`, `.codex/hooks/`, `.cursor/hooks/`, and their `hooks.json` or config entry points as needed. +- If `AGENTS.md` references a skill, agent, or hook, prefer a tracked file under `.codex/` or `.cursor/` rather than an untracked local-only instruction. +- When adding or changing repo-managed skills, agents, or hooks, commit the matching `.codex/` and `.cursor/` files in the same task so the workflow change is reviewable. +- Review `.codex/hooks.json` and `.cursor/hooks.json` before changing agent orchestration or hook behavior, because they are the entry points contributors will actually load. + ### Security and Boundaries - Never commit secrets or API keys.