chore(agent-workflow): track AI tooling and PR-first branch flow#26
chore(agent-workflow): track AI tooling and PR-first branch flow#26tomcasaburi merged 2 commits intomasterfrom
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (46)
📝 WalkthroughWalkthroughAdds tracked AI tooling and a set of repo-managed agent specs, hook configurations/scripts, and skill documentation; updates AGENTS.md and .gitignore so the new .codex/.cursor files are tracked and executed by hook runners. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
src/lib/debug-utils.ts (1)
4-11:⚠️ Potential issue | 🟡 MinorOrphaned data under old store names won't be cleared.
After this rename, existing users with data stored under the old names (
plebbitReactHooks-subplebbits,plebbitReactHooks-subplebbitsPages) will retain orphaned data thatdeleteDatabasesno longer targets. If the intent is a clean migration, consider also clearing the legacy stores—either here or via a one-time migration utility.🛡️ Optional: Clear both old and new store names during transition
const deleteDatabases = () => Promise.all([ localForage.createInstance({ name: "plebbitReactHooks-accountsMetadata" }).clear(), localForage.createInstance({ name: "plebbitReactHooks-accounts" }).clear(), localForageLru.createInstance({ name: "plebbitReactHooks-communities" }).clear(), + localForageLru.createInstance({ name: "plebbitReactHooks-subplebbits" }).clear(), // legacy cleanup localForageLru.createInstance({ name: "plebbitReactHooks-comments" }).clear(), localForageLru.createInstance({ name: "plebbitReactHooks-communitiesPages" }).clear(), + localForageLru.createInstance({ name: "plebbitReactHooks-subplebbitsPages" }).clear(), // legacy cleanup ]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/debug-utils.ts` around lines 4 - 11, The deleteDatabases function no longer clears legacy localForage stores, leaving orphaned data; update deleteDatabases to also clear the old store names (plebbitReactHooks-subplebbits and plebbitReactHooks-subplebbitsPages) alongside the new names so both legacy and current databases are removed during migration or provide a one-time migration routine that deletes those legacy instances using the same localForage/localForageLru createInstance(...).clear() pattern used for the other stores.src/stores/accounts/accounts-actions.test.ts (1)
393-422:⚠️ Potential issue | 🟡 MinorThese
accountNamecases still pass if the option is ignored.
publishCommunityEdit,createCommunity, anddeleteCommunityonly assert “no throw” or that an address exists. They would still go green if the implementation silently used the active account instead of the named one. Please assert against the named account’s store entry or spy on that account’splebbitinstance.Also applies to: 454-467
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/stores/accounts/accounts-actions.test.ts` around lines 393 - 422, The tests for publishCommunityEdit, createCommunity, and deleteCommunity currently only check for no-throw or that an address exists, so update those tests to verify the named account was actually used: after calling accountsActions.createAccount("Name") and invoking accountsActions.publishCommunityEdit/createCommunity/deleteCommunity with the accountName argument, assert that the operation used that account's store entry (e.g., read the account record for "SubEditAccount"/"CreateSubAccount" from the accounts store and assert its expected plebbit/request was used) or spy on the named account's plebbit instance to confirm the API method was called for that named account rather than the active account; modify the tests referencing publishCommunityEdit, createCommunity, and deleteCommunity to include one of these explicit assertions.src/hooks/feeds/feeds.ts (2)
199-223:⚠️ Potential issue | 🟠 MajorForward the rest of the feed options when buffering.
useBufferedFeedsderivespostsPerPages,filters, andnewerThans, but theaddFeedToStore()call drops them. The buffered feed stored under thatfeedNamecan therefore be built with the wrong page size or unfiltered posts.Suggested fix
- addFeedToStore(feedName, uniqueCommunityAddresses, sortType, account, isBufferedFeed).catch( - (error: unknown) => - log.error("useBufferedFeeds addFeedToStore error", { feedName, error }), - ); + addFeedToStore( + feedName, + uniqueCommunityAddresses, + sortType, + account, + isBufferedFeed, + postsPerPages[i], + filters[i], + newerThans[i], + ).catch((error: unknown) => + log.error("useBufferedFeeds addFeedToStore error", { feedName, error }), + );Also applies to: 233-249
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/feeds/feeds.ts` around lines 199 - 223, The buffering logic in useBufferedFeeds drops postsPerPage, filter, and newerThan when calling addFeedToStore, causing buffered feeds (identified by feedName computed from feedNames/useFeedNames and sortTypes/uniqueCommunityAddressesArrays) to be recreated with wrong page size or filters; update the addFeedToStore call inside useBufferedFeeds to include and forward the remaining feed options (postsPerPage, filter, newerThan and any other feedOptions fields you derived into postsPerPages/filters/newerThans) so the stored buffered feed preserves the original feedOptions used to compute feedName and pagination.
291-308:⚠️ Potential issue | 🟠 MajorFix mutation of incoming arrays in normalization helpers.
Both
useUniqueSortedArraysanduseUniqueSortedcall.sort()directly on incoming arrays, which mutates them in place. This reorders parent state arrays (communityAddresses/feedsOptions[*].communityAddresses) and can unintentionally change feed keys as a side effect.Suggested fix
- uniqueSorted.push([...new Set(stringsArray.sort())]); + uniqueSorted.push([...new Set([...stringsArray].sort())]); ... - return [...new Set(stringsArray.sort())]; + return [...new Set([...stringsArray].sort())];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/feeds/feeds.ts` around lines 291 - 308, The helpers mutate incoming arrays by calling .sort() directly; update useUniqueSortedArrays and useUniqueSorted to sort copies instead of the originals: for useUniqueSortedArrays, iterate arrs and for each stringsArray create a shallow copy first (e.g., [...stringsArray]) then sort and dedupe with new Set before pushing to uniqueSorted; for useUniqueSorted, return a deduped sorted array built from a shallow copy of stringsArray (e.g., [...stringsArray].sort()) so the parent arrays (communityAddresses / feedsOptions[*].communityAddresses) are not mutated by useUniqueSortedArrays or useUniqueSorted.src/hooks/accounts/accounts.test.ts (1)
2347-2392:⚠️ Potential issue | 🟠 MajorRestore the prototype mock in a
finallyblock.If any
waitForor assertion fails before Line 2392,Community.prototype.rolesToGetstays patched and contaminates later tests. Wrap the override and the test body intry/finallyso cleanup is guaranteed.Proposed fix
const moderatorAuthorAddress = "author address"; const moderatingCommunityAddress = "moderating community address"; const rolesToGet = Community.prototype.rolesToGet; Community.prototype.rolesToGet = () => ({ [moderatorAuthorAddress]: { role: "moderator" }, }); - const rendered = renderHook<any, any>((communityAddress) => { - const { accountCommunities } = useAccountCommunities(); - const account = useAccount(); - const { setAccount } = accountsActions; - const community = useCommunity({ communityAddress }); - return { accountCommunities, setAccount, account }; - }); - const waitFor = testUtils.createWaitFor(rendered); - await waitFor(() => rendered.result.current.account); + try { + const rendered = renderHook<any, any>((communityAddress) => { + const { accountCommunities } = useAccountCommunities(); + const account = useAccount(); + const { setAccount } = accountsActions; + const community = useCommunity({ communityAddress }); + return { accountCommunities, setAccount, account }; + }); + const waitFor = testUtils.createWaitFor(rendered); + await waitFor(() => rendered.result.current.account); - // change author address - await act(async () => { - await rendered.result.current.setAccount({ - ...rendered.result.current.account, - author: { address: moderatorAuthorAddress }, - }); - }); - await waitFor( - () => rendered.result.current.account.author.address === moderatorAuthorAddress, - ); - // account communities are not yet added, will be added after we fetch the sub - expect(rendered.result.current.accountCommunities[moderatingCommunityAddress]).toBe( - undefined, - ); - expect(rendered.result.current.account.communities[moderatingCommunityAddress]).toBe( - undefined, - ); + // change author address + await act(async () => { + await rendered.result.current.setAccount({ + ...rendered.result.current.account, + author: { address: moderatorAuthorAddress }, + }); + }); + await waitFor( + () => rendered.result.current.account.author.address === moderatorAuthorAddress, + ); + expect(rendered.result.current.accountCommunities[moderatingCommunityAddress]).toBe( + undefined, + ); + expect(rendered.result.current.account.communities[moderatingCommunityAddress]).toBe( + undefined, + ); - // fetch the moderating community from the moderator account - rendered.rerender(moderatingCommunityAddress); - await waitFor(() => rendered.result.current.accountCommunities[moderatingCommunityAddress]); - expect(rendered.result.current.accountCommunities[moderatingCommunityAddress].role.role).toBe( - "moderator", - ); - await waitFor(() => rendered.result.current.account.communities[moderatingCommunityAddress]); - expect( - rendered.result.current.account.communities[moderatingCommunityAddress].role.role, - ).toBe("moderator"); - - // unmock the roles - Community.prototype.rolesToGet = rolesToGet; + // fetch the moderating community from the moderator account + rendered.rerender(moderatingCommunityAddress); + await waitFor(() => rendered.result.current.accountCommunities[moderatingCommunityAddress]); + expect( + rendered.result.current.accountCommunities[moderatingCommunityAddress].role.role, + ).toBe("moderator"); + await waitFor(() => rendered.result.current.account.communities[moderatingCommunityAddress]); + expect( + rendered.result.current.account.communities[moderatingCommunityAddress].role.role, + ).toBe("moderator"); + } finally { + Community.prototype.rolesToGet = rolesToGet; + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/accounts/accounts.test.ts` around lines 2347 - 2392, You currently replace Community.prototype.rolesToGet and restore it at the end of the test, but if an assertion or waitFor throws the mock remains patched; wrap the override and all following test logic that depends on it in a try/finally: store the original value in rolesToGet, set Community.prototype.rolesToGet = () => {...} before the try, run the test body (renders, waits, assertions) inside try, and in finally restore Community.prototype.rolesToGet = rolesToGet so the prototype is always reset even on failures.
♻️ Duplicate comments (1)
.cursor/skills/release-description/SKILL.md (1)
14-16:⚠️ Potential issue | 🟡 MinorGit tag sorting may not return the semantically latest release.
Same issue as in
.codex/skills/release-description/SKILL.md- the command uses--sort=-creatordatewhich could return an incorrect tag if tags are created out of chronological order.Refer to the comment on
.codex/skills/release-description/SKILL.mdlines 14-16 for details and suggested fix.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.cursor/skills/release-description/SKILL.md around lines 14 - 16, The git tag command `git tag --sort=-creatordate | head -1` can return a non-semantic "latest" tag if tags were created out of chronological order; replace that pipeline with a semver-aware sort such as using `--sort=-v:refname` so the command becomes `git tag --sort=-v:refname | head -1`, updating the occurrence of `git tag --sort=-creatordate | head -1` in the file to use `--sort=-v:refname` instead.
🟠 Major comments (25)
.codex/skills/review-and-merge-pr/SKILL.md-17-20 (1)
17-20:⚠️ Potential issue | 🟠 MajorDon't send the no-PR path back through
make-closed-issue.Line 19 points the agent back to the commit-first flow this PR is replacing. If no PR exists yet, the next step should be to create/open one for the current branch, not invoke the old close-on-commit workflow.
Suggested fix
-If there is no open PR yet, stop and use `make-closed-issue` first. +If there is no open PR yet, stop and create/open a PR for the current branch before proceeding.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.codex/skills/review-and-merge-pr/SKILL.md around lines 17 - 20, Update the decision flow in the SKILL.md logic so that when the current branch is not `master` and no open PR exists, the agent creates/opens a PR for the current branch instead of routing to the `make-closed-issue` (commit-first) path; specifically, remove or replace the sentence that instructs sending the no-PR case back through `make-closed-issue` and add a line instructing the agent to open a PR for the current branch (keeping the existing master-specific behavior of inspecting open PRs and choosing one matching the user request)..codex/agents/react-patterns-enforcer.md-13-17 (1)
13-17:⚠️ Potential issue | 🟠 MajorUse a broader strategy to detect changed files instead of only
HEAD~1.The
git diff --name-only HEAD~1 -- '*.ts'approach only inspects the diff between the current HEAD and its parent commit. This misses:
- Uncommitted/unstaged changes in the working tree
- Staged changes awaiting commit
- Changes from multiple commits if the agent runs after several commits
To catch all recently modified files, check:
Alternative detection approach
# Uncommitted working tree changes: git diff --name-only -- '*.ts' # Staged changes: git diff --cached --name-only -- '*.ts' # Last committed delta (if relevant): git diff --name-only HEAD~1 -- '*.ts'Then union these sets to get the full picture of recent changes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.codex/agents/react-patterns-enforcer.md around lines 13 - 17, The current detection uses the single command `git diff --name-only HEAD~1 -- '*.ts'`, which misses staged and working-tree changes; replace that logic with a union of three checks: `git diff --name-only -- '*.ts'` (working-tree unstaged), `git diff --cached --name-only -- '*.ts'` (staged), and `git diff --name-only HEAD~1 -- '*.ts'` (last commit), combine their outputs (deduplicate) to produce the final file list, and update the code where `git diff --name-only HEAD~1 -- '*.ts'` is invoked so it runs these three commands and merges results before proceeding..cursor/agents/react-patterns-enforcer.md-13-17 (1)
13-17:⚠️ Potential issue | 🟠 MajorUse
git diff --name-onlyinstead ofHEAD~1to detect all recent changes.The command
git diff --name-only HEAD~1only inspects the last committed delta. This misses files modified but not yet committed (staged or unstaged). Since the agent description says "Use after writing or modifying" without requiring a commit first, the current command will silently skip changed files when run on uncommitted changes.Replace with
git diff --name-onlyto detect working-tree changes, orgit diff --cached --name-onlyfor staged changes, depending on intended workflow. If comparing to a specific baseline, document it explicitly.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.cursor/agents/react-patterns-enforcer.md around lines 13 - 17, The current guidance runs git diff --name-only HEAD~1 which only shows the last commit; change the command text in the agent instructions to use git diff --name-only (or offer git diff --cached --name-only for staged-only) so uncommitted working-tree changes are detected as described, and update any adjacent documentation/comments to explain that git diff --name-only checks working-tree changes while --cached limits to staged files..codex/skills/fix-merge-conflicts/SKILL.md-58-60 (1)
58-60:⚠️ Potential issue | 🟠 MajorBroaden conflict marker detection to all file types and hidden directories.
The current command is limited to TypeScript and JSON files, which will miss unresolved merge conflicts in Markdown, YAML, shell scripts, and other file types. Additionally, it excludes hidden directories like
.codex/and.cursor/by default, which this PR is starting to track. For a skill teaching merge conflict resolution, the detection command should be comprehensive.Suggested fix
-rg '<<<<<<<|=======|>>>>>>>' --type ts --type json +rg -n --hidden --glob '!.git' '<<<<<<<|=======|>>>>>>>' .🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.codex/skills/fix-merge-conflicts/SKILL.md around lines 58 - 60, The ripgrep invocation that currently restricts search to TypeScript/JSON (the command using --type ts --type json and the pattern '<<<<<<<|=======|>>>>>>>') should be changed to scan all files and include hidden/ignored directories; remove the --type filters and add ripgrep flags to include hidden files and respect no-ignore (e.g., use --hidden and --no-ignore or -uu) so the search will catch conflict markers in Markdown, YAML, shell scripts, and tracked hidden dirs like .codex/.cursor; update the example command in SKILL.md to reflect these flags and keep the same conflict marker regex.src/lib/plebbit-js/plebbit-js-mock.test.ts-85-88 (1)
85-88:⚠️ Potential issue | 🟠 MajorUse the renamed community field consistently.
This test now passes
communityAddressesas a string, while the rest of the rename lands on singularcommunityAddress. If the plural form is intentional, the value should at least be an array; as written, the mock can accept an input shape the real publish path rejects.Suggested fix
const comment = await plebbit.createComment({ content: "content", - communityAddresses: "community address", + communityAddress: "community address", });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/plebbit-js/plebbit-js-mock.test.ts` around lines 85 - 88, The test is passing the old/incorrect field communityAddresses as a string; update the call to match the renamed API by using the singular communityAddress and provide the correct type (an array of addresses if the real publish path expects multiple addresses) — e.g., change the createComment call in plebbit-js-mock.test.ts to pass communityAddress: ["community address"] (or a single-element array) so the mock's input shape matches the real publish path and the createComment function signature..cursor/skills/review-and-merge-pr/SKILL.md-17-20 (1)
17-20:⚠️ Potential issue | 🟠 MajorDon't send no-PR cases back to
make-closed-issue.That reintroduces the close-on-commit flow this repo is replacing. If no PR exists yet, the skill should hand off to the PR-creation flow instead of the old issue-closing workflow.
Suggested fix
-If there is no open PR yet, stop and use `make-closed-issue` first. +If there is no open PR yet, stop and use the PR-creation workflow first. +Do not use `make-closed-issue`, because this repo now requires reviewed PRs before closing work.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.cursor/skills/review-and-merge-pr/SKILL.md around lines 17 - 20, The logic in the skill should not route "no PR exists" cases back to make-closed-issue; update the decision flow so when the current branch is not `master` it prefers the PR for that branch, and when the current branch is `master` it inspects open PRs to match the user request, but if no open PR is found it should hand off to the PR-creation flow (do not call or route to `make-closed-issue`); modify the control path that currently invokes `make-closed-issue` to instead invoke or queue the PR creation skill/workflow..codex/skills/deslop/SKILL.md-9-22 (1)
9-22:⚠️ Potential issue | 🟠 MajorDiff against
masterhere, notmain.This repo's default branch is
master, so these commands point at the wrong base. Either switch tomasteror resolve the default branch dynamically before diffing.Suggested fix
-Scan the diff against main and remove AI-generated slop introduced in this branch. +Scan the diff against `master` and remove AI-generated slop introduced in this branch. ... - git diff main...HEAD + git diff master...HEAD ... - git diff main + git diff master🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.codex/skills/deslop/SKILL.md around lines 9 - 22, The workflow examples in SKILL.md use "git diff main...HEAD" and "git diff main" which are wrong for this repo; update those examples to use the repo's default branch (replace "main" with "master") or show a dynamic approach (e.g., resolve the default branch) and add a short note explaining to use "master" when this repo's default branch is master so readers run the correct git diff command..cursor/skills/fix-merge-conflicts/SKILL.md-56-60 (1)
56-60:⚠️ Potential issue | 🟠 MajorSearch all files for leftover conflict markers.
The current
rgonly scans TS/JSON, so markers can survive in Markdown, YAML, lockfiles, or shell scripts while the skill reports success. Drop the type filters or derive the exact file list from git before declaring the repo clean.Suggested fix
-rg '<<<<<<<|=======|>>>>>>>' --type ts --type json +rg -n '^(<<<<<<<|=======|>>>>>>>)'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.cursor/skills/fix-merge-conflicts/SKILL.md around lines 56 - 60, Update the "Verify no remaining markers" step so the ripgrep invocation checks all tracked files instead of only TypeScript/JSON: locate the line containing the rg pattern '<<<<<<<|=======|>>>>>>>' in the "### 4. Verify no remaining markers" section and remove the --type ts --type json filters (or alternatively replace the command with one that enumerates files from git e.g., using git ls-files) so conflict markers cannot remain in Markdown, YAML, lockfiles, or scripts..codex/skills/you-might-not-need-an-effect/SKILL.md-27-31 (1)
27-31:⚠️ Potential issue | 🟠 MajorRun tests after
fix=truerefactors.Build-only verification is too weak for an automated effect-removal skill. These edits can preserve types while still breaking runtime behavior, so the workflow should run
yarn testwhenever it changes code.Suggested fix
- - `fix=true`: apply the refactors, then verify with `yarn build` + - `fix=true`: apply the refactors, then verify with `yarn build` + and `yarn test`🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.codex/skills/you-might-not-need-an-effect/SKILL.md around lines 27 - 31, Update the "3. **Fix or propose**" step so that when `fix=true` the workflow runs tests after building: replace the single `yarn build` verification with `yarn build` followed by `yarn test` and ensure the step fails (returns non-zero / aborts) if tests fail; refer to the "3. **Fix or propose**" block and the `fix=true` branch, and mention `yarn build` and `yarn test` explicitly so maintainers know to run both commands and treat test failures as errors.src/lib/plebbit-js/plebbit-js-mock-content.ts-1002-1004 (1)
1002-1004:⚠️ Potential issue | 🟠 MajorPreserve the edit payload when creating
CommunityEdit.
createCommunityEdit()now ignores the incoming options, andCommunityEditno longer copies any fields onto the publication. The mock loses address/title/description edits, so the community-edit flow diverges from the other publication mocks.🛠️ Proposed fix
- async createCommunityEdit() { - return new CommunityEdit(); + async createCommunityEdit(createCommunityEditOptions: any) { + return new CommunityEdit(createCommunityEditOptions); }-class CommunityEdit extends Publication {} +class CommunityEdit extends Publication { + constructor(publishCommunityEditOptions: any) { + super(); + for (const prop in publishCommunityEditOptions) { + // `@ts-ignore` + this[prop] = publishCommunityEditOptions[prop]; + } + } +}Also applies to: 1429-1429
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/plebbit-js/plebbit-js-mock-content.ts` around lines 1002 - 1004, createCommunityEdit() currently ignores the incoming options and returns a new empty CommunityEdit, losing edits (address/title/description); update createCommunityEdit(options) to pass the incoming options (payload) into the CommunityEdit constructor or call a setter so the instance copies/preserves all fields from the provided options, and ensure CommunityEdit's constructor or a copyFrom method assigns address, title, description and any other publication edit fields; apply the same change to the other occurrence of createCommunityEdit to keep mock behavior consistent with other publication mocks.src/lib/plebbit-js/plebbit-js-mock-content.ts-1197-1211 (1)
1197-1211:⚠️ Potential issue | 🟠 MajorDon't replace
this.postswith a plain object on first update.
JSON.parse(JSON.stringify(community))strips the non-enumerablecommunityback-reference fromPages. Afterthis.postsis overwritten, the reassignedgetPage()runs without that context, so loading another page after the first update can fail.🛠️ Proposed fix
async simulateGetCommunityOnFirstUpdateEvent() { this._getCommunityOnFirstUpdate = false; // `@ts-ignore` const community = await new Plebbit().getCommunity({ address: this.address }); const props = JSON.parse(JSON.stringify(community)); + const posts = props.posts; + delete props.posts; for (const prop in props) { if (prop.startsWith("_")) { continue; } // `@ts-ignore` this[prop] = props[prop]; } - this.posts.getPage = community.posts.getPage; + this.posts.pages = posts.pages; + this.posts.pageCids = posts.pageCids; this.updatingState = "succeeded";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/plebbit-js/plebbit-js-mock-content.ts` around lines 1197 - 1211, The code currently deep-clones `community` and copies its properties onto `this`, which strips the non-enumerable Pages back-reference and ends up replacing `this.posts` with a plain object; instead, when implementing simulateGetCommunityOnFirstUpdateEvent() skip copying the `posts` property (do not overwrite `this.posts`) and then assign the updater function from the real Pages object while preserving context, e.g., set `this.posts.getPage = community.posts.getPage.bind(this.posts)` (or otherwise reuse the original Pages instance) so the Pages back-reference remains intact and subsequent getPage calls work correctly.src/lib/plebbit-js/plebbit-js-mock-content.ts-1153-1163 (1)
1153-1163:⚠️ Potential issue | 🟠 MajorKeep
createdCommunitiesin sync whenedit()renames the address.After this method updates
this.address, the backing map still stays under the old key.plebbit.communitiesanddelete()will keep using stale data, and the truthy guard also blocks valid edits likefalseor"".🛠️ Proposed fix
async edit(editCommunityOptions: any) { assert( editCommunityOptions && typeof editCommunityOptions === "object", `invalid editCommunityOptions '${editCommunityOptions}'`, ); + const previousAddress = this.address; for (const prop in editCommunityOptions) { - if (editCommunityOptions[prop]) { + if (editCommunityOptions[prop] !== undefined) { // `@ts-ignore` this[prop] = editCommunityOptions[prop]; } } + if (previousAddress && this.address && previousAddress !== this.address) { + createdCommunities[this.address] = this; + delete createdCommunities[previousAddress]; + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/plebbit-js/plebbit-js-mock-content.ts` around lines 1153 - 1163, The edit() method currently skips falsy values and doesn't update the createdCommunities backing map when this.address is changed; modify edit(editCommunityOptions) to iterate only over own keys (e.g., Object.keys/editCommunityOptions) so empty string or false are allowed, capture the previousAddress before assigning this.address, apply property updates to this, and if previousAddress !== this.address update the plebbit.createdCommunities (or createdCommunities map) by removing the old key and inserting the new key pointing to this instance so plebbit.communities and delete() see the renamed entry..codex/hooks/yarn-install.sh-19-25 (1)
19-25:⚠️ Potential issue | 🟠 MajorPropagate
yarn installfailures instead of always succeeding.The hook returns exit code 0 regardless of whether
cdoryarn installfails. Line 19 uses|| exit 0to swallow directory change failures, and line 22 has no error handling foryarn install. This allows dependency resolution to break silently while the workflow reports success.🐛 Proposed fix
if [ "$file_path" = "package.json" ]; then # Change to project directory - cd "$(dirname "$0")/../.." || exit 0 + cd "$(dirname "$0")/../.." || exit 1 echo "package.json changed - running yarn install to update yarn.lock..." - yarn install + yarn install || exit 1 fi exit 0🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.codex/hooks/yarn-install.sh around lines 19 - 25, The hook currently swallows failures: the cd command uses "|| exit 0" and the script always ends with "exit 0", and "yarn install" has no error handling; modify .codex/hooks/yarn-install.sh to propagate errors by removing the "|| exit 0" on the cd invocation, checking the exit status of "yarn install" (or use "set -e" / "set -o errexit" at the top) and exit non‑zero on failure so that failures in cd or yarn install cause the hook to fail rather than always returning 0..codex/hooks/verify.sh-33-45 (1)
33-45:⚠️ Potential issue | 🟠 MajorDon't mask build/test failures in the verify hook.
This script always reports success because both commands are wrapped with
|| trueand the file ends withexit 0. That means callers cannot distinguish a clean verification run from a broken one, which defeats the purpose of averifyhook.Proposed fix
echo "Running build and tests..." echo "" +status=0 + # Run build (catches compilation errors) echo "=== yarn build ===" -yarn build 2>&1 || true +yarn build 2>&1 || status=1 echo "" # Run tests echo "=== yarn test ===" -yarn test 2>&1 || true +yarn test 2>&1 || status=1 echo "" restore_dist_worktree -echo "Verification complete." -exit 0 +if [ "$status" -ne 0 ]; then + echo "Verification failed." +else + echo "Verification complete." +fi +exit "$status"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.codex/hooks/verify.sh around lines 33 - 45, The verify hook masks failures by appending "|| true" to both "yarn build" and "yarn test" and always ending with "exit 0"; remove the "|| true" from those commands and instead capture their exit statuses (e.g., store $? after "yarn build" and "yarn test") so you can run restore_dist_worktree unconditionally and then exit with a non-zero code if either step failed; specifically update the commands in verify.sh (the "yarn build" and "yarn test" invocations and the final "exit 0") so failures propagate to the script caller while still ensuring restore_dist_worktree always runs.src/hooks/communities.ts-304-323 (1)
304-323:⚠️ Potential issue | 🟠 MajorIgnore stale address-resolution completions.
This lookup has no request/version guard. If
communityAddresschanges while an earlierresolveCommunityAddress()call is still in flight, the older promise can overwriteresolvedAddressandstatefor the new input.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/communities.ts` around lines 304 - 323, The async resolver closure started for resolveCommunityAddress lacks a freshness guard and can overwrite state when communityAddress changes; fix by capturing a local token (e.g. const requested = communityAddress or a generated requestId) before awaiting resolveCommunityAddress(communityAddress, chainProviders) and, after the await or in the catch, only call setState, setResolvedAddress, and setErrors if the current communityAddress (or requestId) still matches the captured token; also avoid pushing stale errors by checking the same guard before setErrors and before log.error so only the latest request updates state or logs.src/hooks/communities.ts-101-117 (1)
101-117:⚠️ Potential issue | 🟠 MajorDon't swallow stats fetch failures.
If
fetchCidorJSON.parsefails, this hook only logs and keeps returningstate: "fetching-ipfs"with emptyerror/errors. Callers can't stop loading or render the failure.Also applies to: 129-137
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/communities.ts` around lines 101 - 117, The hook currently swallows failures from account.plebbit.fetchCid and JSON.parse and only logs them, leaving callers stuck in "fetching-ipfs"; update the catch block inside the IIFE to call setCommunityStats with an error state/value (include the communityAddress and the caught error) so state reflects failure (e.g. setCommunityStats(communityAddress, { state: "error", error })) and ensure any partial fetchedCid is included in the error payload; do the same for the other similar block at lines ~129-137 so callers can stop loading and render an error.src/hooks/accounts/accounts.ts-145-145 (1)
145-145:⚠️ Potential issue | 🟠 MajorDon't merge owner communities from the active account here.
useAccountCommunities({ accountName })still callsuseListCommunities()with no account override, so this merge uses whichever account is active instead of the requested account.Also applies to: 226-234
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/accounts/accounts.ts` at line 145, The code is calling useListCommunities() without forwarding the requested account, causing ownerCommunityAddresses to be populated from the active account instead of the accountName parameter; update the calls inside useAccountCommunities (the ownerCommunityAddresses assignment and the other occurrences around the merge logic) to call useListCommunities({ accountName }) (or the hook's account override option) so the list is fetched for the requested account rather than the active account.src/hooks/communities.ts-86-89 (1)
86-89:⚠️ Potential issue | 🟠 MajorPass
accountNamethrough touseCommunity.
useCommunityStatsreadsaccountName, but it loads the community through the active account. With a non-active account,statsCidcan come from the wrong context or never load at all.Suggested fix
- const community = useCommunity({ communityAddress, onlyIfCached }); + const community = useCommunity({ communityAddress, accountName, onlyIfCached });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/communities.ts` around lines 86 - 89, useCommunity is currently called without the accountName so community may be loaded from the active account instead of the requested account; update the call to useCommunity in this file to pass accountName from options (e.g., call useCommunity with { communityAddress, onlyIfCached, accountName }) so communityStatsCid comes from the correct account context used by useCommunityStats.src/lib/utils/utils.ts-381-385 (1)
381-385:⚠️ Potential issue | 🟠 MajorCheck the
commentIsValid()results here.
commentIsValid()resolvesfalsefor invalid replies; it doesn't reject.Promise.all()will still fulfill, sorepliesAreValid()currently returnstrueeven when a nested reply failed validation.Suggested fix
- const promises = replies.map((reply) => - commentIsValid(reply, { validateReplies: false, blockCommunity: true }, plebbit), - ); - await Promise.all(promises); + const results = await Promise.all( + replies.map((reply) => + commentIsValid(reply, { validateReplies: false, blockCommunity: true }, plebbit), + ), + ); + if (results.some((isValid) => !isValid)) { + if (blockCommunity) { + communitiesWithInvalidComments[comment.communityAddress] = true; + } + return false; + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/utils/utils.ts` around lines 381 - 385, The repliesAreValid flow currently calls commentIsValid for each reply and awaits Promise.all(promises) but ignores returned booleans, so invalid replies (false) don't mark the check as failed; change the logic in repliesAreValid to await Promise.all(promises) into a results array, then check results.includes(false) (or use results.every(Boolean)) and return/throw accordingly so that any false from commentIsValid causes repliesAreValid to fail; reference the call to commentIsValid and the Promise.all(promises) usage to locate where to collect and evaluate results.src/stores/communities-pages/communities-pages-store.ts-98-102 (1)
98-102:⚠️ Potential issue | 🟠 MajorDon't log the full
accountobject here.These payloads include
account, and this codebase already treats signer/author fields as sensitive elsewhere because they can expose private key material. Sending the raw account into debug logs will leak secrets whenever tracing is enabled.Also applies to: 118-122, 153-159
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/stores/communities-pages/communities-pages-store.ts` around lines 98 - 102, The trace logs in communitiesPagesStore.addNextCommunityPageToStore (and the other similar trace calls at the indicated locations) are currently including the full account object which may contain sensitive signer/private-key data; update those log.trace calls to stop passing the entire account object and instead pass only non-sensitive identifiers (e.g., account.address or a masked ID) or remove the account field entirely so no raw account object is logged. Ensure you change each occurrence referenced (the call shown and the ones around lines 118-122 and 153-159) to use the safe identifier symbol rather than the full account payload.src/stores/communities-pages/communities-pages-store.ts-286-321 (1)
286-321:⚠️ Potential issue | 🟠 MajorScope the cached community instances by account, not just address.
fetchPageCommunities[communityAddress]reuses the firstaccount.plebbit.createCommunity()result for every later account. That mixes per-account client config/permissions for the same community, and the stale instance even survivesresetCommunitiesPagesStore(). Key this cache by account identity (orplebbitinstance) and clear it during reset.Also applies to: 386-399
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/stores/communities-pages/communities-pages-store.ts` around lines 286 - 321, fetchPageCommunities is currently keyed only by communityAddress so the first account’s plebbit.createCommunity() instance is reused across different accounts; change the cache to be scoped by account (for example use a composite key like `${account.id}:${communityAddress}` or key by the account.plebbit instance) and update fetchPage to look up/create using that account-scoped key (affecting fetchPageCommunities and any uses in fetchPage). Also ensure the cache is cleared in resetCommunitiesPagesStore (and the analogous cache usage at the other location noted) so per-account instances are removed on reset.src/stores/communities-pages/communities-pages-store.ts-264-309 (1)
264-309:⚠️ Potential issue | 🟠 MajorDon't write mod-queue client state into
community.posts.
fetchPage()is called for both"posts"and"modQueue", but this callback always mutatescommunity.posts.clients. Any mod-queue fetch will overwrite post-feed client state and the moderation path never gets its own client tree.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/stores/communities-pages/communities-pages-store.ts` around lines 264 - 309, The callback onCommunityPostsClientsStateChange always writes into community.posts.clients, so when fetchPage handles pageType "modQueue" it accidentally overwrites posts client state; create a separate handler (e.g., onCommunityModQueueClientsStateChange) that mirrors onCommunityPostsClientsStateChange but updates community.modQueue.clients (and community.modQueue[...] trees) and, inside fetchPage where utils.pageClientsOnStateChange is called, pass the appropriate handler based on pageType (use onCommunityPostsClientsStateChange for "posts" and the new onCommunityModQueueClientsStateChange for "modQueue") so each pageType keeps its own client tree.src/stores/accounts/accounts-actions.ts-428-433 (1)
428-433:⚠️ Potential issue | 🟠 MajorSerialize the account write behind subscribe/unsubscribe.
Both actions update state immediately and fire
accountsDatabase.addAccount(updatedAccount)without awaiting it. Two quick toggles can finish out of order and leave the persistedsubscriptionslist wrong after reload.Also applies to: 463-468
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/stores/accounts/accounts-actions.ts` around lines 428 - 433, The subscribe/unsubscribe flows update state immediately but call accountsDatabase.addAccount(updatedAccount) without awaiting, allowing concurrent toggles to persist out-of-order; serialize writes for a given account by chaining or awaiting previous write promises before calling accountsDatabase.addAccount. Implement a per-account write-queue (e.g., a map keyed by updatedAccount.id that stores the last write Promise) and when subscribing/unsubscribing (the code that constructs updatedAccount and calls accountsDatabase.addAccount), replace the fire-and-forget with: chain the new addAccount call onto the existing promise for that account (prevPromise.then(() => accountsDatabase.addAccount(updatedAccount))).catch(log) and then replace the map entry with the new promise; keep immediate UI state update via accountsStore.setState but ensure persistence calls for the same account are executed in order (apply same fix to both the block around updatedAccount/accountsDatabase.addAccount and the similar block at lines 463-468).src/stores/accounts/accounts-actions.ts-1187-1189 (1)
1187-1189:⚠️ Potential issue | 🟠 MajorGuard
account.plebbit.communitiesbefore calling.includes().
useListCommunities()insrc/hooks/communities.tsalready treatsplebbit.communitiesas optional, so this direct.includes()can throw when the local communities list has not been initialized yet.🩹 Minimal guard
-const localCommunityAddresses = account.plebbit.communities; +const localCommunityAddresses = Array.isArray(account.plebbit.communities) + ? account.plebbit.communities + : [];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/stores/accounts/accounts-actions.ts` around lines 1187 - 1189, Guard against a missing or non-array plebbit.communities before calling .includes(): ensure the code that sets localCommunityAddresses (or the subsequent check) verifies account.plebbit?.communities is an array (e.g. Array.isArray) and only calls .includes(communityAddress) when that check passes; otherwise treat it as an empty list so the "account is owner" branch is skipped safely. Target the variables/members localCommunityAddresses, account.plebbit.communities and the conditional that uses .includes(communityAddress) in accounts-actions.ts.src/hooks/accounts/accounts.test.ts-2501-2506 (1)
2501-2506:⚠️ Potential issue | 🟠 MajorRemove the unstable
title === undefinedassertion from this delete test.This is checking the same transient state that the previous test already calls out as unreliable in CI. Since
Community.update()can populatetitleshortly after creation, this makes the delete-path test flaky even when deletion is correct.Proposed fix
// can useCommunity rendered.rerender(createdCommunityAddress); await waitFor(() => rendered.result.current.community); expect(rendered.result.current.community.address).toBe(createdCommunityAddress); - expect(rendered.result.current.community.title).toBe(undefined); // delete it await act(async () => { await rendered.result.current.deleteCommunity(createdCommunityAddress); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/accounts/accounts.test.ts` around lines 2501 - 2506, The test is making a flaky assertion on transient community.title; remove the unstable check. In the test that calls rendered.rerender(createdCommunityAddress) and waits for rendered.result.current.community, delete the line expect(rendered.result.current.community.title).toBe(undefined); (or replace it with a stable assertion such as asserting the property exists or leaving it unasserted). Keep the address assertion (expect(...community.address).toBe(createdCommunityAddress)) and do not add timing-dependent checks tied to Community.update().
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 41b01c07-25b4-4ad2-b910-4bbdae6dc31c
📒 Files selected for processing (148)
.codex/agents/code-quality.md.codex/agents/plan-implementer.md.codex/agents/react-patterns-enforcer.md.codex/hooks.json.codex/hooks/format.sh.codex/hooks/sync-git-branches.sh.codex/hooks/verify.sh.codex/hooks/yarn-install.sh.codex/skills/commit-format/SKILL.md.codex/skills/commit/SKILL.md.codex/skills/context7/SKILL.md.codex/skills/deslop/SKILL.md.codex/skills/find-skills/SKILL.md.codex/skills/fix-merge-conflicts/SKILL.md.codex/skills/implement-plan/SKILL.md.codex/skills/issue-format/SKILL.md.codex/skills/make-closed-issue/SKILL.md.codex/skills/readme/SKILL.md.codex/skills/refactor-pass/SKILL.md.codex/skills/release-description/SKILL.md.codex/skills/review-and-merge-pr/SKILL.md.codex/skills/you-might-not-need-an-effect/SKILL.md.cursor/agents/code-quality.md.cursor/agents/plan-implementer.md.cursor/agents/react-patterns-enforcer.md.cursor/hooks.json.cursor/hooks/format.sh.cursor/hooks/sync-git-branches.sh.cursor/hooks/verify.sh.cursor/hooks/yarn-install.sh.cursor/skills/commit-format/SKILL.md.cursor/skills/commit/SKILL.md.cursor/skills/context7/SKILL.md.cursor/skills/deslop/SKILL.md.cursor/skills/find-skills/SKILL.md.cursor/skills/fix-merge-conflicts/SKILL.md.cursor/skills/implement-plan/SKILL.md.cursor/skills/issue-format/SKILL.md.cursor/skills/make-closed-issue/SKILL.md.cursor/skills/readme/SKILL.md.cursor/skills/refactor-pass/SKILL.md.cursor/skills/release-description/SKILL.md.cursor/skills/review-and-merge-pr/SKILL.md.cursor/skills/you-might-not-need-an-effect/SKILL.md.gitignoreAGENTS.mdREADME.mddocs/TODO.mddocs/algorithms.mddocs/clients.mddocs/mock-content.mddocs/schema.mddocs/testing.mdscripts/coverage-gaps.mjssrc/hooks/accounts/accounts.test.tssrc/hooks/accounts/accounts.tssrc/hooks/accounts/index.tssrc/hooks/accounts/utils.test.tssrc/hooks/accounts/utils.tssrc/hooks/actions/actions.test.tssrc/hooks/actions/actions.tssrc/hooks/actions/index.tssrc/hooks/authors/author-avatars.tssrc/hooks/authors/authors.test.tssrc/hooks/authors/authors.tssrc/hooks/authors/index.tssrc/hooks/authors/utils.tssrc/hooks/comments.test.tssrc/hooks/comments.tssrc/hooks/communities.test.tssrc/hooks/communities.tssrc/hooks/feeds/feeds.test.tssrc/hooks/feeds/feeds.tssrc/hooks/feeds/index.tssrc/hooks/replies.test.tssrc/hooks/states.test.tssrc/hooks/states.tssrc/hooks/subplebbits.tssrc/index.tssrc/lib/chain/chain.test.tssrc/lib/chain/index.tssrc/lib/community-address.test.tssrc/lib/community-address.tssrc/lib/debug-utils.tssrc/lib/localforage-lru/localforage-lru.test.tssrc/lib/localforage-lru/localforage-lru.tssrc/lib/plebbit-js/fixtures/markdown-example.tssrc/lib/plebbit-js/plebbit-js-mock-content.donttest.tssrc/lib/plebbit-js/plebbit-js-mock-content.tssrc/lib/plebbit-js/plebbit-js-mock.test.tssrc/lib/plebbit-js/plebbit-js-mock.tssrc/lib/subplebbit-address.test.tssrc/lib/test-utils.tssrc/lib/utils/index.tssrc/lib/utils/utils.test.tssrc/lib/utils/utils.tssrc/lib/validator.tssrc/stores/accounts/account-generator.tssrc/stores/accounts/accounts-actions-internal.test.tssrc/stores/accounts/accounts-actions-internal.tssrc/stores/accounts/accounts-actions.test.tssrc/stores/accounts/accounts-actions.tssrc/stores/accounts/accounts-database.test.tssrc/stores/accounts/accounts-database.tssrc/stores/accounts/accounts-store.test.tssrc/stores/accounts/index.tssrc/stores/accounts/utils.test.tssrc/stores/accounts/utils.tssrc/stores/authors-comments/authors-comments-store.test.tssrc/stores/authors-comments/authors-comments-store.tssrc/stores/authors-comments/index.tssrc/stores/comments/index.tssrc/stores/communities-pages/communities-pages-store.test.tssrc/stores/communities-pages/communities-pages-store.tssrc/stores/communities-pages/index.tssrc/stores/communities/communities-store.test.tssrc/stores/communities/communities-store.tssrc/stores/communities/index.tssrc/stores/feeds/feed-sorter.test.tssrc/stores/feeds/feeds-store.test.tssrc/stores/feeds/feeds-store.tssrc/stores/feeds/index.tssrc/stores/feeds/utils.test.tssrc/stores/feeds/utils.tssrc/stores/replies-pages/index.tssrc/stores/replies-pages/replies-pages-store.test.tssrc/stores/replies-pages/replies-pages-store.tssrc/stores/replies-pages/utils.test.tssrc/stores/replies/index.tssrc/stores/replies/replies-store.test.tssrc/stores/replies/replies-store.tssrc/stores/replies/utils.test.tssrc/stores/replies/utils.tssrc/stores/subplebbits-pages/index.tssrc/stores/subplebbits-pages/subplebbits-pages-store.test.tssrc/stores/subplebbits-pages/subplebbits-pages-store.tssrc/stores/subplebbits/index.tssrc/stores/subplebbits/subplebbits-store.test.tssrc/stores/subplebbits/subplebbits-store.tssrc/types.tstest/browser-e2e/accounts.test.jstest/browser-e2e/communities.test.jstest/browser-e2e/feeds.test.jstest/browser-plebbit-js-mock-content/plebbit-js-mock-content.test.jstest/browser-plebbit-js-mock/accounts.test.jstest/browser-plebbit-js-mock/communities.test.jstest/browser-plebbit-js-mock/feeds.test.jstest/test-server/index.js
💤 Files with no reviewable changes (2)
- src/lib/subplebbit-address.test.ts
- src/hooks/subplebbits.ts
| --- | ||
| 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) |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
What are the current Context7 API endpoints and how do they work?
💡 Result:
Context7 REST API (documentation lookup)
Base URL: https://context7.com (endpoints below are under /api/v2) [1]
1) Search libraries
Endpoint: GET /api/v2/libs/search [1]
Auth: Authorization: Bearer <CONTEXT7_API_KEY> [1]
Query params:
libraryName(required) — e.g.react,nextjs[1]query(required) — what you’re trying to do; used for relevance ranking [1]
What it returns: an array of matching libraries with an id like /facebook/react plus metadata (scores, versions, snippet counts). [1]
2) Fetch documentation “context” (snippets) for a library
Endpoint: GET /api/v2/context [1]
Auth: Authorization: Bearer <CONTEXT7_API_KEY> [1]
Query params:
libraryId(required) — from search results (e.g./facebook/react) [1]query(required) — what you want to know/do [1]type(optional) —json(default) ortxt(plain text ready to paste into an LLM prompt) [1]
What it returns:
json: array of snippets withtitle,content,source[1]txt: a single plain-text blob suitable for prompting [1]
Version pinning: you can request version-specific context by using a versioned libraryId (example shown in docs: /vercel/next.js/v15.1.8). [1]
Typical workflow: call Search → pick best libraryId → call Context with your question. [1]
Context7 hosted MCP server (for AI tools that speak MCP)
MCP endpoint (API key header approach): https://mcp.context7.com/mcp (used by MCP clients like Cursor/Claude Code). [2]
Clients commonly pass the key as a header named CONTEXT7_API_KEY. [2]
OAuth-enabled MCP endpoint: https://mcp.context7.com/mcp/oauth [3]
How it works at a high level: connect to the OAuth endpoint → browser sign-in → redirect back → client handles token refresh. [3]
Sources
[1] Context7 “API Guide” (REST endpoints, params, formats, errors) (context7.com)
[2] Context7 “Installation” (hosted MCP server URL + headers) (context7.com)
[3] Context7 “OAuth” (OAuth MCP endpoint and flow) (context7.com)
Citations:
- 1: https://context7.com/docs/api-guide
- 2: https://context7.com/docs/installation?utm_source=openai
- 3: https://context7.com/docs/howto/oauth?utm_source=openai
🌐 Web query:
Context7 API library ID format and examples
💡 Result:
Context7 “library ID” format
- Base format:
/<owner>/<repo>(a leading/, then GitHub-styleowner/repo). Example:/facebook/react. [1] - Pinned version format (optional):
/<owner>/<repo>/<version>where version is typically a tag likev15.1.8. Example:/vercel/next.js/v15.1.8. [1]
How to find the correct library ID
- Call Search Library:
GET /api/v2/libs/searchand read theidfield from the results (thatidis the library ID you pass to the context endpoint). Example result showsid: "/facebook/react". [1]
How it’s used (examples)
- Fetch docs context (default version):
GET /api/v2/context?libraryId=/facebook/react&query=hooks[1]
- Fetch docs context (pinned version):
GET /api/v2/context?libraryId=/vercel/next.js/v15.1.8&query=app%20router[1]
Fix API authentication requirement in the Tips section.
The documentation incorrectly states "No API key is required for basic usage (rate-limited)" on line 65. According to the official Context7 API documentation, both the search and context endpoints require authentication via Authorization: Bearer <CONTEXT7_API_KEY>. Update the Tips section to clarify that an API key is required for all API calls, and optionally note any current rate limits for authenticated requests.
The API endpoints (/api/v2/libs/search and /api/v2/context), parameter names, and example library ID formats are all accurate.
🧰 Tools
🪛 LanguageTool
[grammar] ~6-~6: Ensure spelling is correct
Context: ...may have changed since training. --- # Context7 ## Overview This skill enables retrieval o...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.cursor/skills/context7/SKILL.md around lines 1 - 67, Update the Tips
section to remove the incorrect statement "No API key is required for basic
usage (rate-limited)" and instead state that both Context7 endpoints
(/api/v2/libs/search and /api/v2/context) require authentication via the
Authorization: Bearer <CONTEXT7_API_KEY> header; optionally add a brief note
about rate limits for authenticated requests and keep the examples, parameter
names, and library ID formats unchanged (edit the Tips paragraph that currently
mentions API key requirements).
| const deleteCaches = () => | ||
| Promise.all([ | ||
| localForageLru.createInstance({ name: "plebbitReactHooks-subplebbits" }).clear(), | ||
| localForageLru.createInstance({ name: "plebbitReactHooks-communities" }).clear(), | ||
| localForageLru.createInstance({ name: "plebbitReactHooks-comments" }).clear(), | ||
| localForageLru.createInstance({ name: "plebbitReactHooks-subplebbitsPages" }).clear(), | ||
| localForageLru.createInstance({ name: "plebbitReactHooks-communitiesPages" }).clear(), | ||
| ]); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check if dist files exist and whether they contain stale store names.
# Expected: If dist is tracked, it should contain "communities" not "subplebbits" after rebuild.
echo "=== Checking if dist/ is tracked in git ==="
git ls-files --error-unmatch dist/lib/debug-utils.js 2>/dev/null && echo "dist/lib/debug-utils.js IS tracked" || echo "dist/lib/debug-utils.js is NOT tracked"
echo ""
echo "=== Checking .gitignore for dist/ ==="
rg -n "^dist" .gitignore || echo "No dist entry in .gitignore"
echo ""
echo "=== Current content of dist/lib/debug-utils.js (if exists) ==="
if [ -f dist/lib/debug-utils.js ]; then
cat dist/lib/debug-utils.js
else
echo "File does not exist"
fiRepository: bitsocialnet/bitsocial-react-hooks
Length of output: 1305
Rebuild dist files before merging—dist file contains stale store names.
The compiled dist/lib/debug-utils.js references the old store names (plebbitReactHooks-subplebbits, plebbitReactHooks-subplebbitsPages), while the source has been updated to plebbitReactHooks-communities and plebbitReactHooks-communitiesPages. This mismatch will cause runtime errors in the published package when it attempts to clear non-existent stores.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/debug-utils.ts` around lines 13 - 18, The compiled dist output is
stale: update the build so dist/lib/debug-utils.js matches the source change in
deleteCaches (which now uses localForageLru.createInstance with names
"plebbitReactHooks-communities" and "plebbitReactHooks-communitiesPages");
rebuild the project (run the repository's build script) to regenerate dist
artifacts, verify dist/lib/debug-utils.js contains the new store names instead
of the old "plebbitReactHooks-subplebbits" and
"plebbitReactHooks-subplebbitsPages", and include the regenerated dist files in
the PR before merging.
|
Addressed the valid CI/review findings in commit 682c0fe. Fixed the stop-hook cleanup logic so it only restores/cleans |
682c0fe to
aa5cb91
Compare
|
Restacked this PR on top of master in aa5cb91 so it only contains the AI workflow and contributor-tooling changes that are actually unique to PR 26. Kept in this PR:
Dropped from this PR:
Local verification:
|
| # 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 |
There was a problem hiding this comment.
Format hook invokes wrong formatter tool
Medium Severity
The afterFileEdit hook calls npx prettier --config config/prettier.config.js but the project's actual formatter is oxfmt (the yarn prettier script in package.json maps to oxfmt, not prettier). There's no evidence prettier is a project dependency or that config/prettier.config.js exists. This hook either silently no-ops (due to || true) or applies formatting rules inconsistent with the rest of the codebase, defeating its purpose.
Additional Locations (1)
| ;; | ||
| esac | ||
|
|
||
| exit 0 |
There was a problem hiding this comment.
Massive file duplication across .codex and .cursor
Low Severity
Every file under .codex/ (agents, hooks, skills, hooks.json) is duplicated byte-for-byte under .cursor/. This is 14+ skill files, 3 agent files, 4 shell scripts, and 2 JSON configs — all maintained in two places. The AGENTS.md rule to "keep equivalent workflow files aligned" will be very hard to enforce manually and increases the risk of the copies silently drifting apart. Symlinks or a shared directory with toolchain-specific entry points would eliminate the duplication.
Additional Locations (2)
aa5cb91 to
9a7a16d
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
| fi | ||
|
|
||
| return 0 | ||
| } |
There was a problem hiding this comment.
git cherry failure silently marks branch as integrated
Low Severity
branch_is_integrated falls through to return 0 (meaning "integrated") when git cherry fails and produces empty output via || true. An empty cherry_output causes the grep '^+' check to find no match, so the function reports the branch as fully integrated. Combined with the deletion logic downstream, this could cause a temporary branch to be incorrectly deleted when the default branch ref is missing or git cherry otherwise errors out.


Ports the 5chan branch-and-PR workflow into bitsocial-react-hooks, including tracked
.codexand.cursorfiles, mirrored hook/skill updates, and the newreview-and-merge-prflow. Updates.gitignoreto allowlist the AI workflow subtrees instead of ignoring them. Fixes stale repo-name references so the committed tooling points atbitsocialnet/bitsocial-react-hooksand project 6.Closes #25
Note
Medium Risk
Primarily docs/tooling, but it changes
.gitignorebehavior and introduces hook scripts (including git branch cleanup) that could affect developer workflows if enabled or misconfigured.Overview
Adds repo-managed AI contributor tooling under
.codex/and.cursor/, including mirrored agent definitions (quality check runner, plan implementer, React patterns enforcer), reusable “skills” playbooks (commit/issue formatting, merge conflict resolution, plan execution, PR review+merge), and hook scripts for auto-formatting, runningyarn installonpackage.jsonedits, syncing/pruning git branches, and runningyarn build/yarn teston stop.Updates
.gitignoreto allowlist these.codex/.cursorsubtrees (instead of ignoring them entirely) and extendsAGENTS.mdwith explicit PR-first git workflow rules plus guidance to keep Codex/Cursor workflow files aligned and tracked.Written by Cursor Bugbot for commit 9a7a16d. This will update automatically on new commits. Configure here.
Summary by CodeRabbit