Compare commits

...
Author SHA1 Message Date
Patrick Erichsen a128c101ad chore: release clawhub cli 0.23.0 (#2812) 2026-06-23 13:45:20 -07:00
Patrick Erichsen dfde400fcf fix(seed): populate public corpus plugin metadata 2026-06-23 13:14:30 -07:00
Patrick Erichsen 071c43da43 fix: shrink security dataset export batches 2026-06-23 12:52:50 -07:00
Vyctor H. Brzezowski 7cc6b22176 feat(web): rename publishers browse to Creators (#2799)
* feat(web): rename publishers browse to Creators

Align /publishers page heading and filter tabs with clearer creator-focused copy, move Official after All, and show full org labels on desktop only.

* test(e2e): align local-auth flows with owner-qualified routes

Update playwright local-auth helpers and specs for canonical profile, skill, and plugin URLs after the main branch routing migration.

* fix(test): export plugin validation href helper for e2e typecheck

* chore: format local-auth helpers import

* chore: retrigger CI after flaky local-auth shards

* feat(web): move publishers browse to /creators route

Keep /publishers and /users as legacy redirects with search preserved, and align registry copy, tests, and reserved slugs with the new path.

* fix(web): drop unused OpenClaw slug re-export after schema move

* chore: format openClawExtensionSlugs re-export cleanup
2026-06-23 12:24:25 -07:00
Vyctor H. Brzezowski b927db6340 fix(web): show only downloads in plugin browse listings (#2797)
* fix(web): show only downloads in plugin browse listings

Plugin list rows and cards on /plugins and the home Plugins tab no longer surface star counts.

* docs: add UI proof screenshots for plugin listing change
2026-06-23 12:24:11 -07:00
Patrick Erichsen e018269af6 fix: make security dataset workflow dispatchable (#2802) 2026-06-23 11:58:22 -07:00
Vincent Koc 9b352d7f99 fix(routing): protect publisher aliases from plugin collisions 2026-06-23 17:56:48 +08:00
Vincent Koc ab862d49f1 fix(catalog): include verified personal skill publishers 2026-06-23 17:37:18 +08:00
Vincent Koc f4f6f34542 fix(catalog): paginate verified skill feed publication 2026-06-23 17:18:43 +08:00
Vincent Koc 6aab4f9437 feat(catalog): publish verified organization skills feed 2026-06-23 17:04:26 +08:00
Vincent Koc 94f2f532c3 fix(catalog): version and harden hosted plugin feed (#2806) 2026-06-23 16:11:40 +08:00
Jesse Merhi 2cf2636ab8 chore: remove local staged secret hook
Remove the repo-managed local staged-secret hook and scanner now that GitHub secret scanning push protection is the primary secret-blocking control.
2026-06-23 18:08:18 +10:00
Vincent Koc 72c5cdd864 feat(catalog): publish hosted OpenClaw plugin feed (#2805)
* feat(schema): define hosted catalog feed contract

* feat(catalog): publish cached hosted plugin feed

* feat(catalog): add feed publication and edge delivery

* fix(catalog): recheck live official publisher state

* fix(workflow): require main for catalog publication

* fix(catalog): harden publication inputs

* docs(catalog): document consumer rollout boundary
2026-06-23 15:41:24 +08:00
Patrick Erichsen 775146ff6f feat: publish security dataset from live export 2026-06-22 22:56:33 -07:00
Patrick Erichsen e82f0704c8 Publish security dataset from live Convex export (#2800)
* feat(cli): restore skill sync command

* feat: publish security dataset from live export
2026-06-22 22:40:09 -07:00
Patrick Erichsen 4a78ca3a06 fix(web): keep staff skill pages hydrated (#2798)
* fix(web): keep staff skill pages hydrated

* test(e2e): align local auth routes with owner paths
2026-06-22 22:35:57 -07:00
Patrick Erichsen 5c4b7d45df Revert "docs: document security dataset export workflow"
This reverts commit 8841ac7771.
2026-06-22 22:33:15 -07:00
Patrick Erichsen 8841ac7771 docs: document security dataset export workflow 2026-06-22 22:29:47 -07:00
Vyctor H. Brzezowski a5e41320c8 fix(web): recall publishers outside browse window in search (#2790)
* fix: recall publishers outside top install window in search

Publisher search only scanned the top 500 by installs and dropped empty
profiles, so handles like vincentkoc never appeared even when the user
profile was public.

* test: cover publisher search recall for low-install handles

Add regression coverage for publishers with published skills that fall
outside the top install browse window, matching the vyctorbrzezowski case.

* test: align publisher search mocks with downloads browse indexes

* chore: add production publisher search proof for PR 2790

* test: drop invalid publisher list stats assertion

Remove stats.skills expectation from listPublicPage search recall test;
public list items only expose downloads and installs counts.

* chore: retrigger CI after delete-account flake
2026-06-22 21:51:40 -07:00
Patrick Erichsen 917fb3fbe9 test: stabilize production menu smoke
Stabilizes production menu smoke by clicking exact header nav links and avoiding mobile drawer transition races between SPA navigations.\n\nVerification:\n- PLAYWRIGHT_BASE_URL=https://clawhub.ai bunx playwright test --workers=1 --project=mobile-chrome e2e/menu-smoke.pw.test.ts -g "header menu routes render"\n- PLAYWRIGHT_BASE_URL=https://clawhub.ai bunx playwright test --workers=1 e2e/menu-smoke.pw.test.ts e2e/publish-entry-workflows.pw.test.ts e2e/upload-auth-smoke.pw.test.ts\n- git diff --check
2026-06-22 21:45:42 -07:00
Patrick Erichsen b6acfb4fd7 test: update prod smoke skill canonical route
Updates production HTTP smoke to expect the owner-qualified skill canonical URL.\n\nVerification:\n- bun run test:e2e:prod-http\n- git diff --check
2026-06-22 21:39:10 -07:00
Patrick Erichsen 55fe4a8563 feat: add owner-qualified catalog routes
Adds canonical owner-qualified publisher, skill, and plugin routes while preserving legacy redirects.\n\nIncludes API, CLI, docs, and user-facing copy updates for /<owner>/skills/<slug> and /<owner>/plugins/<slug>.\n\nMerged by request before the local-auth matrix was green; static, unit, packages, types-build, e2e-http, and playwright-smoke were green on a1328b8.
2026-06-22 21:35:56 -07:00
Vincent Koc d4205c8a7e fix(ci): report testbox hydration failures (#2787) 2026-06-23 12:07:47 +08:00
Vyctor H. Brzezowski 8035a16024 fix(schema): expand reserved catalog topic slugs (#2791)
Block additional trust, curation, brand, and channel topic labels that
publishers could use to imply platform endorsement.
2026-06-22 21:01:51 -07:00
Patrick Erichsen fb7b73b86a feat: reclaim deleted org handles
Adds an admin-only deleted-org handle reclaim path and hard-delete cleanup for empty deleted org tombstones. Also makes local-auth publish flows resilient to cold local Convex startup timeouts.
2026-06-22 20:38:14 -07:00
Nancy 92e055f8f1 fix: align home app card icons (#2788) 2026-06-22 19:50:30 -07:00
Vyctor H. BrzezowskiandCursor 0dafef6c8b feat(web): polish skill and plugin detail pages (#2780)
* fix: polish skill detail hero metadata

* fix: improve skill readme presentation

* chore: outline skill detail structure

* fix: narrow detail page container

* fix: compact skill sidebar on detail pages

* fix: use body font for install switcher

* fix: align plugin detail sidebar

* fix: shorten skill readme preview

* fix: soften related skills heading

* fix: remove duplicate stars metadata

* fix: remove related skill hover underline

* chore: remove detail debug outlines

* fix: align hero with content column

* fix: rename summary disclosure action

* fix: wrap tab content in contrast panel

* fix: restore full width hero layout

* fix: refine skill detail surfaces

* fix: soften skill readme body copy

* fix: hide skill detail breadcrumbs

* fix: place skill taxonomy above title

* fix: reduce skill detail title size

* fix: add skill hero top spacing

* fix: standardize skill markdown formatting

* fix: refine related skills navigation

* fix: align plugin detail hero with skills

* fix: increase hero taxonomy spacing

* fix: mark official plugin owners

* fix: tune detail sidebar labels

* fix: restyle install tab switcher

* fix: add subtle skill detail wash

* fix: horizontalize skill versions panel

* fix: animate install tab switcher

* fix: align plugin versions layout

* fix: improve tab markdown surface contrast

* fix: separate detail categories with commas

* fix: remove detail tab underline bars

* fix: bleed skill wash behind header

* fix: polish detail versions changelog

* fix: add file tree to skill files view

* fix: anchor skill wash to page top

* fix: restore plugin version download actions

* fix: align detail sidebar top spacing

* fix: restore active detail tab bar

* fix: collapse detail version changelogs

* fix: clarify version changelog toggles

* fix: tune detail tab and install polish

* fix: polish markdown code blocks

* fix: refine markdown code wrap control

* docs: capture detail polish direction

* fix: refine release history layout

* feat: simplify detail file navigation

* fix: align detail hero sidebar patterns

* fix: unify plugin and skill detail polish

* fix: refine detail hero and release rows

* fix: move related skills below detail content

* fix: align detail hero title with main content column

Keep the hero wash full width while constraining taxonomy, title, and
summary to the same grid column as install and tab content on desktop.

* fix: increase star count badge font to 14px

Make the sidebar star action count easier to read on detail pages.

* fix: align shiki code block surfaces with detail markdown

Override Shiki's inline pre background so fenced blocks use the shared
markdown-code-block surface on skill and plugin README tabs.

* fix: remove code wrap toggle blur flicker

Drop the wrap-state blur reveal so toggling nowrap/wrap keeps the code
DOM stable without flashing highlighted tokens.

* fix: contain detail markdown overflow in tab bodies

Keep README and SKILL surfaces clipped to the tab column while preserving
horizontal scroll only inside code blocks and tables.

* fix: use neutral hover border on detail sidebar actions

Override the accent outline hover on Star, Share, and Download sidebar
buttons so detail pages keep a quieter action treatment.

* fix: tighten release row checks and download actions

Keep scan badges on one horizontal row, left-align package actions with
the column header, and show an icon-only download control.

* fix: collapse long plugin README previews like SKILL.md

Reuse the skill readme preview limiter with Read more/Show less on plugin
README.md tabs so long documentation stays scannable by default.

* fix: match activity metric info icon to security audit

Reuse the quiet sidebar info button styling so download labels no longer
show a circular hover treatment on the help icon.

* chore: remove unused activity metric info button styles

* fix: use neutral colors for download trend sparklines

Keep sidebar activity graphs muted with ink-soft tones instead of accent
red on skill and plugin detail pages.

* fix(ui): style related skills category link as outline button

Give the compact hero "More in …" footer full-width outline button affordance with neutral hover, matching sidebar secondary actions.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): compact skill/plugin detail hero on mobile

Tighten vertical rhythm below 1100px: side-by-side Star/Share, smaller
action buttons, denser metadata rows, shorter download sparkline, and
reduced gaps between hero, sidebar, and install sections.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): reduce sidebar action count font to 13px

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): reduce sidebar action count font to 13px

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): shrink sidebar star count badge height

Lower the action-count pill so Star and Share buttons align at the same height.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): restore markdown ordered and bullet list markers

Tailwind preflight strips list-style from ol/ul; re-apply disc and decimal
markers inside .markdown so SKILL.md and plugin README numbering renders.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: add download period tabs to detail sidebar

Replace the static 30-day downloads row with All time, 30d, and 7d tabs
that update the sparkline and total on skill and plugin detail pages.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): polish audit sidebar, badges, and chart theme tokens

Make creator badges fully clickable, collapse Latest audit to one inline row,
and tune download sparkline colors per theme with softer blue tones.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: make creator user badges fully clickable

Use fallbackHandle for profile links, pass plugin ownerHandle when the
owner record omits it, and add sidebar hover affordance on the whole badge.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): tighten audit sidebar and related skills footer

Center the category footer action, remove the secondary-actions negative
margin hack, and keep Latest audit on one compact inline row.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): refine downloads tabs, report row, and related summaries

Move download period tabs inline with the Downloads label using listing-style
underline tabs, drop the Report block top border, and cap related skill
descriptions at 80 characters.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: polish skill and plugin detail pages

* feat: add diff viewer skeleton

* fix: retain diff skeleton while versions load

* feat: restructure skill card review layout

* feat: add category icons to related skills

* fix: keep primary skill file first

* fix: keep skill card context expanded

* fix: refine skill card overview and risk contrast

* fix: show stars in related skill rows

* fix: refine install requirement tabs

* fix: join skill card risk rail

* fix: align plugin versions panel behavior

* fix: polish plugin repository and requirement panels

* fix: stabilize detail page skeleton layouts

* fix: resolve detail hydration gaps and finish polish pass

Keep mobile/desktop detail markup stable for SSR hydration, sync Shiki
theme selection with useSyncExternalStore, and complete tabpanel ARIA for
Files and Versions. Also lands remaining plugin categorize, install, and
metadata dialog polish from the detail-page iteration.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: stabilize detail page CI contracts

* feat(web): polish detail install shell and checkpoint branch WIP

Add subtle terminal $ prompt to skill/plugin install commands with
vertical alignment and descender-safe line height. Bundle remaining
detail-page polish, dialog tweaks, and local PR proof artifacts as a
restore point before further agent work.

* fix(web): polish plugin sidebar download and detail hero alignment

Move plugin download inline with the downloads count when no activity graph
is shown, and to the sidebar footer when a graph is present. Align skill/plugin
hero summary rows, compact management toolbar actions, and plugin mobile
About/Stats tabs. Remove accidental local proof artifacts from the branch.

* fix(web): stabilize detail page CI

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-22 19:32:12 -07:00
Patrick Erichsen bbe887faac feat(cli): restore skill sync command
Restores the ClawHub CLI sync command as a one-way skill publish workflow without install/download telemetry updates.
2026-06-22 18:45:59 -07:00
Patrick Erichsen 51d42badcf fix: route banned users to account page (#2785) 2026-06-22 18:07:27 -07:00
Vincent Koc b7a7555b19 fix(test): model unavailable publish flash storage 2026-06-23 08:03:42 +08:00
37eac63dc8 Fix official-first category plugin pagination (#2775)
* fix: avoid double pagination in official category browse

* fix: remove stale prod smoke retry assignment

* fix: preserve community fill in official category browse

* test: extend malicious skill ban e2e timeout

* test: tolerate slug check teardown timeout

* test: cover plugin catalog journeys

* fix: preserve official-first plugin filters

---------

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
2026-06-22 14:31:13 -06:00
Vyctor H. BrzezowskiandPatrick Erichsen 2993f6ccb7 polish: refine homepage hero (#2765)
* polish: refine homepage hero

* fix(web): preserve accessible hero trigger

* test: align header typeahead metadata assertion

---------

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-06-22 13:28:55 -07:00
Vyctor H. Brzezowski 9ea97b3c58 fix(web): polish browse listing pages (#2767)
* feat: polish browse listing pages

* fix(web): refine mobile publisher rows

* test(web): preserve browse contracts after rebase
2026-06-22 13:11:51 -07:00
Vyctor H. Brzezowski 6dfe780a29 build: generate llms.txt from canonical metadata (#2483)
* docs: add llms.txt

* docs: avoid dead llms skill examples

* refactor: centralize public registry routes

* refactor: share canonical ClawHub links

* feat: generate llms.txt from canonical metadata

* build: generate llms.txt for dev and production

* test: follow canonical docs navigation link
2026-06-22 13:04:07 -07:00
Vyctor H. Brzezowski 519f56301d perf: cache homepage listing tabs (#2764) 2026-06-22 13:03:11 -07:00
Patrick Erichsen 1379d90ddf fix: hide plugin scopes in display labels
Hide scoped plugin package prefixes in visible search and breadcrumb labels while preserving canonical scoped routes.
2026-06-22 13:01:55 -07:00
vyctorbrzezowski 6bd73e33bb feat: unify header search results 2026-06-22 13:01:11 -07:00
Patrick Erichsen e03432d4d8 fix: drop stale prod smoke retry assignment 2026-06-22 12:42:50 -07:00
Patrick Erichsen c1bb8d94cd fix: remove activity metric tooltip 2026-06-22 12:42:50 -07:00
Momo 6046bf9eda fix: honor owner scope in skill install resolver
Fixes #2766
2026-06-22 20:45:19 +08:00
Vincent Koc bf727b6a32 fix(codeql): clean clawhub quality findings 2026-06-22 18:18:21 +08:00
Jesse Merhi 39d03a577f Fix filtered plugin catalog pagination (#2771)
* fix: resume filtered plugin package pagination

* test: wait for copied publish dialog state

* ci: retry local convex module prep

* test: stabilize banned account local auth flow
2026-06-22 17:07:30 +10:00
Jason (Json) c70755d25d Fix Official plugin download listing (#2772)
Use a selective family, Official status, and downloads index for Official plugin listings while preserving legacy pagination cursor compatibility.
2026-06-21 23:26:13 -06:00
Vincent Koc 503a2bf022 fix(ci): reduce ClawHub worker fanout (#2770) 2026-06-22 12:41:56 +08:00
Jesse Merhi 379c1871f4 Add skill and plugin download activity graphs
Restore downloads as the public activity metric and add deferred 30-day download graphs for skills and plugins.
2026-06-22 11:59:30 +10:00
Jesse Merhi a86c48ce3b Tune publisher abuse pressure labels
Retune publisher-abuse aggregate scoring to v4, keep this path flag-only for rollout, clear stale aggregate nominations, and remove the direct publisher-abuse ban UI.
2026-06-22 10:53:33 +10:00
Patrick Erichsen 6f28659e7b fix: backfill stored plugin manifests (#2763) 2026-06-19 19:56:48 -07:00
Patrick Erichsen 3836b7643f fix: harden plugin manifest backfill
Supports direct config-map plugin manifests and adds a cursor-based production backfill page action.
2026-06-19 19:34:50 -07:00
Patrick Erichsen a7b7df7129 feat: backfill plugin manifest summaries
Adds a production-safe backfill for latest active plugin package releases so existing plugin pages can render typed manifest capability tabs without republishing.
2026-06-19 18:55:37 -07:00
Patrick Erichsen 08a95f3c74 feat: render plugin manifest capabilities (#2759) 2026-06-19 18:30:06 -07:00
Vyctor H. Brzezowski 730063b575 fix(web): hide pending and suspicious items from New (#2760) 2026-06-19 18:15:40 -07:00
Vyctor H. BrzezowskiandCursor ea0125d87e Refresh homepage experience (#2756)
* style: add Mobbin-inspired home listing toolbar and grid

Introduce a catalog section below the hero with Skills/Plugins toggle,
sort tabs, filter affordance, and a card grid fed by public list APIs.

Co-authored-by: Cursor <cursoragent@cursor.com>

* style: switch home listing from card grid to horizontal rows

Use icon, single-line summary, stats, and install CTA in scan-friendly
rows inside one bordered list instead of a three-column card grid.

Co-authored-by: Cursor <cursoragent@cursor.com>

* style: refine home listing rows like reference catalogs

Drop install CTAs, use muted icon tiles with brighter glyphs, and
flatten the list into divider rows without index numbers.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(home): rebuild listing with search and category select

Add inline catalog search, category dropdown with icons, grid/list views,
and empty states tuned for the home listing toolbar.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: refresh homepage hero and listing

* feat: add homepage apps section

* feat: add homepage publish section

* feat: add homepage ecosystem footer

* fix: use favicon icons for homepage app plugins

* fix: standardize footer styling globally

* fix: add spacing above homepage apps link

* fix: smooth homepage publish section transition

* fix: neutralize homepage light mode background

* fix: expand homepage app shortcuts grid

* fix: neutralize light theme surfaces

* fix: remove light publish section transition overlay

* fix: square footer bottom edge

* fix: simplify category menu scrollbar

* fix: clarify homepage category filters

* fix: refine mobile listing controls

* fix: restore homepage listing fold fade

* fix: tighten homepage listing row labels

* fix: space homepage apps cta

* fix: align homepage icons and app grids

* fix: scroll homepage app tabs on mobile

* fix: balance homepage app copy on mobile

* fix: balance homepage cli copy on mobile

* fix: refine mobile homepage motion

* feat: add popular publishers to homepage

* fix: remove stars from homepage listings

* fix: rank homepage listings by installs

* feat: show publisher catalog counts

* fix: align publisher card stats

* refactor: simplify publisher catalog counts

* fix: tighten publisher section heading

* fix: show app tiles on mobile

* fix: center mobile apps banner

* fix: use install icon in homepage listings

* feat: expand popular publishers

* feat: add mouse drag to publisher carousel

* chore: update pinned publishers

* fix: allow dragging publisher cards

* fix: show app card backgrounds on hover

* feat: curate homepage app shortcuts

* chore: reorder featured publishers

* fix: diversify homepage app shortcuts

* fix: hide native listing search clear

* fix: refine homepage app shortcuts and search reset

* fix: keep app icon frames rounded

* fix: hide officials tab for skills listing

* fix: replace featured homepage tabs with new

* fix: call homepage publishers creators

* fix: simplify homepage hero subtitle

* fix: use singular official plugin tab

* fix: make official the first plugin tab

* feat: add homepage trending skills tab

* fix: use existing trending skills leaderboard

* fix: show trending leaderboard installs

* fix: tighten listing kind toggle padding

* fix: normalize listing kind control padding

* fix: restore equal listing kind padding

* fix: shorten skills top tab label

* fix: shorten plugin top tab label

* fix: align trending skills installs metric

* fix: normalize listing view toggle padding

* Revert "fix: align trending skills installs metric"

This reverts commit 5c421b5cd4326347b4b702949b035fdf954811d8.

* Revert "fix: show trending leaderboard installs"

This reverts commit 8c7daa0e59c9e07cb99c232a2a34e5afbab80eeb.

* fix: hide stats on homepage trending skills

* fix: center listing row hover background

* chore: clean up homepage pre-pr checks

* fix: page homepage listing results

* fix: resolve homepage skill categories

* fix: stabilize homepage listing shortcut

* fix: harden homepage section rendering

* fix: preserve homepage skill category results

* perf: optimize homepage footer artwork

* fix: extend homepage byos reveal field

* fix: stabilize homepage CI

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-19 17:29:40 -07:00
Jason (Json) fc07846839 fix: make catalog metadata publisher-owned (#2757)
* fix: make catalog topics canonical

* fix: make catalog metadata publisher-owned
2026-06-19 17:14:03 -07:00
Vyctor H. Brzezowski 53d16a55d4 fix: align official plugin browse with official publishers (#2758)
* fix: bound official plugin browse queries

* fix: normalize official publisher plugins
2026-06-19 17:07:56 -07:00
Patrick Erichsen 0f5496ff31 fix: allow github sync owner-scoped skill slugs 2026-06-19 15:00:23 -07:00
Patrick Erichsen 7accfb71c7 chore: remove registry artifact backup jobs
Stop custom registry artifact backup/backfill/restore behavior now that Convex backups with file storage are the recovery source of truth. Legacy backup schema tables remain inert until a separate verified cleanup removes stored rows.
2026-06-19 13:35:44 -07:00
Patrick Erichsen 2ae30d70d5 feat: add Vercel Speed Insights 2026-06-19 11:36:01 -07:00
Jason (Json) 7760696362 fix: show taxonomy on plugin cards (#2748)
Restore resolved plugin taxonomy on sorted catalog cards and show controlled category labels when author topics are absent.

Prepared head SHA: eed82783f0
Reviewed-by: Jason (Json) <263060202+fuller-stack-dev@users.noreply.github.com>
2026-06-19 01:02:52 -06:00
Patrick Erichsen f381b01829 feat: surface plugin manifest icons (#2747) 2026-06-18 20:15:35 -07:00
Jesse Merhi 8090d4ecea feat: add skill install backfill migration
Adds the aggregate skill install backfill migration, tests, and operator proof.
2026-06-19 13:08:12 +10:00
Patrick Erichsen 1cac1b2539 feat: derive skill icons from categories (#2745) 2026-06-18 19:18:46 -07:00
Patrick Erichsen 84a26a6acd fix: persist registry backup seed completion state (#2746) 2026-06-18 18:43:12 -07:00
Patrick Erichsen 6bbdec2a4e ci: shard local-auth playwright gate (#2743) 2026-06-18 17:40:55 -07:00
Patrick Erichsen b1b3c5e1cd feat: enforce Convex retention policy
Add typed Convex retention policies, auth session/refresh-token cleanup crons, and retention guidance.
2026-06-18 17:30:48 -07:00
Patrick Erichsen 0a7232ee5e feat: parallelize registry artifact backup backfill (#2741) 2026-06-18 16:56:33 -07:00
Patrick Erichsen bf42578b04 chore: remove empty legacy cleanup tables
Remove depRegistryCache and githubBackupSyncState from the Convex schema after both production tables were replaced with empty arrays.
2026-06-18 16:52:35 -07:00
Patrick Erichsen c7d8e1cd0a chore: remove legacy rate limits schema
Remove the legacy rateLimits Convex schema table after production data was replaced with an empty table. Active rateLimitCounters code and schema remain untouched.
2026-06-18 16:36:07 -07:00
Jason (Json) 769c620800 feat: move topic discovery into search and category browse (#2732)
* feat: search categories and topics

* fix: remove topic filters from browse sidebars

* feat: show top topics for selected categories

* fix(deps): resolve static audit advisories

* fix(search): scope skill recall by category

* fix(search): keep scoped recall within Convex limits

* fix(search): avoid Convex pagination fan-out
2026-06-18 17:31:13 -06:00
Patrick Erichsen 8c4a9ce9a4 fix: stop auto-greeting staff emails (#2738) 2026-06-18 16:21:03 -07:00
Patrick Erichsen f3842551c4 chore: add skill stat event survivor counter 2026-06-18 15:55:17 -07:00
Patrick Erichsen d8f82b7379 chore: remove catalog classification rollout migrations (#2735)
* chore: remove catalog classification rollout migrations

* chore: keep static audit gate green
2026-06-18 15:52:46 -07:00
Patrick Erichsen 6bcf96fea9 Add package runtime id repair command (#2734)
* feat: add package runtime id repair

* chore: refresh audit gate overrides
2026-06-18 15:45:55 -07:00
Patrick Erichsen 22d3cd133c fix: shard skill stat event pruning 2026-06-18 08:06:15 -07:00
Patrick Erichsen 731d1aa800 fix: prune processed skill stat events 2026-06-17 21:39:40 -07:00
Patrick Erichsen f92ccfd488 fix: remove reply-to from transactional emails (#2725) 2026-06-17 21:07:25 -07:00
Jason (Json) de28e2a6eb fix: stabilize sparse skill category pagination (#2724)
Merged after exact-head maintainer review.

Prepared head SHA: 66d9662023
Autoreview: clean, no accepted/actionable findings.

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Reviewed-by: @fuller-stack-dev
2026-06-17 21:58:02 -06:00
Patrick Erichsen f6a2c875d6 fix: encode R2 backup object keys for SigV4 2026-06-17 20:18:20 -07:00
Patrick Erichsen caa3359329 chore: remove deprecated download dedupe table (#2722) 2026-06-17 18:34:00 -07:00
Patrick Erichsen eccdbb3830 chore: remove legacy rate limit shard schema
Remove the empty legacy rateLimitShards table from the Convex schema and remove its temporary cleanup functions/tests after production contents were cleared.
2026-06-17 18:08:42 -07:00
Patrick Erichsen c13a2514da docs: add namespace claims guide
Adds a dedicated ClawHub namespace claims guide and routes existing claim references through it.
2026-06-17 17:59:12 -07:00
Jason (Json)andPatrick Erichsen 4bc58e4939 feat: add catalog classification backfill (#2719)
* feat: add controlled catalog taxonomy and topics

* refactor: limit taxonomy input to publish surfaces

* fix: omit inactive official-first filter

* fix: preserve unsaved catalog metadata edits

* fix: enforce catalog taxonomy invariants

* fix: close catalog taxonomy review gaps

* fix: tolerate retired stored skill categories

* fix: bound catalog metadata filter scans

* fix: preserve comma-containing topic labels

* fix: tolerate retired stored plugin categories

* fix: ignore empty secret integration metadata

* fix: honor explicit categories in related skills

* fix: harden taxonomy rollout migration

* fix: harden taxonomy browse pagination

* fix: preserve skill topic recommendation fallback

* fix: scale curated skill category browse

* fix: preserve legacy plugin category filters

* fix: preserve taxonomy compatibility semantics

* fix: preserve legacy catalog browse links

* feat: make catalog metadata editing explicit

* test: update plugin manage context contract

* fix: address taxonomy review findings

* fix: preserve empty category publish flags

* fix: preserve catalog search and publish metadata

* chore: keep taxonomy migration operator-run

* docs: keep taxonomy migrations operator-run

* fix: reject inherited category aliases

* fix: preserve normalized topic search behavior

* test: cover full topic pagination cursors

* feat: add catalog classification backfill

---------

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-06-17 17:58:26 -07:00
Vyctor H. Brzezowski 1a3fdd8f51 fix: redirect sign-in to dashboard (#2718) 2026-06-17 17:31:21 -07:00
Vyctor H. Brzezowski 2d93401234 fix: use installs instead of downloads on social cards (#2713) 2026-06-17 17:28:20 -07:00
Vyctor H. Brzezowski b1e077da38 Polish ClawHub header and search UX (#2717)
* feat: sync header design polish

* fix: restore header search hints

* fix: strengthen search tab affordance

* Revert "fix: strengthen search tab affordance"

This reverts commit b586437577fce1b6521bb56ac01201cdb2bbc7da.

* fix: match search tab affordance

* fix: prevent header logo underline

* fix: soften search footer row

* fix: align search tab radii

* fix: make search footer full width

* fix: align mobile header search height

* fix: add responsive header more menu

* fix: simplify header more trigger

* fix: show mobile menu at compact header widths

* fix: widen compact search results menu

* fix: remove compact menu hover glow

* fix: use body font for user handle

* fix: prevent mobile nav link underline

* fix: revamp compact mobile header

* fix: connect mobile search results

* fix: move logged-out appearance switcher

* perf: shrink clawhub logo asset

* test: update header design contract

* fix: refine mobile auth controls

* fix: preserve watermark logo asset

* fix: scope arrow navigation to search tabs
2026-06-17 17:28:13 -07:00
Jason (Json)andPatrick Erichsen 04ec212100 feat: add controlled catalog taxonomy and topics (#2699)
* feat: add controlled catalog taxonomy and topics

* refactor: limit taxonomy input to publish surfaces

* fix: omit inactive official-first filter

* fix: preserve unsaved catalog metadata edits

* fix: enforce catalog taxonomy invariants

* fix: close catalog taxonomy review gaps

* fix: tolerate retired stored skill categories

* fix: bound catalog metadata filter scans

* fix: preserve comma-containing topic labels

* fix: tolerate retired stored plugin categories

* fix: ignore empty secret integration metadata

* fix: honor explicit categories in related skills

* fix: harden taxonomy rollout migration

* fix: harden taxonomy browse pagination

* fix: preserve skill topic recommendation fallback

* fix: scale curated skill category browse

* fix: preserve legacy plugin category filters

* fix: preserve taxonomy compatibility semantics

* fix: preserve legacy catalog browse links

* feat: make catalog metadata editing explicit

* test: update plugin manage context contract

* fix: address taxonomy review findings

* fix: preserve empty category publish flags

* fix: preserve catalog search and publish metadata

* chore: keep taxonomy migration operator-run

* docs: keep taxonomy migrations operator-run

* fix: reject inherited category aliases

* fix: preserve normalized topic search behavior

* test: cover full topic pagination cursors

* fix: tolerate invalid stored topics

---------

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-06-17 18:20:54 -06:00
Patrick Erichsen 0c493cfa19 Add deprecated download dedupe cleanup (#2715)
* feat: add deprecated download dedupe cleanup

* chore: refresh vulnerable transitive overrides
2026-06-17 16:37:41 -07:00
Patrick Erichsen 9331619cb8 fix: remove global slug reservation delete copy (#2716) 2026-06-17 16:36:51 -07:00
Patrick Erichsen 69c79e8f01 fix: bound rate limit counter storage 2026-06-17 16:24:13 -07:00
Patrick Erichsen 65e321b280 fix: parallelize skill artifact backup uploads 2026-06-17 15:40:08 -07:00
Patrick Erichsen 11ad31aa4e feat: scope skill slugs by owner (#2299)
* feat: scope skill slugs by owner

* test: cover owner-scoped skill slug publishes

* feat: scope skill slugs by owner

* fix: restore owner-scoped skill ci

* fix: keep package names separate from skill slugs
2026-06-17 15:31:40 -07:00
Patrick Erichsen 3c46a6b996 chore: remove ClawPack repair operator
Remove the completed one-off ClawPack repair operator after production repair and verification.
2026-06-17 14:09:38 -07:00
Patrick Erichsen 60c73e4c54 fix: sanitize ClawPack repair metadata (#2710) 2026-06-17 13:20:30 -07:00
Patrick Erichsen 4ccc9a1c62 chore: use worktreeinclude for local state setup (#2709) 2026-06-17 12:54:47 -07:00
Vyctor H. Brzezowski 5f0e1ee639 feat: import owned public GitHub skills (#2444)
* feat: add GitHub skill import flow

* polish: refine GitHub import review states

* feat: link publish page to GitHub import

* test: keep import route checks lint-clean

* fix: harden GitHub import review flow

* polish: show copied state for GitHub import links

* fix: stabilize GitHub import slug review state

* fix: harden GitHub import discovery

* fix: address GitHub import review feedback

* fix: preserve legacy GitHub skill filenames

* fix: allow public GitHub URL imports

* style: format github import

* fix: remove stale import route imports

* fix: enforce owned GitHub import URLs

* fix: align GitHub import with main
2026-06-17 12:43:51 -07:00
Andy Ye 6d87fc078d fix: repair historical ClawPack release files
Merge PR #2583 to add an admin-gated historical ClawPack release file repair action with dry-run, confirmation, cursor/resume support, regression coverage, and security-moderation docs.
2026-06-17 12:33:11 -07:00
Patrick Erichsen 5be7035a69 docs: add org namespace claim form (#2637) 2026-06-17 12:31:00 -07:00
Andy YeandPatrick Erichsen 96b04b0837 fix(clawhub): explain CLI transport failures (#2681)
* fix(clawhub): explain CLI transport failures

* fix(clawhub): redact generic auth credentials

* fix(clawhub): harden CLI error redaction

* fix: align auth peer dependency

---------

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-06-17 12:25:56 -07:00
Patrick Erichsen d6e54bdb65 fix: scope plugin validation findings to latest release (#2557) 2026-06-17 12:24:36 -07:00
Patrick Erichsen 691ba20195 fix: clear auth after account deletion (#2708) 2026-06-17 12:14:58 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 3d971fd3c2 build(deps): bump the production-minor-and-patch group across 1 directory with 20 updates (#2698)
Bumps the production-minor-and-patch group with 20 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@convex-dev/auth](https://github.com/get-convex/convex-auth) | `0.0.93` | `0.0.94` |
| [@openclaw/plugin-inspector](https://github.com/openclaw/plugin-inspector) | `0.3.12` | `0.3.15` |
| [@radix-ui/react-avatar](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/avatar) | `1.1.12` | `1.2.0` |
| [@radix-ui/react-dialog](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/dialog) | `1.1.16` | `1.1.17` |
| [@radix-ui/react-dropdown-menu](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/dropdown-menu) | `2.1.17` | `2.1.18` |
| [@radix-ui/react-label](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/label) | `2.1.9` | `2.1.10` |
| [@radix-ui/react-select](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/select) | `2.3.0` | `2.3.1` |
| [@radix-ui/react-separator](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/separator) | `1.1.9` | `1.1.10` |
| [@radix-ui/react-slot](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/slot) | `1.2.5` | `1.3.0` |
| [@radix-ui/react-toggle-group](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/toggle-group) | `1.1.12` | `1.1.13` |
| [@radix-ui/react-tooltip](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/tooltip) | `1.2.9` | `1.2.10` |
| [@react-email/render](https://github.com/resend/react-email/tree/HEAD/packages/render) | `2.0.8` | `2.0.9` |
| [@tanstack/react-router](https://github.com/TanStack/router/tree/HEAD/packages/react-router) | `1.170.15` | `1.170.16` |
| [@tanstack/react-start](https://github.com/TanStack/router/tree/HEAD/packages/react-start) | `1.168.25` | `1.168.26` |
| [convex](https://github.com/get-convex/convex-backend/tree/HEAD/npm-packages/convex) | `1.40.0` | `1.41.0` |
| [convex-helpers](https://github.com/get-convex/convex-helpers/tree/HEAD/packages/convex-helpers) | `0.1.118` | `0.1.119` |
| [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `1.17.0` | `1.20.0` |
| [semver](https://github.com/npm/node-semver) | `7.8.3` | `7.8.4` |
| [tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss) | `4.3.0` | `4.3.1` |
| [undici](https://github.com/nodejs/undici) | `7.27.2` | `7.28.0` |



Updates `@convex-dev/auth` from 0.0.93 to 0.0.94
- [Changelog](https://github.com/get-convex/convex-auth/blob/main/CHANGELOG.md)
- [Commits](https://github.com/get-convex/convex-auth/compare/v0.0.93...v0.0.94)

Updates `@openclaw/plugin-inspector` from 0.3.12 to 0.3.15
- [Release notes](https://github.com/openclaw/plugin-inspector/releases)
- [Changelog](https://github.com/openclaw/plugin-inspector/blob/main/CHANGELOG.md)
- [Commits](https://github.com/openclaw/plugin-inspector/compare/v0.3.12...v0.3.15)

Updates `@radix-ui/react-avatar` from 1.1.12 to 1.2.0
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/avatar/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/avatar)

Updates `@radix-ui/react-dialog` from 1.1.16 to 1.1.17
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/dialog/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/dialog)

Updates `@radix-ui/react-dropdown-menu` from 2.1.17 to 2.1.18
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/dropdown-menu/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/dropdown-menu)

Updates `@radix-ui/react-label` from 2.1.9 to 2.1.10
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/label/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/label)

Updates `@radix-ui/react-select` from 2.3.0 to 2.3.1
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/select/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/select)

Updates `@radix-ui/react-separator` from 1.1.9 to 1.1.10
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/separator/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/separator)

Updates `@radix-ui/react-slot` from 1.2.5 to 1.3.0
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/slot/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/slot)

Updates `@radix-ui/react-toggle-group` from 1.1.12 to 1.1.13
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/toggle-group/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/toggle-group)

Updates `@radix-ui/react-tooltip` from 1.2.9 to 1.2.10
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/tooltip/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/tooltip)

Updates `@react-email/render` from 2.0.8 to 2.0.9
- [Release notes](https://github.com/resend/react-email/releases)
- [Changelog](https://github.com/resend/react-email/blob/canary/packages/render/CHANGELOG.md)
- [Commits](https://github.com/resend/react-email/commits/@react-email/render@2.0.9/packages/render)

Updates `@tanstack/react-router` from 1.170.15 to 1.170.16
- [Release notes](https://github.com/TanStack/router/releases)
- [Changelog](https://github.com/TanStack/router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/TanStack/router/commits/@tanstack/react-router@1.170.16/packages/react-router)

Updates `@tanstack/react-start` from 1.168.25 to 1.168.26
- [Release notes](https://github.com/TanStack/router/releases)
- [Changelog](https://github.com/TanStack/router/blob/main/packages/react-start/CHANGELOG.md)
- [Commits](https://github.com/TanStack/router/commits/@tanstack/react-start@1.168.26/packages/react-start)

Updates `convex` from 1.40.0 to 1.41.0
- [Release notes](https://github.com/get-convex/convex-backend/releases)
- [Changelog](https://github.com/get-convex/convex-backend/blob/main/npm-packages/convex/CHANGELOG.md)
- [Commits](https://github.com/get-convex/convex-backend/commits/HEAD/npm-packages/convex)

Updates `convex-helpers` from 0.1.118 to 0.1.119
- [Changelog](https://github.com/get-convex/convex-helpers/blob/main/packages/convex-helpers/CHANGELOG.md)
- [Commits](https://github.com/get-convex/convex-helpers/commits/npm/0.1.119/packages/convex-helpers)

Updates `lucide-react` from 1.17.0 to 1.20.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.20.0/packages/lucide-react)

Updates `semver` from 7.8.3 to 7.8.4
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v7.8.3...v7.8.4)

Updates `tailwindcss` from 4.3.0 to 4.3.1
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.1/packages/tailwindcss)

Updates `undici` from 7.27.2 to 7.28.0
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v7.27.2...v7.28.0)

---
updated-dependencies:
- dependency-name: "@convex-dev/auth"
  dependency-version: 0.0.94
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@openclaw/plugin-inspector"
  dependency-version: 0.3.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@radix-ui/react-avatar"
  dependency-version: 1.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
- dependency-name: "@radix-ui/react-dialog"
  dependency-version: 1.1.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@radix-ui/react-dropdown-menu"
  dependency-version: 2.1.18
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@radix-ui/react-label"
  dependency-version: 2.1.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@radix-ui/react-select"
  dependency-version: 2.3.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@radix-ui/react-separator"
  dependency-version: 1.1.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@radix-ui/react-slot"
  dependency-version: 1.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
- dependency-name: "@radix-ui/react-toggle-group"
  dependency-version: 1.1.13
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@radix-ui/react-tooltip"
  dependency-version: 1.2.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@react-email/render"
  dependency-version: 2.0.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@tanstack/react-router"
  dependency-version: 1.170.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@tanstack/react-start"
  dependency-version: 1.168.26
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: convex
  dependency-version: 1.41.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
- dependency-name: convex-helpers
  dependency-version: 0.1.119
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: lucide-react
  dependency-version: 1.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
- dependency-name: semver
  dependency-version: 7.8.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: tailwindcss
  dependency-version: 4.3.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: undici
  dependency-version: 7.28.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-17 12:14:27 -07:00
Vyctor H. Brzezowski f3ab8663e3 feat(cli): polish terminal branding (#2434) 2026-06-17 12:13:42 -07:00
S.F.Kislev d061779cf0 fix: preserve vector score for lexical skill matches (#2591) 2026-06-17 12:12:01 -07:00
Jesse Merhi afc31802e8 fix: derive skill publish metadata from storage (#2470) 2026-06-17 11:54:10 -07:00
Vyctor H. Brzezowski 815a053b32 feat: add post-publish share dialog (#2439) 2026-06-17 11:52:56 -07:00
Patrick Erichsen 8d7ac580d1 chore: remove retired capability schema
Remove retired capability/capabilityTags/executesCode schema fields and the packageCapabilitySearchDigest table definition after the production cleanup verified those fields are empty.
2026-06-17 08:21:33 -07:00
Jesse Merhi 791dead7a8 fix: stabilize package recommendation fallback sorting
Summary:
- Add recommended sort support for skill-backed package catalog rows.
- Fall package/plugin recommended browse back to installs while recommendation score fields are missing.
- Keep new fallback pagination cursors on installs so later pages do not switch ordering.
- Default filtered plugin browse to installs unless Recommended is explicitly selected.

Verification:
- CI green on PR #2675 before merge.
- bunx convex codegen
- focused Vitest: 4 files passed, 424 tests passed
- ci:types-build passed
- ci:static passed
- post-cleanup focused Vitest: 2 files passed, 382 tests passed
2026-06-18 00:53:39 +10:00
Jesse Merhi 0189ddd5c2 fix: add install sort support to package catalog APIs
- Add install sorting to package and plugin catalog API paths.
- Reject removed downloads sort requests with 400.
- Keep recommended browse stable during recommendation-score backfill.
- Normalize stale plugin UI downloads sort URLs back to the default browse state.
2026-06-17 17:36:36 +10:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 4860945f34 build(deps-dev): bump the development-minor-and-patch group across 1 directory with 6 updates (#2689)
Bumps the development-minor-and-patch group with 6 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@playwright/test](https://github.com/microsoft/playwright) | `1.60.0` | `1.61.0` |
| [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) | `4.1.8` | `4.1.9` |
| [oxfmt](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt) | `0.54.0` | `0.55.0` |
| [oxlint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint) | `1.69.0` | `1.70.0` |
| [react-email](https://github.com/resend/react-email/tree/HEAD/packages/react-email) | `6.6.0` | `6.6.1` |
| [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.8` | `4.1.9` |



Updates `@playwright/test` from 1.60.0 to 1.61.0
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.60.0...v1.61.0)

Updates `@vitest/coverage-v8` from 4.1.8 to 4.1.9
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.9/packages/coverage-v8)

Updates `oxfmt` from 0.54.0 to 0.55.0
- [Release notes](https://github.com/oxc-project/oxc/releases)
- [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxfmt/CHANGELOG.md)
- [Commits](https://github.com/oxc-project/oxc/commits/oxfmt_v0.55.0/npm/oxfmt)

Updates `oxlint` from 1.69.0 to 1.70.0
- [Release notes](https://github.com/oxc-project/oxc/releases)
- [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint/CHANGELOG.md)
- [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.70.0/npm/oxlint)

Updates `react-email` from 6.6.0 to 6.6.1
- [Release notes](https://github.com/resend/react-email/releases)
- [Changelog](https://github.com/resend/react-email/blob/canary/packages/react-email/CHANGELOG.md)
- [Commits](https://github.com/resend/react-email/commits/react-email@6.6.1/packages/react-email)

Updates `vitest` from 4.1.8 to 4.1.9
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.9/packages/vitest)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  dependency-version: 1.61.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-minor-and-patch
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 4.1.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-minor-and-patch
- dependency-name: oxfmt
  dependency-version: 0.55.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-minor-and-patch
- dependency-name: oxlint
  dependency-version: 1.70.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-minor-and-patch
- dependency-name: react-email
  dependency-version: 6.6.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-minor-and-patch
- dependency-name: vitest
  dependency-version: 4.1.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-16 17:42:29 -07:00
Patrick Erichsen 2523832e18 fix: clarify publisher abuse ban emails (#2697) 2026-06-16 17:42:17 -07:00
Patrick Erichsen effd52a4ea docs: fix acceptable usage table formatting 2026-06-16 17:36:02 -07:00
Patrick Erichsen 3a46cdd07e docs: clarify acceptable usage policy 2026-06-16 17:19:10 -07:00
Patrick Erichsen 1e7de4a2a2 fix: remove retired capability metadata surfaces
Remove retired capabilities/capabilityTags/executesCode surfaces for skills and plugins, add guarded cleanup tooling for legacy rows, and sanitize pre-cleanup legacy seed/release payloads.
2026-06-16 17:03:44 -07:00
Patrick Erichsen 5471fb280f fix: seed worktree dev previews locally (#2693) 2026-06-16 12:53:59 -07:00
Patrick Erichsen 7efc6c4555 docs: strengthen ClawHub PR maintainer reviews (#2692) 2026-06-16 12:35:28 -07:00
Patrick Erichsen 0e20d64c63 chore: remove retired comment schema (#2690) 2026-06-16 08:32:04 -07:00
Jesse Merhi 303075c82b Remove retired moderation surfaces (#2658)
* chore: remove retired moderation surfaces

* test: cover retired moderation action wrappers

* chore: retain moderation security gates

* test: cover retired moderation compatibility paths

* fix: stop settings queries during account deletion

* docs: add retired comment purge command

* chore: delete retired scanner tombstones
2026-06-16 08:01:11 -07:00
Patrick Erichsen a3d4509102 docs: simplify content rights reporting page (#2683) 2026-06-15 21:35:20 -07:00
Patrick Erichsen 9c50b60e35 refactor: remove root install telemetry schema (#2682) 2026-06-15 21:20:28 -07:00
Jesse Merhi 2c1b331be0 Rank skills by installs and harden install telemetry
* docs: add install telemetry ranking spec

* feat: rank skills by installs and dedupe install telemetry

* test: cover install telemetry dedupe cleanup

* fix: clean install dedupes on skill hard delete

* fix: drain telemetry clear batches

* fix: phase telemetry clear batches

* test: cover install stat and export cursor regressions

* chore: drop obsolete recommendation ranking changes

* test: update skill stat event batch harness

* docs: remove install telemetry spec

* chore: remove downloads from search scoring input

* fix: keep install telemetry prune cron guarded
2026-06-16 13:49:50 +10:00
Patrick Erichsen 6feaa0974a fix: remove registry backup index writes 2026-06-15 20:36:39 -07:00
Patrick Erichsen 1b66b84e58 Increase bulk publisher abuse pressure (#2678)
* feat: increase bulk publisher abuse pressure

* feat: raise bulk publisher pressure elasticity
2026-06-15 20:04:02 -07:00
Patrick Erichsen 9115151949 feat: allow official personal publishers 2026-06-15 20:01:48 -07:00
Patrick Erichsen e8a75de2b1 refactor: remove root install telemetry (#2673)
* refactor: remove root install telemetry

* chore: add root telemetry cleanup job

* fix: preserve legacy root telemetry deletion

* fix: handle token list after account deletion

* test: isolate local-auth e2e from production crons
2026-06-15 19:42:51 -07:00
Patrick Erichsen 0db6b17a62 Cap skill stat drain action batches (#2671)
* fix: cap skill stat drain action batches

* test: align content rights correspondence guard
2026-06-15 18:56:06 -07:00
Jason (Json) e49d680f6c feat: add profile link to account menu (#2625)
Adds a Profile action to the signed-in account menu and resolves the active personal publisher handle server-side, including stale and legacy publisher-pointer handling with focused unit and browser coverage.

Prepared head SHA: a0926771c3
Reviewed against current main: 549eda8e44
Reviewed-by: @fuller-stack-dev
2026-06-15 19:42:02 -06:00
Patrick Erichsen 549eda8e44 docs: add content rights requester update flow (#2672) 2026-06-15 18:41:12 -07:00
Patrick Erichsen e3e5705d89 feat(cli)!: remove sync command (#2669)
* feat(cli)!: remove sync command

* feat(cli): reconcile skill publishes

* chore: release clawhub cli 0.22.0

* fix(cli): publish new skills after resolver miss

* feat(workflow)!: remove bump input
2026-06-15 18:22:52 -07:00
Patrick Erichsen 69dd3b5f68 Polish content rights email workflow (#2670)
* chore: simplify content rights correspondence skill

* fix: show staff email title before greeting
2026-06-15 18:21:49 -07:00
Patrick Erichsen 44f4ab0e74 fix: set GitHub scan upload content type 2026-06-15 18:09:15 -07:00
Patrick Erichsen 4efbf01fa8 fix: floor skill stat drain batch size
Floor legacy/small scheduled skill stat doc-sync action batch sizes to the production drain size so continuations cannot crawl indefinitely.
2026-06-15 18:05:40 -07:00
Patrick Erichsen f366269519 feat: add owner version deletion
Add owner-only one-way deletion for individual skill versions and plugin releases, with CLI --version support, latest/only-version guards, and browser proof.
2026-06-15 17:52:28 -07:00
Patrick Erichsen 1622a74802 fix: recover forced GitHub scan requests
Allows operator-forced GitHub-backed rescans to recover incomplete pending requests that have no worker job, with regression coverage for the production NVIDIA scan state.
2026-06-15 17:52:24 -07:00
Patrick Erichsen 455f4ea19c fix: serialize skill stat event drain
Serialize the skill document stat drain behind a short-lived Convex lease and add admin kick/status helpers for manual backlog recovery.
2026-06-15 17:48:58 -07:00
Vincent Koc 5256b9e2ba fix(agents): sync autoreview scope guard
Sync the vendored autoreview skill from openclaw/agent-skills#32, including the closeout scope guard from openclaw/openclaw#93435.\n\nVerification: diff whitespace check, shell syntax, Python compile, prompt-policy assertion, and repo oxfmt check for SKILL.md.
2026-06-16 08:40:52 +08:00
Patrick Erichsen 76638de7ad feat: run full ClawScan for GitHub skills
Adds full ClawScan execution for GitHub-backed skills and updates the static audit ignore list for newly reported DOMPurify advisories.
2026-06-15 17:36:41 -07:00
Patrick Erichsen 89bb8fe938 feat: add content rights case tooling (#2657) 2026-06-15 17:32:07 -07:00
Patrick Erichsen 60c61d44a6 fix: remove homepage proof stats strip
Remove the hard-coded homepage proof stats strip and unused styles. Update the DOMPurify override to keep the static audit gate green.
2026-06-15 17:22:56 -07:00
Patrick Erichsen ab3c708cc9 fix: accept legacy install telemetry snapshots (#2656) 2026-06-15 12:32:29 -07:00
Patrick Erichsen da9da2b66d chore: remove legacy package ZIP backfill (#2654) 2026-06-15 11:55:26 -07:00
Patrick Erichsen 44ce895b44 fix: use installs across public adoption surfaces (#2653)
* fix: use installs across public adoption surfaces

* fix(cli): expose install-based explore sorting

* test: type legacy publisher sort coverage

* fix: preserve install compatibility paths

* fix: bound legacy hover install fallback

* docs: stop advertising downloads sort

* fix: preserve install rollout compatibility

* chore: update ws security override

* test: stub image optimizer in local browser smoke

* fix: normalize legacy download sort aliases
2026-06-15 11:06:29 -07:00
Patrick Erichsen 7f35ce91af fix: move GitHub source sync to Node runtime (#2652) 2026-06-15 10:49:18 -07:00
Patrick Erichsen bb35aca7bf fix: show publisher installs on profiles (#2651) 2026-06-15 09:35:17 -07:00
Jesse Merhi 078425f074 feat: add plugin install ranking (#2633) 2026-06-15 09:28:26 -07:00
Jesse Merhi e5f3ba272b fix: restrict docs auth return origins to dev only
* fix: couple docs auth localhost returns to a local app origin

The /auth/docs broker POSTs the signed-in user's auth token to the
return_to origin, but the allowlist trusted http://localhost:4173 /
127.0.0.1:4173 unconditionally, so production could hand the token to a
local listener.

Allow loopback return origins only when the app itself is served from a
loopback origin, so a public deployment (incl. staging/preview) can never
post the token to localhost regardless of runtime env. Keep the fixed
production docs origins (clawhub.ai, documentation.openclaw.ai,
docs.openclaw.ai), drop loopback from the production CSP form-action, and
record the token-destination invariant in specs/auth-identity.md.

* fix: align docs auth form destinations

* fix: retry public GitHub package fetches

* Revert "fix: retry public GitHub package fetches"

This reverts commit 1529aa34f3.
2026-06-15 21:52:06 +10:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ae7aa5861d build(deps-dev): bump the development-minor-and-patch group across 1 directory with 2 updates (#2622)
Bumps the development-minor-and-patch group with 2 updates in the / directory: [@tailwindcss/vite](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-vite) and [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@tailwindcss/vite` from 4.3.0 to 4.3.1
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.1/packages/@tailwindcss-vite)

Updates `@types/node` from 25.9.2 to 25.9.3
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@tailwindcss/vite"
  dependency-version: 4.3.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-minor-and-patch
- dependency-name: "@types/node"
  dependency-version: 25.9.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 04:04:29 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> d56c07e9ae build(deps): bump the github-actions group across 1 directory with 2 updates (#2620)
Bumps the github-actions group with 2 updates in the / directory: [actions/checkout](https://github.com/actions/checkout) and [actions/upload-artifact](https://github.com/actions/upload-artifact).


Updates `actions/checkout` from 4 to 6
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v4...v6)

Updates `actions/upload-artifact` from 4 to 7
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 04:04:15 -07:00
Patrick Erichsen 078d1c88b8 chore: remove API key required cleanup migration (#2648) 2026-06-14 23:13:29 -07:00
Patrick Erichsen ca1f88f3e4 fix: allow guarded transfer of deleted skills (#2647) 2026-06-14 21:47:37 -07:00
Patrick Erichsen 61302e920f feat: add plugin versions tab (#2646)
* feat: add package versions API wrapper

* fix: omit empty package version cursor

* feat: add plugin versions tab

* fix: paginate plugin version history

* fix: remove unsafe plugin version downloads

* fix: harden plugin versions loading

* fix: preserve plugin versions pagination state

* fix: reset plugin detail state on navigation
2026-06-14 20:43:18 -07:00
Patrick Erichsen 2293016c20 revert: retire API key required skill feature (#2644) 2026-06-14 20:42:26 -07:00
Patrick Erichsen b0c8fe2a01 fix: serialize registry backup index writes 2026-06-14 20:37:34 -07:00
Patrick Erichsen ee9c6c1412 fix: normalize publisher recovery handle comparisons (#2643)
Normalize mixed-case stored user handles in publisher recovery guards and retire the previous mixed-case handle before handoff.
2026-06-14 20:26:35 -07:00
Patrick Erichsen ade8e3843a fix: serialize registry backup retry drains (#2641) 2026-06-14 19:59:05 -07:00
clawsweeper[bot]momothemageclawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>momothemage
44a552f3da fix: add guarded personal publisher recovery (#2642)
Summary:
- The PR adds an admin-only personal publisher recovery flow with HTTP API, admin CLI support, shared response schema, docs/spec notes, and tests.
- Reproducibility: yes. Source inspection shows current main lacks a publisher-recovery route and personal pub ...  to an existing ClawHub user, so a replacement GitHub principal has no staff recovery path without this PR.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix: migrate publisher recovery resource owners
- PR branch already contained follow-up commit before automerge: fix: add guarded personal publisher recovery

Validation:
- ClawSweeper review passed for head 5cfb360520.
- Required merge gates passed before the squash merge.

Prepared head SHA: 5cfb360520
Review: https://github.com/openclaw/clawhub/pull/2642#issuecomment-4704078560

Co-authored-by: momothemage <niuzhengnan@163.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: momothemage
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
2026-06-15 02:57:31 +00:00
Patrick Erichsen 85695c49a8 fix: compact theme controls in profile menu (#2640) 2026-06-14 19:53:43 -07:00
Patrick Erichsen 47038f0e24 fix: skip indexed registry backup retries (#2639) 2026-06-14 19:27:33 -07:00
Patrick Erichsen 3c48b989d4 test: cover skill summary digest sync (#2638) 2026-06-14 19:26:28 -07:00
Patrick Erichsen 03f97349b0 fix: preserve legacy package ZIP hashes (#2636)
* test: fix indexed row helper lint

* fix: preserve legacy package zip hashes
2026-06-14 18:56:31 -07:00
Patrick Erichsen 6ae7cb5345 fix: bulk repair registry backup indexes 2026-06-14 18:54:22 -07:00
Patrick Erichsen 17b5461539 fix: drain registry backup retries safely (#2634) 2026-06-14 18:25:34 -07:00
Patrick Erichsen b8ec008de3 fix: preserve single-row ClawHub header (#2632) 2026-06-14 16:46:37 -07:00
Patrick Erichsen fdb4d312f4 docs: restore OpenClaw-hosted ClawHub docs (#2631)
* Revert "fix: serve docs subdomain root (#2630)"

This reverts commit 9ef33726b8.

* Revert "feat: move docs to canonical subdomain"

This reverts commit 6afb91b772.

* Revert "docs: serve ClawHub docs with shared OpenClaw renderer (#2609)"

This reverts commit 71156751ff.

* fix: restore ClawHub email docs host

* test: preserve non-docs validation fixes

* test: remove stale docs proxy test

* docs: keep ClawHub contributor references current

* fix: keep docs auth callback outside docs routes
2026-06-14 16:05:34 -07:00
Patrick Erichsen 9ef33726b8 fix: serve docs subdomain root (#2630) 2026-06-14 13:28:45 -07:00
Peter Steinberger 6afb91b772 feat: move docs to canonical subdomain 2026-06-13 15:38:43 -07:00
Peter Steinberger 749028df12 fix: keep proxy tests out of server routes 2026-06-13 14:36:48 -07:00
Peter Steinberger 6820029e2f fix: preserve Ask Molty auth through proxy 2026-06-13 14:29:26 -07:00
Vincent Koc e86aa30a77 fix: require npm publish proof artifact 2026-06-13 18:06:27 +08:00
Vincent Koc 7d69b337c7 fix: make skill updates rollback safe 2026-06-13 18:04:04 +08:00
Vincent Koc 9fb09e0793 fix: retire cli browser token callback 2026-06-13 17:58:32 +08:00
Vincent Koc 3711b45d08 fix: default cli login to device flow 2026-06-13 17:55:09 +08:00
Vincent Koc 49b0c33fe8 fix: require confirmation for cli token handoff 2026-06-13 17:50:24 +08:00
Vincent Koc 84f2216d73 fix: harden clawhub cli sync reporting 2026-06-13 17:45:21 +08:00
Vincent Koc 64e22ae06e fix: harden clawhub cli release and sync checks 2026-06-13 17:38:03 +08:00
Patrick Erichsen 71156751ff docs: serve ClawHub docs with shared OpenClaw renderer (#2609)
* feat: serve clawhub docs under docs route

* feat: build ClawHub docs with openclaw docs

* chore: remove unused nav icon export

* docs: use ClawHub logo in docs shell

* docs: prepare ClawHub docs for Mintlify

* test: use static preview readiness for playwright

* chore: ignore current esbuild audit advisories

* docs: build ClawHub docs with shared OpenClaw renderer

* test: print package publish e2e failures
2026-06-12 19:02:44 -07:00
Patrick Erichsen 4b3c2bb630 Back up registry artifacts and add migration backup gate (#2600)
* fix: retarget registry backup gate

* feat: mirror package registry artifacts

* fix: keep package backup latest on newest release

* fix: bound registry backup health checks

* fix: harden package artifact backup paths

* fix: report package backup sync progress

* fix: type package backup sync result

* fix: require migration backup before dry run

* fix: simplify registry backup roots

* feat: back up registry artifacts to R2
2026-06-12 16:31:58 -07:00
Patrick Erichsen ee1c80202f fix: prefer app auth for trusted publisher repo lookup (#2623) 2026-06-12 16:31:43 -07:00
Jesse Merhi 73c32d6296 chore: fix dependabot bun update config (#2566) 2026-06-12 16:14:29 -07:00
Patrick Erichsen 99e5ab4ed7 docs: add session store validation remediation
Add the missing sdk-load-session-store remediation section and table entry.
2026-06-12 13:01:51 -07:00
Patrick Erichsen 496f52693a fix: clarify scoped package publish access errors (#2617) 2026-06-12 12:43:15 -07:00
Patrick Erichsen 229a05a4c7 feat: render mermaid diagrams in markdown (#2616) 2026-06-12 11:04:54 -07:00
Jesse Merhi b2447d750a Show unfiltered browse totals 2026-06-12 19:04:57 +10:00
Momo 8ce11888aa fix: tolerate duplicate github auth rows (#2612) 2026-06-12 14:49:49 +08:00
Momoandclawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> 3e66b50065 feat: expose skill description setup metadata (#2571)
* feat: expose skill description setup metadata

* feat: expose skill description setup metadata

* fix: narrow skill setup metadata contract

* fix: preserve required setup env metadata

* fix: import public skill file access helper

---------

Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
2026-06-12 11:19:38 +08:00
Patrick Erichsen 4f2d20e1e0 chore: remove clawscan cleanup migration
Remove the temporary ClawScan cleanup migration and narrow the schema after production cleanup.
2026-06-11 19:55:33 -07:00
Patrick Erichsen 1f66c9cde6 chore: add clawscan cleanup migration (#2610) 2026-06-11 19:12:31 -07:00
Patrick Erichsen dfac8d83ce fix: allow npm-compatible publisher handles
Allow publisher/org handles to use npm-compatible dots and underscores, update route validation and user-facing copy, and use neutral scoped package examples in docs/tests.
2026-06-11 18:45:25 -07:00
Patrick Erichsen a63d153b1d chore: release clawhub cli 0.21.0 2026-06-11 17:58:19 -07:00
Patrick Erichsen 9d49df109d ci: make package publish artifacts matrix-safe (#2606) 2026-06-11 17:56:04 -07:00
Patrick Erichsen 59c1ee3a7b chore: remove clawscan note remnants
Delete all remaining deprecated ClawScan note compatibility code, schema fields, tests, and spec text.
2026-06-11 17:54:45 -07:00
Patrick Erichsen a810d838bd fix: reserve docs owner handle (#2604) 2026-06-11 17:47:58 -07:00
Patrick Erichsen e319df0760 Move ClawHub emails to React Email (#2597)
* feat: update ClawHub email templates

* feat: move ClawHub emails to React Email

* fix: align local auth email assertion
2026-06-11 17:25:19 -07:00
Patrick Erichsen a1becd0c0f chore: remove autoban remediation tooling
Remove the obsolete one-off autoban remediation command/API, keep deprecated clawscan-note records from leaking through APIs, and retain legacy queue compatibility for old scan jobs.
2026-06-11 17:13:26 -07:00
Patrick Erichsen 2e7f3e752e fix: use no-reply sender for staff emails (#2602) 2026-06-11 16:58:38 -07:00
Patrick Erichsen 3b53ddbdbe chore: rename ClawHub admin package (#2599) 2026-06-11 16:49:17 -07:00
Patrick Erichsen 9a0671034c feat: expose package trusted publisher CLI commands (#2601)
* feat: expose package trusted publisher CLI commands

* docs: document package trusted publisher commands

* test: isolate clawhub artifact default help
2026-06-11 16:43:00 -07:00
Patrick Erichsen 6bc45b30ba fix: block malicious skill versions on download
Ensures skill file/card/download APIs block the exact requested malicious skill version, even when the current/latest version is clean.
2026-06-11 16:17:59 -07:00
Jesse MerhiandPatrick Erichsen 2c9aa36dcb feat: remove the souls feature end to end
Removes the SOULS content type and the SoulHub/onlycrabs.ai dual-site mode:
six Convex tables, the /api/v1/souls HTTP API, /souls routes, soul OG
images, GitHub soul backups, seeds, the VITE_FEATURE_SOULS flag, and the
site-mode machinery. Surviving skills-only code paths are de-branched and
simplified (tag resolution, publish form, nav/footer, ban flow, search).

Product decisions: /souls URLs and /api/v1/souls return plain 404s (no
redirect or 410 tombstone); reserved slugs souls/soulhub/onlycrabs stay.

Deploy prerequisite: clear the six soul tables and four storage blobs in
the prod Convex dashboard first (see PR description runbook).

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-06-11 10:45:58 -07:00
Patrick Erichsen 6fd3dcabf9 docs: add clawhub 0.20.2 release notes 2026-06-11 10:36:13 -07:00
Momoandclawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> 7166883d7d fix: tolerate duplicate CLI device user codes (#2589)
* fix: tolerate duplicate CLI device user codes

* fix: harden duplicate device code handling

* fix: tolerate duplicate CLI device user codes

---------

Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
2026-06-11 18:18:38 +08:00
Jason (Json) 4496acf9f0 fix: remove homepage suggestion chips (#2590) 2026-06-11 15:46:16 +10:00
Jesse Merhi d6500794c6 feat: rank skills by installs and dedupe install telemetry (#2535) 2026-06-11 14:22:11 +10:00
joshavant a1c91b265e chore: remove temporary auth identity cleanup tool 2026-06-10 19:19:55 -05:00
joshavant cd47bbe060 chore: add temporary auth identity cleanup tool 2026-06-10 19:12:34 -05:00
joshavant 31c69ff518 chore: remove temporary auth identity cleanup tool 2026-06-10 18:57:04 -05:00
Patrick Erichsen c22a2d295b fix: normalize ClawScan review visibility 2026-06-10 16:51:30 -07:00
joshavant f0b0185649 chore: add temporary auth identity cleanup tool 2026-06-10 18:46:50 -05:00
joshavant 7742ac7f52 chore: remove temporary identity remediation tool 2026-06-10 15:34:22 -05:00
joshavant e4ef192ce6 chore: add temporary identity remediation tool 2026-06-10 15:32:09 -05:00
joshavant 207ab1758e chore: remove github auth remediation tool 2026-06-10 14:34:21 -05:00
Patrick Erichsen 8446d65224 chore: remove migration lifecycle skill (#2592) 2026-06-10 12:32:57 -07:00
joshavant 58051dffcb chore: add github auth remediation tool 2026-06-10 14:14:08 -05:00
joshavant 0b6466184b fix: reject malformed github oauth ids 2026-06-10 13:14:04 -05:00
joshavant b3f98b4686 fix: prevent GitHub email account linking 2026-06-10 12:03:16 -05:00
Patrick Erichsen 79d9a86e6f ci: dispatch plugin inspector scans on pin bumps (#2584)
* ci: dispatch plugin inspector bulk scans on pin bump

* ci: rename plugin inspector bulk scan workflow

* fix: include CLI plugin inspector pin in scan dispatch

* chore: remove stale plugin inspector cron comment

* fix: dispatch inspector scans from main pushes
2026-06-09 21:45:29 -07:00
Patrick Erichsen 27faf50992 feat: surface author remediation for plugin validation
Adds author-remediation metadata and ClawHub validation UI/CLI/email handling for Plugin Inspector findings.
2026-06-09 21:09:25 -07:00
Patrick Erichsen e9571f3aa4 docs: remove stale skill folder guidance
Remove obsolete AGENTS.md guidance that treated repo skill additions as a standing contributor-policy issue.
2026-06-09 21:08:16 -07:00
Patrick Erichsen 18ec428fc5 docs: correct org delete moderation note (#2582) 2026-06-09 21:06:50 -07:00
Patrick Erichsen 0724abccbc Add guarded admin email and reserve clawhub handle (#2580)
* feat: add guarded admin email command

* fix: reserve clawhub publisher handle

* fix: harden admin org deletion and email audit
2026-06-09 20:56:48 -07:00
Patrick Erichsen 7b71c15ee1 fix: delegate browser automation scanning to SkillSpector
Remove ClawHub static-scanner browser automation rules because SkillSpector owns that analysis lane. Keep static moderation focused on non-browser-specific concrete source/sink evidence and update policy/spec wording accordingly.

Linear: CLAW-252
2026-06-09 20:55:08 -07:00
Patrick Erichsen d2921c0600 feat: add owner skill deletion (#2548) 2026-06-09 20:50:17 -07:00
Patrick Erichsen f5bf1b61e6 chore: remove stale maintenance backfills
Remove completed/stale maintenance backfills and their obsolete package artifact HTTP/CLI/schema surface. Keep the targeted legacy publisher ownership repair path.
2026-06-09 18:47:30 -07:00
Patrick Erichsen 6447101397 chore: remove clawhub ui proof skill 2026-06-09 16:29:58 -07:00
Patrick Erichsen d12adf2f37 chore: remove dependency registry cleanup 2026-06-09 16:29:13 -07:00
Patrick Erichsen 741599d20e fix: remove redundant ban email support note 2026-06-09 15:20:26 -07:00
Patrick Erichsen 9244087d00 chore: add admin alias for ClawHub operator CLI 2026-06-09 15:20:26 -07:00
Patrick Erichsen 23edf7d19d fix: split dependency cleanup pagination (#2573) 2026-06-09 11:32:52 -07:00
Jesse Merhi 1876698093 fix: simplify package listing cursors
* fix: simplify package listing cursors

* test: avoid asserting package cursor internals
2026-06-10 00:54:52 +10:00
Jason (Json)andJesse Merhi ad640d2903 fix: sort plugins by downloads with family index
* fix: sort plugin downloads by family index

* fix: reset legacy package download cursors

---------

Co-authored-by: Jesse Merhi <79823012+jesse-merhi@users.noreply.github.com>
2026-06-09 23:18:36 +10:00
Jesse Merhi c0143b1cd1 fix: retire dependency registry scans 2026-06-09 22:28:13 +10:00
clawsweeper[bot]chlinyclawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>momothemage
8970a46acf fix: use consistent installedAt timestamp for origin.json and lockfile (#2569)
Summary:
- The PR captures one `installedAt` value per install/update path and reuses it for both `.clawhub/origin.json` and the lockfile, with regression tests for timestamp equality.
- Reproducibility: yes. Source inspection of current `main` shows separate timestamp writes in `cmdInstall` an ... d report provides the CLI and `jq` reproduction path, though I did not execute it in this read-only review.

Automerge notes:
- PR branch already contained follow-up commit before automerge: test: add regression tests for installedAt timestamp equality
- PR branch already contained follow-up commit before automerge: fix: use consistent installedAt timestamp for origin.json and lockfile

Validation:
- ClawSweeper review passed for head b941ee37d6.
- Required merge gates passed before the squash merge.

Prepared head SHA: b941ee37d6
Review: https://github.com/openclaw/clawhub/pull/2569#issuecomment-4657649958

Co-authored-by: chliny <chliny11@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: momothemage
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
2026-06-09 08:16:53 +00:00
Jesse Merhi a3de2360b4 docs: clarify ClawHub vulnerability disclosure policy (#2567) 2026-06-09 17:38:56 +10:00
clawsweeper[bot]momothemageclawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>momothemage
620445f080 fix: slim package manage context (#2564)
Summary:
- The branch narrows `packages.getManageContext` to package/release identifier fields, adds unit and local-auth payload-capture coverage, and adjusts a WebCrypto digest helper.
- Reproducibility: yes. Source inspection of current main shows `getManageContext` returning the full package document and public release object, and the PR adds a focused test for the slim response shape.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix: slim package manage context

Validation:
- ClawSweeper review passed for head 5b5834bfc6.
- Required merge gates passed before the squash merge.

Prepared head SHA: 5b5834bfc6
Review: https://github.com/openclaw/clawhub/pull/2564#issuecomment-4655984471

Co-authored-by: momothemage <niuzhengnan@163.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: momothemage
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
2026-06-09 04:35:14 +00:00
Patrick Erichsen 89a89af183 fix: suppress inspector gap findings in clawhub 2026-06-08 21:29:31 -07:00
Patrick Erichsen 2d367c60ed Revert "Revert "chore: disable plugin inspector nightly schedule (#2558)" (#2…" (#2563)
This reverts commit 8b39d00247.
2026-06-08 20:51:15 -07:00
Patrick Erichsen 8b39d00247 Revert "chore: disable plugin inspector nightly schedule (#2558)" (#2562)
This reverts commit e09bf7f0fd.
2026-06-08 20:35:53 -07:00
Patrick Erichsen e6a8bce394 Fix package publish validation flow (#2561)
* feat: support prebuilt package publish artifacts

* fix: finalize package publish validation flow

* fix: polish plugin inspector findings email
2026-06-08 20:35:25 -07:00
Patrick Erichsen e09bf7f0fd chore: disable plugin inspector nightly schedule (#2558) 2026-06-08 20:09:23 -07:00
Patrick Erichsen c9bb130235 feat: support prebuilt package publish artifacts 2026-06-08 19:52:32 -07:00
Patrick Erichsen be70ae5e9f Publish public Plugin Inspector findings (#2525)
* feat: gate plugin publishes with inspector warnings

* fix: harden plugin publish inspector gate

* feat: publish public plugin inspector findings

* fix: refine plugin inspector publish errors

* feat: add package validation command

* fix: refine plugin validation findings UI

* fix: scan only latest plugin releases nightly

* feat: add dry run for nightly plugin inspection

* test: fix plugin validation tab e2e matcher
2026-06-08 19:41:48 -07:00
Vincent Koc 9321c83b93 fix: block moderated skill version metadata
Block exact skill-version metadata and scan responses when the requested version is the moderated source version. Apply the same guard to package-compatible skill version metadata and cover public-null fallback cases.
2026-06-09 11:23:55 +09:00
Patrick Erichsen 2a3e08e1bd fix: include clawscan summaries in blocked version emails (#2554) 2026-06-08 18:14:51 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 0ead9a60cf build(deps-dev): bump the development-minor-and-patch group with 3 updates (#2553)
Bumps the development-minor-and-patch group with 3 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [oxfmt](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt) and [oxlint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint).


Updates `@types/node` from 25.9.1 to 25.9.2
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `oxfmt` from 0.53.0 to 0.54.0
- [Release notes](https://github.com/oxc-project/oxc/releases)
- [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxfmt/CHANGELOG.md)
- [Commits](https://github.com/oxc-project/oxc/commits/oxfmt_v0.54.0/npm/oxfmt)

Updates `oxlint` from 1.68.0 to 1.69.0
- [Release notes](https://github.com/oxc-project/oxc/releases)
- [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint/CHANGELOG.md)
- [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.69.0/npm/oxlint)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 25.9.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-minor-and-patch
- dependency-name: oxfmt
  dependency-version: 0.54.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-minor-and-patch
- dependency-name: oxlint
  dependency-version: 1.69.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-08 15:02:38 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> f0274e41c4 build(deps): bump the production-minor-and-patch group with 13 updates (#2552)
Bumps the production-minor-and-patch group with 13 updates:

| Package | From | To |
| --- | --- | --- |
| [@radix-ui/react-avatar](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/avatar) | `1.1.11` | `1.1.12` |
| [@radix-ui/react-dialog](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/dialog) | `1.1.15` | `1.1.16` |
| [@radix-ui/react-dropdown-menu](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/dropdown-menu) | `2.1.16` | `2.1.17` |
| [@radix-ui/react-label](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/label) | `2.1.8` | `2.1.9` |
| [@radix-ui/react-select](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/select) | `2.2.6` | `2.3.0` |
| [@radix-ui/react-separator](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/separator) | `1.1.8` | `1.1.9` |
| [@radix-ui/react-slot](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/slot) | `1.2.4` | `1.2.5` |
| [@radix-ui/react-toggle-group](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/toggle-group) | `1.1.11` | `1.1.12` |
| [@radix-ui/react-tooltip](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/tooltip) | `1.2.8` | `1.2.9` |
| [@tanstack/react-router](https://github.com/TanStack/router/tree/HEAD/packages/react-router) | `1.170.12` | `1.170.15` |
| [@tanstack/react-start](https://github.com/TanStack/router/tree/HEAD/packages/react-start) | `1.168.21` | `1.168.25` |
| [semver](https://github.com/npm/node-semver) | `7.8.2` | `7.8.3` |
| [undici](https://github.com/nodejs/undici) | `7.27.1` | `7.27.2` |


Updates `@radix-ui/react-avatar` from 1.1.11 to 1.1.12
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/avatar/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/avatar)

Updates `@radix-ui/react-dialog` from 1.1.15 to 1.1.16
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/dialog/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/dialog)

Updates `@radix-ui/react-dropdown-menu` from 2.1.16 to 2.1.17
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/dropdown-menu/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/dropdown-menu)

Updates `@radix-ui/react-label` from 2.1.8 to 2.1.9
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/label/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/label)

Updates `@radix-ui/react-select` from 2.2.6 to 2.3.0
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/select/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/select)

Updates `@radix-ui/react-separator` from 1.1.8 to 1.1.9
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/separator/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/separator)

Updates `@radix-ui/react-slot` from 1.2.4 to 1.2.5
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/slot/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/slot)

Updates `@radix-ui/react-toggle-group` from 1.1.11 to 1.1.12
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/toggle-group/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/toggle-group)

Updates `@radix-ui/react-tooltip` from 1.2.8 to 1.2.9
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/tooltip/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/tooltip)

Updates `@tanstack/react-router` from 1.170.12 to 1.170.15
- [Release notes](https://github.com/TanStack/router/releases)
- [Changelog](https://github.com/TanStack/router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/TanStack/router/commits/@tanstack/react-router@1.170.15/packages/react-router)

Updates `@tanstack/react-start` from 1.168.21 to 1.168.25
- [Release notes](https://github.com/TanStack/router/releases)
- [Changelog](https://github.com/TanStack/router/blob/main/packages/react-start/CHANGELOG.md)
- [Commits](https://github.com/TanStack/router/commits/@tanstack/react-start@1.168.25/packages/react-start)

Updates `semver` from 7.8.2 to 7.8.3
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v7.8.2...v7.8.3)

Updates `undici` from 7.27.1 to 7.27.2
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v7.27.1...v7.27.2)

---
updated-dependencies:
- dependency-name: "@radix-ui/react-avatar"
  dependency-version: 1.1.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@radix-ui/react-dialog"
  dependency-version: 1.1.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@radix-ui/react-dropdown-menu"
  dependency-version: 2.1.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@radix-ui/react-label"
  dependency-version: 2.1.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@radix-ui/react-select"
  dependency-version: 2.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
- dependency-name: "@radix-ui/react-separator"
  dependency-version: 1.1.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@radix-ui/react-slot"
  dependency-version: 1.2.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@radix-ui/react-toggle-group"
  dependency-version: 1.1.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@radix-ui/react-tooltip"
  dependency-version: 1.2.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@tanstack/react-router"
  dependency-version: 1.170.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@tanstack/react-start"
  dependency-version: 1.168.25
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: semver
  dependency-version: 7.8.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: undici
  dependency-version: 7.27.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-08 15:02:35 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 315cf9603f build(deps): bump github/codeql-action in the github-actions group (#2551)
Bumps the github-actions group with 1 update: [github/codeql-action](https://github.com/github/codeql-action).


Updates `github/codeql-action` from 4.36.1 to 4.36.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/87557b9c84dde89fdd9b10e88954ac2f4248e463...8aad20d150bbac5944a9f9d289da16a4b0d87c1e)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.36.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-08 14:54:30 -07:00
Peter Steinberger 2c623b3beb fix: keep dev persona handles stable 2026-06-08 20:27:25 +01:00
Peter Steinberger 01612b4f15 style: fix changelog spacing 2026-06-08 20:11:37 +01:00
Peter Steinberger 295db69eba chore: require Node.js 22 for CLI packages 2026-06-08 20:01:44 +01:00
Patrick Erichsen 1fa4f9049d feat: add plugin export endpoint (#2547)
Add an authenticated /api/v1/plugins/export route that mirrors the skills export shape, supports an optional plugin family filter, defaults to both code and bundle plugins, and emits ZIP archives with manifest, error, and per-plugin metadata entries.

Autoreview findings addressed:

- [P1] Do not mark partially consumed plugin pages done

  Keep merged plugin export family cursor state active while buffered rows remain, and cover the pagination regression.

- [P1] Block release-level security states in plugin export

  Apply the package release download security block before reading release storage blobs.

- [P2] Avoid colliding with exported plugin metadata

  Move generated plugin metadata under __clawhub_export/ so plugin file paths cannot overwrite it.
2026-06-07 22:52:04 -07:00
Patrick Erichsen 5c19da48d0 fix: reserve admin owner handle (#2546) 2026-06-07 20:27:58 -07:00
Patrick Erichsen fc0a85e150 fix: make account recovery purge idempotent (#2543) 2026-06-06 19:08:23 -07:00
Patrick Erichsen 3a2568f751 fix: schedule account deletion digest sync safely 2026-06-06 18:41:44 -07:00
Patrick Erichsen 8e1bad4ac1 feat: improve account appeal notifications (#2520)
* feat: improve account appeal notifications

Adds Resend-backed account-action emails for bans/restores, public-safe ban reason summaries, scanner-triggered appeal scan guidance, and inline banned-account sign-in appeal messaging.

Autoreview findings addressed:

- [P2] Wire restored-account email into unban flow

- [P2] Add the scan upload test to the CI selection

* feat: quarantine malicious artifact uploads

* fix: warn on repeated malicious rejections

* fix: route banned sign-ins to appeal page

* feat: add stored scan report downloads

* chore: bump clawhub cli to 0.20.0

* test: cover malicious skill ban journey

* test: align deletion cleanup expectations after merge

* fix: hide deleted account packages before cleanup

* fix: finalize appeals moderation flow
2026-06-06 16:55:23 -07:00
Patrick Erichsen 8f7c21ac02 test: cover account recreation after deletion (#2540) 2026-06-06 16:47:19 -07:00
Patrick Erichsen 999131e2e7 fix: purge auth-locked deleted accounts
* fix: purge auth-locked deleted accounts

* test: wait for account deletion resources

* fix: mark packages inactive during deletion purge

* test: expect package soft delete before purge

* test: ignore post-delete auth timeout noise
2026-06-06 16:07:19 -07:00
Patrick Erichsen 571cc70c7a test: limit local-auth playwright retries (#2538) 2026-06-06 14:58:18 -07:00
Patrick Erichsen 3ead985f82 fix: restore post-merge CI gates (#2537) 2026-06-06 14:50:46 -07:00
Patrick Erichsen 1117aa4340 feat: add cohort temporal abuse scoring (#2536) 2026-06-06 14:26:46 -07:00
Patrick Erichsen 9ce5e702b5 feat: hard delete account-owned resources (#2533)
* feat: hard delete account-owned resources

* fix: expose account recovery purge dry-run candidates
2026-06-06 14:17:50 -07:00
Patrick Erichsen c632b697bb fix: hide banned user publisher profiles (#2528)
* fix: hide banned user publisher profiles

* docs: require real ClawHub screenshot proof

* test: stabilize local auth profile visibility checks
2026-06-05 17:33:03 -07:00
Patrick Erichsen d2525b179a fix: stop copying publisher ids to skill embeddings (#2532) 2026-06-05 17:28:55 -07:00
Patrick Erichsen 8ebae39140 feat: add temporal publisher abuse scan (#2531) 2026-06-05 17:14:57 -07:00
Patrick Erichsen 821494b9dd fix(cli): accept legacy verify json flag
Accept legacy skill verify --json usage as a hidden compatibility no-op, add a regression e2e for the flattened verifier response, and prepare clawhub@0.19.2.
2026-06-05 16:30:19 -07:00
Patrick Erichsen 94413a60fb feat: show plugin downloads in lists
Show plugin download counts in lists, add downloads sorting, and remove plugin family badges from plugin list/grid rows.
2026-06-05 16:18:00 -07:00
Patrick Erichsen 9cbf98297c fix: exclude official orgs from publisher abuse (#2527) 2026-06-05 15:59:10 -07:00
Patrick Erichsen 94ded18dec feat: add org deletion cascade
Closes #2516.
2026-06-05 14:52:31 -07:00
Patrick Erichsen 9897850074 fix: add targeted legacy publisher ownership repair
Adds a targeted legacy owner-publisher projection repair mutation for one user and covers the repair path in maintenance tests.
2026-06-05 14:24:29 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 159118dc59 build(deps): bump the github-actions group with 3 updates (#2473)
Bumps the github-actions group with 3 updates: [useblacksmith/begin-testbox](https://github.com/useblacksmith/begin-testbox), [github/codeql-action](https://github.com/github/codeql-action) and [trufflesecurity/trufflehog](https://github.com/trufflesecurity/trufflehog).


Updates `useblacksmith/begin-testbox` from d0e04585c26905fdd92c94a09c159544c7ee1b67 to 233448af4bfdc6fca509a7f0974411ac6d8a8043
- [Commits](https://github.com/useblacksmith/begin-testbox/compare/d0e04585c26905fdd92c94a09c159544c7ee1b67...233448af4bfdc6fca509a7f0974411ac6d8a8043)

Updates `github/codeql-action` from 4.36.0 to 4.36.1
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/7211b7c8077ea37d8641b6271f6a365a22a5fbfa...87557b9c84dde89fdd9b10e88954ac2f4248e463)

Updates `trufflesecurity/trufflehog` from 3.95.3 to 3.95.5
- [Release notes](https://github.com/trufflesecurity/trufflehog/releases)
- [Commits](https://github.com/trufflesecurity/trufflehog/compare/v3.95.3...v3.95.5)

---
updated-dependencies:
- dependency-name: useblacksmith/begin-testbox
  dependency-version: 233448af4bfdc6fca509a7f0974411ac6d8a8043
  dependency-type: direct:production
  dependency-group: github-actions
- dependency-name: github/codeql-action
  dependency-version: 4.36.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: trufflesecurity/trufflehog
  dependency-version: 3.95.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-05 14:11:39 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 843f19e640 build(deps): bump commander from 14.0.3 to 15.0.0 (#2476)
Bumps [commander](https://github.com/tj/commander.js) from 14.0.3 to 15.0.0.
- [Release notes](https://github.com/tj/commander.js/releases)
- [Changelog](https://github.com/tj/commander.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tj/commander.js/compare/v14.0.3...v15.0.0)

---
updated-dependencies:
- dependency-name: commander
  dependency-version: 15.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-05 14:07:07 -07:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Peter Steinberger
4b882b2f43 build(deps): bump the production-minor-and-patch group across 1 directory with 12 updates (#2510)
* build(deps): bump the production-minor-and-patch group across 1 directory with 12 updates

Bumps the production-minor-and-patch group with 12 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@convex-dev/auth](https://github.com/get-convex/convex-auth) | `0.0.92` | `0.0.93` |
| [@shikijs/rehype](https://github.com/shikijs/shiki/tree/HEAD/packages/rehype) | `4.1.0` | `4.2.0` |
| [@tanstack/react-router](https://github.com/TanStack/router/tree/HEAD/packages/react-router) | `1.170.8` | `1.170.12` |
| [@tanstack/react-start](https://github.com/TanStack/router/tree/HEAD/packages/react-start) | `1.168.13` | `1.168.21` |
| [convex](https://github.com/get-convex/convex-backend/tree/HEAD/npm-packages/convex) | `1.39.1` | `1.40.0` |
| [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `1.16.0` | `1.17.0` |
| [react](https://github.com/facebook/react/tree/HEAD/packages/react) | `19.2.6` | `19.2.7` |
| [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) | `19.2.6` | `19.2.7` |
| [semver](https://github.com/npm/node-semver) | `7.8.1` | `7.8.2` |
| [shiki](https://github.com/shikijs/shiki/tree/HEAD/packages/shiki) | `4.1.0` | `4.2.0` |
| [undici](https://github.com/nodejs/undici) | `7.26.0` | `7.27.1` |
| [@clack/prompts](https://github.com/bombshell-dev/clack/tree/HEAD/packages/prompts) | `1.4.0` | `1.5.1` |



Updates `@convex-dev/auth` from 0.0.92 to 0.0.93
- [Changelog](https://github.com/get-convex/convex-auth/blob/main/CHANGELOG.md)
- [Commits](https://github.com/get-convex/convex-auth/compare/v0.0.92...v0.0.93)

Updates `@shikijs/rehype` from 4.1.0 to 4.2.0
- [Release notes](https://github.com/shikijs/shiki/releases)
- [Commits](https://github.com/shikijs/shiki/commits/v4.2.0/packages/rehype)

Updates `@tanstack/react-router` from 1.170.8 to 1.170.12
- [Release notes](https://github.com/TanStack/router/releases)
- [Changelog](https://github.com/TanStack/router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/TanStack/router/commits/@tanstack/react-router@1.170.12/packages/react-router)

Updates `@tanstack/react-start` from 1.168.13 to 1.168.21
- [Release notes](https://github.com/TanStack/router/releases)
- [Changelog](https://github.com/TanStack/router/blob/main/packages/react-start/CHANGELOG.md)
- [Commits](https://github.com/TanStack/router/commits/@tanstack/react-start@1.168.21/packages/react-start)

Updates `convex` from 1.39.1 to 1.40.0
- [Release notes](https://github.com/get-convex/convex-backend/releases)
- [Changelog](https://github.com/get-convex/convex-backend/blob/main/npm-packages/convex/CHANGELOG.md)
- [Commits](https://github.com/get-convex/convex-backend/commits/HEAD/npm-packages/convex)

Updates `lucide-react` from 1.16.0 to 1.17.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.17.0/packages/lucide-react)

Updates `react` from 19.2.6 to 19.2.7
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.7/packages/react)

Updates `react-dom` from 19.2.6 to 19.2.7
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.7/packages/react-dom)

Updates `semver` from 7.8.1 to 7.8.2
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v7.8.1...v7.8.2)

Updates `shiki` from 4.1.0 to 4.2.0
- [Release notes](https://github.com/shikijs/shiki/releases)
- [Commits](https://github.com/shikijs/shiki/commits/v4.2.0/packages/shiki)

Updates `undici` from 7.26.0 to 7.27.1
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v7.26.0...v7.27.1)

Updates `@clack/prompts` from 1.4.0 to 1.5.1
- [Release notes](https://github.com/bombshell-dev/clack/releases)
- [Changelog](https://github.com/bombshell-dev/clack/blob/main/packages/prompts/CHANGELOG.md)
- [Commits](https://github.com/bombshell-dev/clack/commits/@clack/prompts@1.5.1/packages/prompts)

---
updated-dependencies:
- dependency-name: "@clack/prompts"
  dependency-version: 1.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
- dependency-name: "@convex-dev/auth"
  dependency-version: 0.0.93
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@shikijs/rehype"
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
- dependency-name: "@tanstack/react-router"
  dependency-version: 1.170.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: "@tanstack/react-start"
  dependency-version: 1.168.20
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: convex
  dependency-version: 1.40.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
- dependency-name: lucide-react
  dependency-version: 1.17.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
- dependency-name: react
  dependency-version: 19.2.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: react-dom
  dependency-version: 19.2.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: semver
  dependency-version: 7.8.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: shiki
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
- dependency-name: undici
  dependency-version: 7.27.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* ci: update convex local auth runner

* ci: keep local auth deployment marker prefixed

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-06-05 13:50:43 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 634667c2c8 build(deps-dev): bump the development-minor-and-patch group across 1 directory with 5 updates (#2475)
Bumps the development-minor-and-patch group with 5 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) | `4.1.7` | `4.1.8` |
| [oxfmt](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt) | `0.51.0` | `0.53.0` |
| [oxlint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint) | `1.66.0` | `1.68.0` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `8.0.14` | `8.0.16` |
| [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.7` | `4.1.8` |



Updates `@vitest/coverage-v8` from 4.1.7 to 4.1.8
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.8/packages/coverage-v8)

Updates `oxfmt` from 0.51.0 to 0.53.0
- [Release notes](https://github.com/oxc-project/oxc/releases)
- [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxfmt/CHANGELOG.md)
- [Commits](https://github.com/oxc-project/oxc/commits/oxfmt_v0.53.0/npm/oxfmt)

Updates `oxlint` from 1.66.0 to 1.68.0
- [Release notes](https://github.com/oxc-project/oxc/releases)
- [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint/CHANGELOG.md)
- [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.68.0/npm/oxlint)

Updates `vite` from 8.0.14 to 8.0.16
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.0.16/packages/vite)

Updates `vitest` from 4.1.7 to 4.1.8
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.8/packages/vitest)

---
updated-dependencies:
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 4.1.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-minor-and-patch
- dependency-name: oxfmt
  dependency-version: 0.53.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-minor-and-patch
- dependency-name: oxlint
  dependency-version: 1.68.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-minor-and-patch
- dependency-name: vite
  dependency-version: 8.0.16
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-minor-and-patch
- dependency-name: vitest
  dependency-version: 4.1.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-05 13:26:14 -07:00
Patrick Erichsen bd30b182d7 feat: show skill scan queue progress 2026-06-05 11:35:08 -07:00
Patrick Erichsen 2d719feef5 fix: sync github status through skill digests 2026-06-04 21:02:54 -07:00
Patrick Erichsen d3e1059266 fix: remove org badge from publisher profiles 2026-06-04 21:02:54 -07:00
Patrick Erichsen 1a968d8243 fix: clean up publisher catalog rows (#2513)
* fix: clean up publisher catalog rows

* fix: type github sync soft deletes
2026-06-04 20:21:59 -07:00
Patrick Erichsen 59667cc1a5 fix: keep pending github skills visible (#2514) 2026-06-04 20:20:13 -07:00
Patrick Erichsen 5b0dd96530 fix: soft delete removed github skills (#2512) 2026-06-04 19:46:57 -07:00
Patrick Erichsen 8e3858a31d chore: release clawhub cli 0.19.1
Release clawhub CLI 0.19.1 so npm includes GitHub-backed skill install support.
2026-06-04 19:26:34 -07:00
Patrick Erichsen fe4acc8e10 feat: surface github sync issues (#2509) 2026-06-04 19:15:39 -07:00
Patrick Erichsen f2e6e87756 test: gate public routes in ci smoke (#2508) 2026-06-04 19:09:39 -07:00
Patrick Erichsen 3de69919de Add official org selector to GitHub sync settings (#2505)
* feat: select official org for github sync sources

* fix: rename github sync publisher field
2026-06-04 19:08:23 -07:00
Patrick Erichsen 7629433248 fix: skip publisher handle conflicts in ownership repair (#2507) 2026-06-04 19:06:38 -07:00
Patrick Erichsen b82ad43eae fix: align org publisher catalog tabs (#2506) 2026-06-04 19:05:33 -07:00
Patrick Erichsen b7ee5c8e62 fix: backfill legacy publisher ownership (#2503)
* fix: backfill legacy publisher ownership

* test: update publisher official mocks
2026-06-04 18:57:42 -07:00
Patrick Erichsen 1c8543ade6 feat: add official org mod commands (#2504) 2026-06-04 18:47:12 -07:00
Patrick Erichsen 7cd0ef362d fix: select publisher previews by installs (#2502) 2026-06-04 18:39:47 -07:00
Patrick Erichsen 9cf9e12bdd GitHub-backed skill source sync (#2498)
* feat: add github-backed skill seed schema

* feat: render github-backed skill manifests

* feat: add github-backed skill install resolver

* feat: add github-backed skill source sync

* fix: handle github resolver blocks

* fix: harden github source sync

* fix: expose github install blocks

* feat: support GitHub-backed skill sync

* fix: handle blocked github skill content

* fix: harden github sync rollout

* fix: bound github sync payloads

* feat: allow forcing pending github skill installs

* fix: remove official publisher seed rollout

* chore: format clawhub e2e test
2026-06-04 18:20:43 -07:00
Patrick Erichsen 112e25e28c fix: retry incomplete github sign-ins (#2501) 2026-06-04 17:18:32 -07:00
Jesse Merhi d786374b77 feat: add shared download metering (#2485) 2026-06-04 16:48:08 -07:00
Jesse Merhi 3fbe27560e feat: add publisher abuse review dashboard (#2471) 2026-06-05 09:05:50 +10:00
Patrick Erichsen 33539b6df3 fix: point account recovery to github issues (#2499) 2026-06-04 14:41:52 -07:00
clawsweeper[bot]clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>momothemage
354f12a033 fix(httpApiV1): surface 400-class delete validation errors instead of opaque 500 (#2496)
Summary:
- The branch narrows the soft-delete bad-request substring whitelist from any `reserved` message to the ClawHu ... rvation phrase and adds regression tests for the intended 400 path and an unrelated reserved-word 500 path.
- Reproducibility: yes. by source inspection: current main maps any cleaned soft-delete error containing `rese ...  while preserving the package route-reservation case. I did not execute tests during this read-only review.

Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.

Validation:
- ClawSweeper review passed for head a58687c54e.
- Required merge gates passed before the squash merge.

Prepared head SHA: a58687c54e
Review: https://github.com/openclaw/clawhub/pull/2496#issuecomment-4620039541

Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: momothemage
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
2026-06-04 07:43:22 +00:00
Momo b0ea80df6c fix(httpApiV1): surface 400-class delete validation errors instead of opaque 500 (#2488)
Merged via squash.

Prepared head SHA: cc6ac9d31a
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Reviewed-by: @momothemage
2026-06-04 15:32:30 +08:00
Patrick Erichsen 909a47106e chore: bump clawhub cli to 0.19.0 (#2486) 2026-06-03 16:19:42 -07:00
Patrick Erichsen e8cfbddf17 feat: add clawhub scan command (#2479)
* docs: design clawhub scan command

* docs: plan clawhub scan command

* feat: add clawhub scan command

* fix: prune ephemeral scan uploads

* fix: avoid scan route slug collision
2026-06-03 16:05:11 -07:00
Patrick Erichsen 162528abe4 feat: show plugin download counts (#2484) 2026-06-03 14:46:20 -07:00
Vyctor H. Brzezowski 74aa61086e fix: gate local Codex workers (#2472) 2026-06-03 12:26:20 -07:00
Momo 858a121d33 fix(plugins): rewrite relative README image URLs to source-host raw URLs (#2412)
Merged via squash.

Prepared head SHA: 545db16f01
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Reviewed-by: @momothemage
2026-06-03 17:05:58 +08:00
Patrick Erichsen 953358a322 fix: use GitHub App auth for GitHub account lookups
* fix: use GitHub App auth for GitHub account lookups

* style: format GitHub account age backfill
2026-06-03 00:24:23 -07:00
Patrick Erichsen 0a79612fe5 feat: add security audit download export (#2477) 2026-06-02 22:20:05 -07:00
Peter Steinberger 9b5d2e088d fix: speed GitHub account-age backfill scan 2026-06-02 13:01:59 +01:00
Peter Steinberger ce62df9d08 fix: batch GitHub account-age backfill writes 2026-06-02 11:52:21 +01:00
Peter Steinberger 0abdbf4a50 fix: harden GitHub account age lookup 2026-06-02 11:32:16 +01:00
Jesse Merhi dcbc38999f fix: require multipart package publishes
Require multipart package publishes so public clients cannot provide trusted file or artifact metadata.

Preserve released CLI multipart field names (`files` and `clawpack`), derive stored file and npm-pack artifact metadata from uploaded bytes, and keep large ClawPack tarballs working through ticketed staged uploads.

Tests:
- bunx vitest run convex/uploads.test.ts --reporter=dot
- bunx vitest run convex/httpApi.handlers.test.ts convex/httpApiV1.handlers.test.ts --testNamePattern "package publish|multipart package publish|multipart ClawPack|staged ClawPack|browser session auth|cliUploadUrl|upload tickets" --reporter=dot
- bun run --cwd packages/clawhub test:src -- src/cli/commands/packages.test.ts --testNamePattern "ClawPack tarballs over|stages ClawPack|publishes a ClawPack|cleans generated ClawPack"
- bun run format:check
- bun run lint
- bunx tsc -p packages/schema/tsconfig.json --noEmit --pretty false
- bunx tsc -p packages/clawhub/tsconfig.json --noEmit --pretty false
- bunx tsc --noEmit --pretty false
- bun run ci:unit
- git diff --check origin/main
2026-06-02 09:59:53 +10:00
Jesse Merhi 01aa28ccda feat: make recommended the default skill ranking
Make Recommended the default public skill ranking while preserving the v1 API no-sort default. Adds recommended/default API support, digest rank indexes/backfill safety, OpenAPI/docs updates, and regression coverage.
2026-06-02 08:20:37 +10:00
Jesse Merhi cb6ced7906 fix: keep skill card metadata grouped (#2465)
Move skill card tag/platform pills below the description and keep author/update/stat metadata grouped at the bottom of grid cards.

Thanks @jesse-merhi.
2026-06-02 05:02:40 +10:00
Vyctor H. Brzezowski ded9ff4235 fix: stabilize auth loading states (#2447) 2026-06-01 10:01:04 -07:00
Val AlexanderandBunsDev 9fc2da4dc4 fix: clarify paginated plugin count (#2393)
* fix: clarify paginated plugin count

* fix: correct shown-count pluralization

Agent-Logs-Url: https://github.com/openclaw/clawhub/sessions/32575bd4-9dc7-4b8f-9536-bdc96f008783

Co-authored-by: BunsDev <68980965+BunsDev@users.noreply.github.com>
2026-05-31 01:00:02 -05:00
Patrick Erichsen 05d5fc1151 docs: explain skill catalog publishing (#2455)
* docs: explain skill publishing workflow

* docs: polish ClawHub publishing guide
2026-05-30 22:14:59 -05:00
Patrick Erichsen 9aaab158cb feat(cli): print stored auth token (#2454) 2026-05-30 21:11:51 -05:00
Patrick Erichsen 6fc5bb7cd8 feat: add reusable skill publish workflow (#2450)
* feat: add reusable skill publish workflow

* test: cover exact skill sync roots

* fix: keep skill workflow scans exact
2026-05-30 20:26:51 -05:00
Patrick Erichsen 9aa3f37ee1 feat: export skill bundle content for security datasets (#2448) 2026-05-30 12:11:19 -05:00
Patrick Erichsen 9a20795b54 feat: add NVIDIA as official publisher (#2445) 2026-05-30 00:37:40 -05:00
Patrick Erichsen ff75a7e9ae fix(auth): avoid false sign-in failure on redirect (#2443) 2026-05-29 16:13:19 -05:00
Patrick Erichsen 4c965f4957 fix: repair pending vt skill versions (#2442) 2026-05-29 16:10:36 -05:00
Patrick Erichsen f71139e9ae feat: add VT pending repair command (#2441) 2026-05-29 15:07:29 -05:00
ShadowandPatrick Erichsen a20e2efd68 feat: add ban appeals service endpoints (#2408)
Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-05-29 14:55:56 -05:00
Dallin Romney 83cc4d0f87 fix: surface organization creation errors (#2440) 2026-05-29 14:39:44 -05:00
Peter Steinberger 1f7f483b1d fix: route diffs language pack slug 2026-05-29 19:45:51 +01:00
Vyctor H. Brzezowski 309723f7a8 fix(cli): report unknown root commands (#2433) 2026-05-29 11:03:53 -05:00
Peter Steinberger 23932ec7de fix(packages): store npm pack entries sequentially 2026-05-29 15:21:55 +01:00
Peter Steinberger b292f7eaf5 fix(auth): let admins publish without github age lookup 2026-05-29 13:41:49 +01:00
Peter Steinberger cbbb6e7a61 fix(packages): allow large npm pack entries over HTTP 2026-05-29 13:36:54 +01:00
Peter Steinberger 42e9690e78 fix(packages): allow large files inside npm pack artifacts 2026-05-29 13:32:05 +01:00
Patrick Erichsen ff48b2cc70 feat!: remove ClawScan note feature (#2432)
BREAKING CHANGE: ClawScan publisher notes are no longer accepted by publish APIs, CLI commands, schema packages, or UI flows.
2026-05-28 16:00:45 -05:00
Peter Steinberger 562810f29b docs: position README banner 2026-05-28 20:47:46 +01:00
Peter Steinberger 327231535f docs: add README banner 2026-05-28 19:43:01 +01:00
Patrick Erichsen 51967bca7f feat: export skillspector issue details (#2430) 2026-05-28 10:32:28 -05:00
Patrick Erichsen 0132f1f530 fix: preserve Codex diagnostic messages (#2429)
* fix: remove global ClawScan claim ceiling

* fix: preserve codex diagnostic messages
2026-05-28 08:46:13 -05:00
Patrick Erichsen 05f27f640e feat: export skillspector dataset signals (#2424) 2026-05-28 08:40:54 -05:00
Vyctor H. Brzezowski 6adf379f32 feat: add publish flow guidance shortcuts (#2423)
* feat: add publish flow guidance shortcuts

* style: polish publish follow-up surfaces

* fix: route soul publish guide link
2026-05-27 20:55:43 -05:00
Patrick Erichsen 7d6efae74b fix: remove global ClawScan claim ceiling (#2422) 2026-05-27 17:35:48 -05:00
Vyctor H. BrzezowskiandPatrick Erichsen d854449610 fix: restrict membership management to org publishers (#2285)
* fix: restrict membership management to org publishers

* fix(api): ignore stale personal publisher memberships

* fix(api): reject stale personal publisher publish targets

* fix(api): reject stale personal publisher memberships

* fix(api): use personal publisher links for package access

* fix(api): guard personal publisher owner scopes

* fix: enforce publisher ownership for skill reads

* fix: narrow personal publisher dashboard owner

* fix: ignore stale personal package memberships

* fix: allow own legacy personal skill destination

* fix: preserve legacy personal package dashboards

* fix: preserve legacy personal skill dashboards

* fix: avoid redundant personal publisher boolean coercion

* fix: preserve legacy personal publisher access

* fix: include legacy direct packages in personal dashboard

* fix: close stale personal publisher ownership gaps

---------

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-05-27 14:34:13 -07:00
Vyctor H. BrzezowskiandPatrick Erichsen 87f2b846ef fix: hide package resources when owners are banned (#2283)
* fix: hide package resources when owners are banned

* fix(api): attribute unban package restores to moderator

* fix(web): clarify package effects in ban confirmations

* fix(api): continue package ban batches during in-flight bans

* fix: keep package unban restore scoped to ban batch

* fix: retimestamp package releases during repeated bans

* fix: start package ban batches after user ban commit

* fix: preserve manual package moderation after unban

* fix: cover personal publisher package sanctions

* fix: restore personal publisher packages in autoban remediation

* fix: bound package publish token revocation batches

* fix: clear package ban reason during remediation restore

* fix: block direct package ban restores

* fix: scan linked personal publisher packages during sanctions

* fix: tighten package sanction restore batches

* fix: block personal publisher publishes after owner ban

* fix: allow initial package ban cleanup before commit

---------

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-05-27 14:17:39 -07:00
Vyctor H. BrzezowskiandPatrick Erichsen 97023d3123 fix(api): guard moderated skill files and tags (#2287)
* fix(api): guard moderated skill files and tags

* fix(api): guard public list latest version ownership

* fix(api): guard stale latest version outputs

* fix(api): keep legacy digest latest versions

* fix(api): guard public latest-version readers

* fix: avoid ambiguous array allocation in skill export

* fix: drop legacy markerless digest versions

* fix: verify markerless digest versions

* test: mark package catalog digest versions

* fix: keep skill list tag resolution on digest path

* test: mark resolved skill versions with owner

* fix: repair digest capability backfill skip

---------

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-05-27 13:34:54 -07:00
Patrick Erichsen a920323a86 ci: fan out skill card worker (#2419) 2026-05-27 14:50:18 -05:00
Patrick Erichsen b8eaada68d fix: frame skillspector findings as advisory (#2418) 2026-05-27 14:29:38 -05:00
Vyctor H. BrzezowskiandPatrick Erichsen 18acbc1209 fix: abort stale unban skill restore batches (#2284)
* fix: abort stale unban skill restore batches

* fix(api): keep ban restore markers monotonic

* fix(api): restore legacy ban-hidden skills on unban

* fix: retimestamp legacy ban-hidden skills on re-ban

* fix: abort stale scheduled skill ban pages

---------

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-05-27 12:14:52 -07:00
Patrick Erichsen 57bc9f2a46 feat: add bulk skill rescan admin tool (#2413)
* feat: add bulk skill rescan backend queue

* feat: expose bulk skill rescan admin API

* feat: add bulk skill rescan mod command

* style: format bulk rescan changes

* fix: print bulk rescan cli progress

* fix: include legacy active skills in bulk rescans
2026-05-27 14:03:03 -05:00
Vyctor H. BrzezowskiandPatrick Erichsen 6f893b54f4 fix: block direct skill transfers under moderation (#2282)
* fix: block direct skill transfers under moderation

* fix(api): block accepted transfers for moderated skills

* fix(api): block malware-flagged skill transfers

* fix: cover moderated skill transfer bypasses

* test: support ownership heal transfer sync

* fix: close moderated transfer backfill gaps

* fix: block soft-deleted transfer guard state

---------

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-05-27 12:01:51 -07:00
Patrick Erichsen 8bb6a0d584 chore: remove repo-local generic skills (#2417) 2026-05-27 13:48:05 -05:00
Vyctor H. Brzezowski 321df223b2 fix(web): polish skill and plugin upload forms (#2415)
* feat: add shared publish form UI pieces

* fix: improve skill publish form UX

* fix: align plugin publish form UX

* fix: refine publish form polish follow-ups

* fix: refine publish upload states

* fix(web): finalize publish upload polish

* test: use ClawHub logo for owner avatar fixture

* fix(web): avoid premature skill publish field errors

* test: adapt local auth publish flow to owner picker

* fix(web): keep plugin publish locked after success
2026-05-27 11:10:50 -07:00
Vyctor H. Brzezowski 707d390923 fix: revalidate package publish owners before insert (#2281)
* fix: revalidate package publish owners before insert

* fix(api): recheck package publisher membership before insert
2026-05-27 11:03:11 -07:00
Patrick Erichsen 3c4608156c fix(cli): remove redundant skill verify json flag (#2401) 2026-05-27 12:52:15 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 9c97d643ac build(deps): bump github/codeql-action (#2306)
Bumps the github-actions group with 1 update in the / directory: [github/codeql-action](https://github.com/github/codeql-action).


Updates `github/codeql-action` from 4.35.4 to 4.36.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/68bde559dea0fdcac2102bfdf6230c5f70eb485e...7211b7c8077ea37d8641b6271f6a365a22a5fbfa)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.35.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-27 12:36:25 -05:00
Patrick Erichsen a66df774f2 feat: make UI proof full-stack by default (#2210)
* feat: add full-stack UI proof backend

* docs: prefer here.now links for issue screenshots
2026-05-27 12:33:09 -05:00
Patrick Erichsen 30bf8f252a feat: add org member removal to clawhub-mod (#2416) 2026-05-27 12:27:16 -05:00
Patrick Erichsen 5ed0ddd066 fix: keep mod org creation actor out of members (#2360) 2026-05-27 12:03:29 -05:00
Jesse Merhi ce1be46c60 docs: clarify security report scope (#2410) 2026-05-27 14:06:25 +10:00
Patrick Erichsen 667bc55299 Add bulk skill security verdicts endpoint (#2404)
* feat: add bulk skill security verdicts endpoint

* fix: keep bulk verdict lookup lightweight

* fix: harden bulk verdict review findings
2026-05-26 10:05:56 -07:00
Jesse Merhi 90de729fe1 fix: simplify official publisher policy (#2380) 2026-05-26 21:44:43 +10:00
Jesse Merhi 8a2c0c06fd Add publisher abuse dry run (#2381)
* feat: add publisher abuse dry run

* chore: remove publisher abuse management UI

* chore: remove unused management styles

* fix: harden publisher abuse review queue

* fix: refresh passing abuse nominations

* fix: bound abuse queue to actionable labels

* fix: keep abuse runs model-consistent

* fix: ignore zero-skill publishers in abuse scoring

* fix: expand filtered abuse queue scan

* fix: score publisher abuse with skill stats

* fix: harden abuse scoring cohort stats

* fix: bound publisher abuse score runs

* fix: keep abuse scoring compatible with existing publisher stats

* fix: recompute missing publisher skill totals

* fix: avoid aggregate fallback for mixed publisher abuse stats

* fix: harden publisher abuse score runs

* fix: derive missing publisher abuse skill counts

* fix: handle unknown abuse publisher package counts

* fix: reopen actionable abuse nominations

* fix: bound abuse cron fallback scoring

* fix: keep publisher stat deltas bounded

* fix: skip unknown-base cron abuse scores

* fix: skip mixed unknown cron abuse scores

* fix: bound manual abuse fallback scoring

* fix: prevent overlapping abuse score runs

* fix: preserve abuse triage notes

* fix: score legacy mixed publishers in abuse cron

* fix: keep publisher abuse dry run internal
2026-05-26 14:35:29 +10:00
Patrick Erichsen 07fed45f42 fix: reshape skill verify security signals (#2402) 2026-05-25 18:49:56 -07:00
Patrick Erichsen cc16d7fbd9 fix: route static scan findings through clawscan (#2398)
* fix: route static scan findings through clawscan

* fix: preserve clawscan-only security verdicts after merge
2026-05-25 18:33:33 -07:00
Patrick Erichsen 1f56a71430 fix: stabilize local-auth publish lifecycle e2e (#2400) 2026-05-25 18:04:35 -07:00
Patrick Erichsen 875f026a23 chore(release): prepare clawhub cli 0.18.0
Bump the clawhub CLI package to 0.18.0 and add release notes for skill verification.
2026-05-25 17:25:02 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 4248f61926 build(deps-dev): bump the development-minor-and-patch group across 1 directory with 10 updates (#2395)
Bumps the development-minor-and-patch group with 10 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@tanstack/devtools-vite](https://github.com/TanStack/devtools/tree/HEAD/packages/devtools-vite) | `0.6.0` | `0.7.0` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `25.7.0` | `25.9.1` |
| [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) | `19.2.14` | `19.2.15` |
| [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react) | `6.0.1` | `6.0.2` |
| [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) | `4.1.6` | `4.1.7` |
| [oxfmt](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt) | `0.49.0` | `0.51.0` |
| [oxlint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint) | `1.64.0` | `1.66.0` |
| [oxlint-tsgolint](https://github.com/oxc-project/tsgolint) | `0.22.1` | `0.23.0` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `8.0.12` | `8.0.14` |
| [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.6` | `4.1.7` |



Updates `@tanstack/devtools-vite` from 0.6.0 to 0.7.0
- [Release notes](https://github.com/TanStack/devtools/releases)
- [Changelog](https://github.com/TanStack/devtools/blob/main/packages/devtools-vite/CHANGELOG.md)
- [Commits](https://github.com/TanStack/devtools/commits/@tanstack/devtools-vite@0.7.0/packages/devtools-vite)

Updates `@types/node` from 25.7.0 to 25.9.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@types/react` from 19.2.14 to 19.2.15
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

Updates `@vitejs/plugin-react` from 6.0.1 to 6.0.2
- [Release notes](https://github.com/vitejs/vite-plugin-react/releases)
- [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite-plugin-react/commits/plugin-react@6.0.2/packages/plugin-react)

Updates `@vitest/coverage-v8` from 4.1.6 to 4.1.7
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.7/packages/coverage-v8)

Updates `oxfmt` from 0.49.0 to 0.51.0
- [Release notes](https://github.com/oxc-project/oxc/releases)
- [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxfmt/CHANGELOG.md)
- [Commits](https://github.com/oxc-project/oxc/commits/oxfmt_v0.51.0/npm/oxfmt)

Updates `oxlint` from 1.64.0 to 1.66.0
- [Release notes](https://github.com/oxc-project/oxc/releases)
- [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint/CHANGELOG.md)
- [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.66.0/npm/oxlint)

Updates `oxlint-tsgolint` from 0.22.1 to 0.23.0
- [Release notes](https://github.com/oxc-project/tsgolint/releases)
- [Commits](https://github.com/oxc-project/tsgolint/compare/v0.22.1...v0.23.0)

Updates `vite` from 8.0.12 to 8.0.14
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.0.14/packages/vite)

Updates `vitest` from 4.1.6 to 4.1.7
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.7/packages/vitest)

---
updated-dependencies:
- dependency-name: "@tanstack/devtools-vite"
  dependency-version: 0.7.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-minor-and-patch
- dependency-name: "@types/node"
  dependency-version: 25.9.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-minor-and-patch
- dependency-name: "@types/react"
  dependency-version: 19.2.15
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-minor-and-patch
- dependency-name: "@vitejs/plugin-react"
  dependency-version: 6.0.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-minor-and-patch
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 4.1.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-minor-and-patch
- dependency-name: oxfmt
  dependency-version: 0.51.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-minor-and-patch
- dependency-name: oxlint
  dependency-version: 1.66.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-minor-and-patch
- dependency-name: oxlint-tsgolint
  dependency-version: 0.23.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-minor-and-patch
- dependency-name: vite
  dependency-version: 8.0.14
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-minor-and-patch
- dependency-name: vitest
  dependency-version: 4.1.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-25 17:19:04 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> aec03c016d build(deps): bump the production-minor-and-patch group across 1 directory with 10 updates (#2396)
Bumps the production-minor-and-patch group with 10 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@shikijs/rehype](https://github.com/shikijs/shiki/tree/HEAD/packages/rehype) | `4.0.2` | `4.1.0` |
| [@tanstack/react-router](https://github.com/TanStack/router/tree/HEAD/packages/react-router) | `1.169.2` | `1.170.8` |
| [@tanstack/react-start](https://github.com/TanStack/router/tree/HEAD/packages/react-start) | `1.167.65` | `1.168.13` |
| [convex](https://github.com/get-convex/convex-backend/tree/HEAD/npm-packages/convex) | `1.38.0` | `1.39.1` |
| [convex-helpers](https://github.com/get-convex/convex-helpers/tree/HEAD/packages/convex-helpers) | `0.1.116` | `0.1.118` |
| [fflate](https://github.com/101arrowz/fflate) | `0.8.2` | `0.8.3` |
| [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `1.14.0` | `1.16.0` |
| [semver](https://github.com/npm/node-semver) | `7.8.0` | `7.8.1` |
| [shiki](https://github.com/shikijs/shiki/tree/HEAD/packages/shiki) | `4.0.2` | `4.1.0` |
| [undici](https://github.com/nodejs/undici) | `7.25.0` | `7.26.0` |



Updates `@shikijs/rehype` from 4.0.2 to 4.1.0
- [Release notes](https://github.com/shikijs/shiki/releases)
- [Commits](https://github.com/shikijs/shiki/commits/v4.1.0/packages/rehype)

Updates `@tanstack/react-router` from 1.169.2 to 1.170.8
- [Release notes](https://github.com/TanStack/router/releases)
- [Changelog](https://github.com/TanStack/router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/TanStack/router/commits/@tanstack/react-router@1.170.8/packages/react-router)

Updates `@tanstack/react-start` from 1.167.65 to 1.168.13
- [Release notes](https://github.com/TanStack/router/releases)
- [Changelog](https://github.com/TanStack/router/blob/main/packages/react-start/CHANGELOG.md)
- [Commits](https://github.com/TanStack/router/commits/@tanstack/react-start@1.168.13/packages/react-start)

Updates `convex` from 1.38.0 to 1.39.1
- [Release notes](https://github.com/get-convex/convex-backend/releases)
- [Changelog](https://github.com/get-convex/convex-backend/blob/main/npm-packages/convex/CHANGELOG.md)
- [Commits](https://github.com/get-convex/convex-backend/commits/npm/1.39.1/npm-packages/convex)

Updates `convex-helpers` from 0.1.116 to 0.1.118
- [Changelog](https://github.com/get-convex/convex-helpers/blob/main/packages/convex-helpers/CHANGELOG.md)
- [Commits](https://github.com/get-convex/convex-helpers/commits/npm/0.1.118/packages/convex-helpers)

Updates `fflate` from 0.8.2 to 0.8.3
- [Release notes](https://github.com/101arrowz/fflate/releases)
- [Changelog](https://github.com/101arrowz/fflate/blob/master/CHANGELOG.md)
- [Commits](https://github.com/101arrowz/fflate/compare/v0.8.2...v0.8.3)

Updates `lucide-react` from 1.14.0 to 1.16.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.16.0/packages/lucide-react)

Updates `semver` from 7.8.0 to 7.8.1
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v7.8.0...v7.8.1)

Updates `shiki` from 4.0.2 to 4.1.0
- [Release notes](https://github.com/shikijs/shiki/releases)
- [Commits](https://github.com/shikijs/shiki/commits/v4.1.0/packages/shiki)

Updates `undici` from 7.25.0 to 7.26.0
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v7.25.0...v7.26.0)

---
updated-dependencies:
- dependency-name: "@shikijs/rehype"
  dependency-version: 4.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
- dependency-name: "@tanstack/react-router"
  dependency-version: 1.170.8
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
- dependency-name: "@tanstack/react-start"
  dependency-version: 1.168.13
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
- dependency-name: convex
  dependency-version: 1.39.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
- dependency-name: convex-helpers
  dependency-version: 0.1.118
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: fflate
  dependency-version: 0.8.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: lucide-react
  dependency-version: 1.16.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
- dependency-name: semver
  dependency-version: 7.8.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: shiki
  dependency-version: 4.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
- dependency-name: undici
  dependency-version: 7.26.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-25 17:10:26 -07:00
Patrick Erichsen b62d8ca813 feat: generate nvidia-style skill cards (#2382)
* feat: generate nvidia-style skill cards

* fix: harden skill card verification

* fix: use version-specific skill card evidence

* fix: close skill card verification gaps

* fix: refresh stale skill cards

* fix: protect skill card provenance fingerprints

* fix: close skill card review gaps

* fix: harden skill card trust boundaries

* fix: isolate skill card renderer

* fix: harden skill card workflows
2026-05-25 16:53:25 -07:00
Patrick Erichsen 01946864f2 feat: refine skillspector audit UI (#2397) 2026-05-25 16:02:19 -07:00
Patrick Erichsen 963b0a5719 fix: increase security scan worker throughput (#2389) 2026-05-23 18:56:52 -07:00
Patrick Erichsen 6a3c8551e8 fix: let codex adjudicate scan evidence (#2388) 2026-05-23 17:46:37 -07:00
Patrick Erichsen 1db8a6ca22 feat: support package security rescans (#2387) 2026-05-23 17:18:18 -07:00
Patrick Erichsen 2ad4068071 fix: streamline security audit agentic lanes (#2386)
* fix: streamline security audit agentic lanes

* fix: cover security audit rescan route mock

* fix: invert security audit meter scale
2026-05-23 16:41:16 -07:00
Patrick Erichsen 7446579772 fix: require scan worker artifact reads (#2385) 2026-05-23 16:26:56 -07:00
Patrick Erichsen c9e105fa34 fix: make security scan worker dispatchable (#2384) 2026-05-23 15:40:14 -07:00
Patrick Erichsen c538848a3d feat: use SkillSpector for agentic risk findings (#2383)
* feat: use SkillSpector for agentic risk findings

* fix: harden SkillSpector result handling
2026-05-23 15:31:58 -07:00
Peter Steinberger c145166287 fix: point docs auth to canonical host 2026-05-23 19:48:52 +01:00
Patrick Erichsen 0907fae0d9 fix: cap skill export page size (#2376) 2026-05-22 13:33:11 -07:00
Patrick Erichsen afd73264bd fix: log skill export failures (#2375) 2026-05-22 12:44:18 -07:00
e22bb7d427 Export skills function added by Mirror Site maintainer (#2138)
* feat: add GET /api/v1/skills/export for batch ZIP download

Add a new REST API endpoint that allows authenticated admin users to
export skills in bulk as a merged ZIP archive, designed for the ClawHub
China mirror site to efficiently sync skill data.

- New endpoint: GET /api/v1/skills/export?startDate=&endDate=&limit=&cursor=
- Admin-only auth via requireExportAuth (Bearer token + role check)
- Zip Slip protection: validateSlug + validateFilePath
- Duplicate ZIP path detection in buildMergedExportZip
- Per-skill metadata written to _export_skill_meta.json (avoids collision with skill files)
- Error recording: missing version/blob logged to _errors.json
- Dedicated rate limit tier: export { ip: 10, key: 60, adminKey: 600 }
- Cursor-based pagination on skillSearchDigest.by_active_created index
- Chunked parallel blob reads (50 concurrent)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: allow authenticated skill exports

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-05-22 12:22:16 -07:00
Momoandmomothemage 232017ba6f feat: surface "API key required" skill attribute (#2353)
Merged via squash.

Prepared head SHA: 94992fb6d1
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Reviewed-by: @momothemage
2026-05-22 16:42:05 +08:00
Tak Hoffman d897660b55 fix(ci): route ClawSweeper comment commands
Route ClawSweeper issue-comment commands through the dispatch workflow so maintainer @clawsweeper commands reach the repair comment router.
2026-05-21 23:36:07 -05:00
Onur Solmaz 5415940219 Merge pull request #2371 from openclaw/fix/publisher-title-clipping
fix: prevent publisher title clipping
2026-05-22 11:51:09 +08:00
Onur Solmaz bf2366fad2 fix: prevent publisher title clipping 2026-05-22 11:22:19 +08:00
Patrick Erichsen 28fb63cc67 fix: use generic 404 for missing skill pages (#2366) 2026-05-21 12:11:21 -07:00
Patrick Erichsen 77624dd70f fix: clarify package publish runtime errors (#2364) 2026-05-21 11:42:47 -07:00
Patrick Erichsen d3cd7a75cf fix: contain skill install command card (#2362) 2026-05-21 10:41:21 -07:00
Patrick Erichsen 7467cd88cc fix: prefer plugin manifest display names (#2361) 2026-05-21 10:23:33 -07:00
Patrick Erichsen b07196f336 feat: add org repair commands to clawhub-mod (#2359) 2026-05-21 08:21:04 -07:00
Patrick Erichsen 92dfd8f35a fix: use readme heading for package display fallback (#2358) 2026-05-21 07:59:32 -07:00
Patrick Erichsen 01e4418ccc fix: persist codex scan diagnostics (#2351) 2026-05-20 20:26:16 -07:00
Patrick Erichsen 64633c2644 fix: make header background opaque (#2352) 2026-05-20 20:04:00 -07:00
Patrick Erichsen 39107900ea fix: hide empty VirusTotal stats (#2350)
* fix: hide empty VirusTotal stats

* fix: avoid partial VirusTotal denominators
2026-05-20 19:58:27 -07:00
Vyctor H. BrzezowskiandPatrick Erichsen a54f240a08 feat: unify signed-out screens with SignInPrompt component (#2148)
Replace ad-hoc sign-in UI on /settings, /dashboard, /import, /stars,
/cli/auth and /docs/auth with a single SignInPrompt component that
mirrors the polished /settings design (gradient backdrop, blur, card
with shadow, LockKeyhole icon, styled GitHub sign-in button).

Also replaces inline 'Sign in to comment.' text in SoulDetailPage and
SkillCommentsPanel with a compact SignInButton size='sm'.

Adds SignInPrompt.test.tsx with 8 unit tests and -stars.test.tsx with
6 route-level tests.

Fixes stars.tsx loading logic so unauthenticated users see the prompt
immediately instead of a skeleton.

- bun run build: pass
- bun run format:check: pass
- bun run lint: pass
- bun run test: 1,758 tests pass

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-05-20 19:37:25 -07:00
Vyctor H. BrzezowskiandPatrick Erichsen 07bf41e109 fix(api): accept full skill list pagination cursors (#2275) (#2286)
Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-05-20 19:36:57 -07:00
Pragnyan Ramtha 5cc3e890fd fix(clawhub): cap embedding text by bytes (#2337) 2026-05-21 12:34:20 +10:00
Vyctor H. Brzezowski cb6f365bb5 fix(web): remove files tab height cap on desktop (#2217)
* fix(web): adjust files tab tree height and mobile disclosure

* fix(web): refine mobile see-all affordance in files tab

* fix(web): polish files preview empty state

* fix(web): add files preview header divider

* fix(web): polish files code preview styling

* fix(web): move files preview metadata to footer

* fix(web): keep files code preview scrollbars visible

* fix(web): tighten files preview footer divider

* fix(web): move files preview scrolling to viewer body

* fix(web): align files tab panel headers

* fix(web): align files panel header dividers

* fix(web): balance files panel header padding

* fix(web): match files preview header typography

* fix(web): precisely align files panel header dividers

* fix(web): split file row paths by filename

* fix(web): add contrast to files preview body

* fix(web): align files panel mobile breakpoint

* fix(web): polish mobile files see all overlay
2026-05-20 19:30:16 -07:00
Luke 9a2e8d6fea fix: clarify package sync and publish paths (#2235) 2026-05-20 17:52:54 -07:00
Pragnyan RamthaandPatrick Erichsen dcf42b0502 fix(clawhub): continue sync after slug conflicts (#2336)
* fix(clawhub): continue sync after slug conflicts

* fix(clawhub): handle locked sync slugs

* fix: continue sync after publish failures

---------

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-05-20 17:44:19 -07:00
Jesse Merhi 5f6b73024c fix: separate financial capability tags from crypto (#2344) 2026-05-20 17:07:39 -07:00
Patrick Erichsen 4e4d5c88d1 feat: consolidate security audit UI (#2349) 2026-05-20 15:33:35 -07:00
Jason (Json) 5be50db7ec feat: add category-aware related skills (#2270) 2026-05-20 15:02:45 -07:00
Patrick Erichsen 3c39480695 Fix undetected VirusTotal results staying pending (#2347)
* fix: increase clawscan worker throughput

* fix: treat undetected VT results as clean
2026-05-20 14:46:38 -07:00
Vyctor H. Brzezowski 526d84f338 fix: render CJK text in OG images (#2342) 2026-05-19 22:13:24 -07:00
Patrick Erichsen 9be35a3d43 feat: restore skill rescan moderation command (#2341) 2026-05-19 22:06:40 -07:00
ImLukeF e712ce7362 fix: increase mobile detail tab touch target 2026-05-20 14:55:01 +10:00
Patrick Erichsen 3c45326b88 fix: show VT undetected fallback as pass (#2338) 2026-05-19 21:22:12 -07:00
Patrick Erichsen d67583f075 chore(release): automate cli github releases (#2331) 2026-05-19 17:05:42 -07:00
Vyctor H. Brzezowski f23671bba9 docs: reconcile local dev worktree workflows (#2328) 2026-05-19 15:56:17 -07:00
Patrick Erichsen b753b1f7ab chore(release): prepare clawhub cli 0.17.0 (#2326) 2026-05-19 14:23:06 -07:00
Patrick Erichsen ffdd06a731 fix: increase clawscan worker throughput (#2327) 2026-05-19 14:20:37 -07:00
Patrick Erichsen 0b888a2d13 feat: add self-serve publisher creation (#2324) 2026-05-19 14:15:54 -07:00
Vyctor H. Brzezowski b8efe83d1b feat: refresh dynamic og images (#2180) 2026-05-19 13:37:36 -07:00
Patrick Erichsen c7935b6800 fix: cancel queued vt update scans (#2313) 2026-05-18 22:56:29 -07:00
Patrick Erichsen 4851f5f76d fix: add vt scan queue pruning helper (#2312) 2026-05-18 22:24:15 -07:00
Patrick Erichsen 970663d51d fix: remove virustotal code insight scans (#2311) 2026-05-18 22:22:42 -07:00
Patrick Erichsen a5ec1c71a3 fix: disable daily vt rescan (#2309) 2026-05-18 21:58:00 -07:00
Patrick Erichsen 66f3d07ca1 feat: add ban reclassification tool (#2305) 2026-05-18 18:42:59 -07:00
Patrick Erichsen 27f0b7d206 fix: paginate autoban remediation dry runs (#2304)
* fix: split autoban dry-run pagination

* fix: paginate autoban remediation dry runs
2026-05-18 18:06:06 -07:00
Patrick Erichsen 60aa4a77fd Fix synchronous skill star counts (#2301)
* fix: update skill star counts synchronously

* test: wait for stable local publish owner

* test: wait for local star persona on detail page
2026-05-18 17:11:23 -07:00
Patrick Erichsen 583a48db07 fix: split autoban dry-run pagination (#2303) 2026-05-18 17:02:30 -07:00
Patrick Erichsen 6814af95df feat: add autoban remediation command (#2302) 2026-05-18 16:28:52 -07:00
Patrick Erichsen 2aa2a449e5 chore: route worktree startup through Worktrunk (#2297)
* fix: add worktree preflight checks

* fix: remove worktree preflight spec note

* chore: compare worktrunk worktree startup

* fix: stop worktrunk dev process group

* chore: route worktree startup through worktrunk

* chore: keep dev seeding as project command

* chore: clarify worktrunk worktree script boundaries

* fix: keep worktree CI green after main merge
2026-05-18 14:02:14 -07:00
Patrick Erichsen 7cf16ebb28 fix: preserve skill updated timestamp on scans (#2296)
* fix: preserve skill updated timestamp on scans

* fix: scope scan timestamp preservation
2026-05-18 11:51:06 -07:00
Patrick Erichsen f9072e53e1 chore: add ClawHub autoreview skill (#2294) 2026-05-18 10:53:23 -07:00
Peter Steinberger ba587040b0 fix: let Codex own release scan verdicts 2026-05-18 09:42:09 +01:00
Peter Steinberger 1a00013c21 test: accept canonical local publish owner 2026-05-18 07:26:52 +01:00
Peter Steinberger 0613c54ce6 chore: prepare 0.16.0 release 2026-05-18 07:17:32 +01:00
Peter Steinberger 389b06b2cc fix: make Codex scan schema strict 2026-05-18 07:09:45 +01:00
Peter Steinberger de59d21291 fix: harden package publish contention (#2291)
* fix: harden package publish contention

* test: fix http retry overload
2026-05-18 07:09:01 +01:00
Peter Steinberger e3ad5892a5 fix: authenticate Codex security worker in CI 2026-05-18 06:55:34 +01:00
Peter Steinberger 74421c37fd fix: make Codex security worker executable in Actions 2026-05-18 06:51:55 +01:00
Peter Steinberger 35aa372b24 feat: run ClawScan classification through Codex (#2290) 2026-05-18 01:55:28 +01:00
Patrick Erichsen 636750fbf8 fix: add admin package name repair (#2289)
* fix: add admin package name repair

* fix: stabilize publish owner selection
2026-05-17 17:08:42 -07:00
Vyctor H. BrzezowskiandPeter Steinberger 8bbc66868d fix: harden skill transfer acceptance (#2276)
* fix: prevent skill transfer acceptance after requester is banned

The acceptTransferInternal mutation did not verify whether the transfer
requester (fromUser) was banned or deactivated when the skill still
belonged to that user. This created a race condition where a pending
transfer could be accepted after the requester was banned, allowing
the skill to escape the ban batch and remain alive under a new owner.

This change moves the requester validity check before the ownership
branch, so it is evaluated unconditionally for all transfers.

Fixes a security vulnerability where banned users' skills could
survive moderation actions via pending transfers.

* fix: harden skill transfer acceptance

Co-authored-by: vyctorbrzezowski <krzyszchweski@gmail.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-05-16 14:35:13 +01:00
Patrick Erichsen fd4d22d2c2 Seed local dev with public corpus (#2226)
* feat: seed local dev with public corpus

* fix: preserve local seed owner helpers after merge
2026-05-14 23:20:04 -07:00
Patrick Erichsen e99eae32d6 fix: replace search loading boxes with skeletons (#2224) 2026-05-14 11:22:43 -07:00
Vyctor H. BrzezowskiandPatrick Erichsen 9ab92e5847 fix(web): harden search relevance UX (#2206)
Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-05-14 11:18:42 -07:00
Patrick Erichsen cccef81a3e fix: show suspicious skills in UI search (#2220) 2026-05-14 10:24:03 -07:00
Momoandmomothemage c7458d9477 fix: package delete returns 500 for packages with capability tags (#2212)
Merged via squash.

Prepared head SHA: 82ca516464
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Reviewed-by: @momothemage
2026-05-14 11:14:19 +08:00
Jason 7e1e2f0a4f Fix local dev seeding, redirects, and package pinning (#2169)
* feat: seed local owner content for dev

* fix: redirect merged skill pages to canonical slug

* chore: pin package dependency versions

* fix: remove dashboard seed button

* docs: document local fixture seeding

* fix: make current-user seed fixtures internal
2026-05-13 16:41:31 -07:00
Patrick Erichsen 9fcf892e34 feat: add ClawScan finding permalinks (#2209) 2026-05-13 16:16:29 -07:00
Patrick Erichsen 22e994fe55 feat: add ClawScan finding permalinks (#2208) 2026-05-13 15:48:55 -07:00
Jason 0dd91f130e fix: use canonical user profile route (#2173) 2026-05-13 15:45:47 -07:00
Patrick Erichsen 893f341bdb feat: add security audits page (#2207)
* feat: add security audits page

* fix: remove stale nav icon export
2026-05-13 15:37:16 -07:00
Patrick Erichsen 3b6d8ac3d8 fix: shorten ClawScan risk badge labels (#2204) 2026-05-13 14:27:58 -07:00
Patrick Erichsen a116d92866 feat: show ClawScan risk levels in UI (#2202) 2026-05-13 14:09:38 -07:00
Patrick Erichsen a1666bb1e6 feat: export ClawScan findings sidecar (#2201)
* feat: export clawscan findings sidecar

* fix: canonicalize clawscan sidecar fields
2026-05-13 09:45:33 -07:00
Peter Steinberger 5c98c7e3e1 docs: add inline comment guidance 2026-05-13 16:13:33 +01:00
Peter Steinberger 0e926c6f8a fix: tolerate publisher handle conflicts during session ensure 2026-05-13 12:31:18 +01:00
Peter Steinbergerandvyctorbrzezowski 50858282b3 feat: add plugin discovery categories
Co-authored-by: vyctorbrzezowski <krzyszchweski@gmail.com>
2026-05-13 12:18:05 +01:00
Peter Steinberger b1206ed994 fix: land catalog docs and starred page fixes 2026-05-13 12:11:10 +01:00
Peter Steinberger 2763be8fb7 test: fix icon publisher CI checks 2026-05-13 10:55:00 +01:00
Peter Steinberger 4ca5ba9d1e fix: tighten ClawHub publish and API edge cases 2026-05-13 10:50:13 +01:00
Momo 5e4be11a85 feat(skills): allow publishers to pick a custom lucide icon (#2174)
Merged via squash.

Prepared head SHA: 1c3d695e87
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Reviewed-by: @momothemage
2026-05-13 17:39:57 +08:00
Peter Steinberger 6c15a481d0 fix: improve search ownership and profile saves 2026-05-13 08:33:19 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> a925359e13 build(deps-dev): bump the development-minor-and-patch group across 1 directory with 7 updates (#2166)
Bumps the development-minor-and-patch group with 7 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@playwright/test](https://github.com/microsoft/playwright) | `1.59.1` | `1.60.0` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `25.6.2` | `25.7.0` |
| [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) | `4.1.5` | `4.1.6` |
| [oxfmt](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt) | `0.48.0` | `0.49.0` |
| [oxlint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint) | `1.63.0` | `1.64.0` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `8.0.11` | `8.0.12` |
| [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.5` | `4.1.6` |



Updates `@playwright/test` from 1.59.1 to 1.60.0
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.59.1...v1.60.0)

Updates `@types/node` from 25.6.2 to 25.7.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@vitest/coverage-v8` from 4.1.5 to 4.1.6
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.6/packages/coverage-v8)

Updates `oxfmt` from 0.48.0 to 0.49.0
- [Release notes](https://github.com/oxc-project/oxc/releases)
- [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxfmt/CHANGELOG.md)
- [Commits](https://github.com/oxc-project/oxc/commits/oxfmt_v0.49.0/npm/oxfmt)

Updates `oxlint` from 1.63.0 to 1.64.0
- [Release notes](https://github.com/oxc-project/oxc/releases)
- [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint/CHANGELOG.md)
- [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.64.0/npm/oxlint)

Updates `vite` from 8.0.11 to 8.0.12
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.0.12/packages/vite)

Updates `vitest` from 4.1.5 to 4.1.6
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.6/packages/vitest)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  dependency-version: 1.60.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-minor-and-patch
- dependency-name: "@types/node"
  dependency-version: 25.7.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-minor-and-patch
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 4.1.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-minor-and-patch
- dependency-name: oxfmt
  dependency-version: 0.49.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-minor-and-patch
- dependency-name: oxlint
  dependency-version: 1.64.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-minor-and-patch
- dependency-name: vite
  dependency-version: 8.0.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-minor-and-patch
- dependency-name: vitest
  dependency-version: 4.1.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-13 08:24:05 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 207e2a8448 build(deps): bump the github-actions group with 2 updates (#2165)
Bumps the github-actions group with 2 updates: [github/codeql-action](https://github.com/github/codeql-action) and [trufflesecurity/trufflehog](https://github.com/trufflesecurity/trufflehog).


Updates `github/codeql-action` from 4.35.3 to 4.35.4
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/e46ed2cbd01164d986452f91f178727624ae40d7...68bde559dea0fdcac2102bfdf6230c5f70eb485e)

Updates `trufflesecurity/trufflehog` from 3.95.2 to 3.95.3
- [Release notes](https://github.com/trufflesecurity/trufflehog/releases)
- [Commits](https://github.com/trufflesecurity/trufflehog/compare/v3.95.2...v3.95.3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.35.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: trufflesecurity/trufflehog
  dependency-version: 3.95.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-13 08:23:57 +01:00
Patrick Erichsen ae2ffd25b5 Add new version detail actions (#2196)
* feat: add new version detail actions

* test: update new version e2e flow
2026-05-12 22:55:35 -07:00
Peter Steinberger 6dbeabb983 ci: harden prod HTTP smoke retries 2026-05-13 06:34:17 +01:00
Patrick Erichsen 8f74484032 docs: add clawhub moderation skill (#2195) 2026-05-12 22:32:48 -07:00
Vyctor H. Brzezowski d794f4633d fix(web): sort publisher previews by downloads (#2176) 2026-05-12 22:17:52 -07:00
Peter Steinberger e6a3bdf4e3 fix: audit identity and publisher changes 2026-05-13 06:13:13 +01:00
Patrick Erichsen 72d4e7a326 feat: restore skills via moderator unhide (#2194) 2026-05-12 22:06:20 -07:00
Peter Steinberger f72e936a47 test: stabilize live e2e coverage 2026-05-13 05:56:02 +01:00
Patrick Erichsen 64f1ffbeab test: add skill publish lifecycle e2e (#2193)
* test: add skill publish lifecycle e2e

* test: generalize local auth e2e lane

* fix: tighten local dev auth e2e gate
2026-05-12 21:40:51 -07:00
Patrick Erichsen 2ddaad62cc feat: add Crabbox UI proof workflow (#2192)
* feat: add crabbox ui proof workflow

* fix: render ui proof video previews inline
2026-05-12 21:13:04 -07:00
Jesse Merhi f0a6789c31 feat: add exact release trust endpoint (#2191) 2026-05-12 20:12:56 -07:00
Marko Anastasov af96221ebb fix: avoid slug availability crash for duplicate slugs (#2179) 2026-05-12 13:20:00 -07:00
Peter Steinberger be77f0626d test: wait for hydration before owner navigation 2026-05-12 08:21:00 +01:00
Peter Steinberger dded7a55b9 fix: improve mobile detail tab targets 2026-05-12 08:11:21 +01:00
Peter Steinberger 5b10c21b50 ci: ignore overbroad tanstack history advisory 2026-05-12 07:34:11 +01:00
Peter Steinberger c107523a31 fix: prepare clawhub 0.15.0 2026-05-12 07:30:23 +01:00
Val Alexander 09cbd9c58d Update styles.css 2026-05-11 22:42:42 -05:00
Val Alexander 22950d1bd2 Update .gitignore 2026-05-11 22:42:23 -05:00
Patrick Erichsen 53b64d1d91 fix: show plugin settings action to managers (#2168) 2026-05-11 17:14:14 -07:00
Patrick Erichsen b80b942fb2 fix: show plugin settings action to managers (#2163) 2026-05-11 17:02:29 -07:00
Patrick Erichsen 426a2a7879 fix: clarify skill summary settings copy (#2162) 2026-05-11 16:37:37 -07:00
Patrick Erichsen ee8e33947c docs: clarify pre-pr validation gates (#2161) 2026-05-11 16:36:01 -07:00
Patrick Erichsen 0c4d0448c1 fix: show full skill description on detail pages (#2160) 2026-05-11 16:27:26 -07:00
Patrick Erichsen 3b2d3dcc7a fix: expand skill summary clamp (#2158) 2026-05-11 15:45:13 -07:00
Patrick Erichsen c1257a31fc fix: polish dashboard artifact cards (#2157) 2026-05-11 15:41:01 -07:00
Patrick Erichsen 404a1359df fix: clamp skill detail summaries (#2156) 2026-05-11 15:03:56 -07:00
Patrick Erichsen 4eb1b97980 fix: refresh skill star state (#2155) 2026-05-11 15:00:24 -07:00
Patrick Erichsen dd7bc17fd3 fix: prevent skill tabs from scrolling (#2154) 2026-05-11 14:59:02 -07:00
Patrick Erichsen 008af4dcf8 fix: show downloads as primary adoption metric (#2153) 2026-05-11 14:37:49 -07:00
Patrick Erichsen ea0f8ba64a test: drop removed rescan e2e probes (#2152) 2026-05-11 14:36:01 -07:00
Patrick Erichsen 39695af5e3 fix: refresh detail page loading skeletons (#2150) 2026-05-11 14:35:43 -07:00
Patrick Erichsen c3d120756e fix: show owner names when handles are hidden (#2151) 2026-05-11 14:23:22 -07:00
Patrick Erichsen b67d7cc619 chore: prepare clawhub cli 0.14.0 (#2149) 2026-05-11 14:20:06 -07:00
Patrick ErichsenBunsDevcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
c51cfe2459 Add publisher notes and unify ClawScan review pages (#2111)
* feat: store clawscan notes on artifact versions

* feat: include clawscan notes in evaluation

* feat: unify ClawScan report layout for plugins and skills

* feat: render clawscan notes in publish and security UI

* chore: document local moderation seed fixtures

* fix: remove appeal surfaces

* fix: remove owner-requested rescans

* feat: add publisher note rescan flow

* fix: resolve main rebase fallout

* fix: address review feedback - breadcrumbs, tab guard, merge target, test matcher

Agent-Logs-Url: https://github.com/openclaw/clawhub/sessions/7bfbe5cf-0b8e-44f9-bf0a-e6235f7f3f1d

Co-authored-by: BunsDev <68980965+BunsDev@users.noreply.github.com>

* fix: address pr ci fallout

* fix: resolve ci after main rebase

* fix: make package VT AI verdicts advisory

* fix: restore skill sidebar actions

* fix: resolve clawscan ui and ci checks

* fix: align security settings access and pending audits

* fix: restore skill version tabs

* fix: show publisher names in sidebars

* fix: align plugin install command styling

* fix: clarify virustotal audit copy

* fix: polish security summaries

* test: align security UI expectations

* docs: document clawscan note workflow

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: BunsDev <68980965+BunsDev@users.noreply.github.com>
2026-05-11 14:14:12 -07:00
Patrick Erichsen 64da959704 Auto-start services for Codex worktrees (#2147)
* feat: auto-start worktree services

* fix: address worktree autostart ci
2026-05-11 12:56:04 -07:00
Patrick Erichsen 5d3de37ff0 feat: add local dev persona FAB (#2146)
* feat: add local dev persona fab

* fix: time out stalled dev persona auth

* fix: restrict dev persona auth to local deployments

* chore: refresh static check dependencies
2026-05-11 12:43:22 -07:00
Patrick Erichsen 8ed8481380 feat: add local clawscan dry run script (#2143)
* feat: add local clawscan dry run script

* chore: add local clawscan script
2026-05-11 09:10:12 -07:00
Peter Steinberger bb6c6c1b38 fix: harden permission denial responses 2026-05-11 15:50:14 +01:00
Peter Steinberger 6f619eabc0 docs: prepare 0.13.0 changelog 2026-05-11 15:18:36 +01:00
Peter Steinberger bac981a193 fix: allow direct skill transfer back to personal publisher 2026-05-11 14:53:33 +01:00
Peter Steinberger 3d021d3c5d fix: send skill transfer payloads as JSON objects 2026-05-11 14:45:30 +01:00
Peter Steinberger a873ffbf53 fix: keep package digests schema-safe 2026-05-11 14:33:53 +01:00
Peter Steinberger 8287c494b2 fix: align publisher lifecycle permissions 2026-05-11 14:20:37 +01:00
Vyctor H. Brzezowski 8a87890c6a feat(web): redesign settings experience
Redesign Settings into focused account, organization, API token, and account deletion views.

Follow-up verification:
- restored the Separator primitive usage so the Radix dependency remains active and static checks pass
- gated the organization member query to the organizations view with a selected org
- added focused settings coverage for default account rendering, organizations navigation/member loading, and legacy hash migration

Validation:
- bun install --frozen-lockfile
- bun run test -- src/routes/-settings.test.tsx src/__tests__/header.test.tsx
- bun run test:ui-contract
- bun run ci:static
- bunx tsc --noEmit
- bunx tsc -p packages/schema/tsconfig.json --noEmit
- bunx tsc -p packages/clawhub/tsconfig.json --noEmit
- VITE_CONVEX_URL=https://example.invalid bun run build
- GitHub repo-owned PR checks passed on verified signed head 7abd808fd4

Vercel fork authorization remained a non-code failure; authenticated local visual proof was blocked by missing GitHub login credentials, while signed-out settings route dev QA rendered without framework overlay.
2026-05-11 08:05:34 -05:00
Luke ff53a37353 fix: bound skill full-text search fanout 2026-05-11 20:47:58 +10:00
Luke b79bfc469e fix: require preinstall package verification in skill prompt
* fix: require preinstall package verification in skill prompt

* fix: include install safety checks in all skill prompts
2026-05-11 20:41:08 +10:00
Momo 190ce3769f fix(search): recall non-first-token matches via full-text indexes (#2140)
Merged via squash.

Prepared head SHA: b597454bc2
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Reviewed-by: @momothemage
2026-05-11 18:36:37 +08:00
Peter Steinberger 75e04d2c91 fix: guard public skill list cursors 2026-05-11 09:38:02 +01:00
Peter Steinberger 3173ecd629 fix: treat VirusTotal AI verdicts as advisory 2026-05-11 08:43:57 +01:00
Peter Steinberger bcc8fa5b71 fix: keep aggregate suspicious rows hidden 2026-05-11 02:31:31 +01:00
Peter Steinberger 5d0264daa7 fix: split ClawScan review from suspicious 2026-05-11 01:28:42 +01:00
Peter Steinberger 2dcaf25d23 chore: add ClawScan inventory counter 2026-05-10 16:39:03 +01:00
Peter Steinberger 3d934a5f28 fix: reduce ClawScan false positives 2026-05-10 16:29:02 +01:00
Peter Steinberger 92f5e68764 chore: add forced skill moderation backfill 2026-05-10 10:04:54 +01:00
Peter Steinberger c2c01300a7 fix: make static suspicious findings advisory 2026-05-10 09:57:58 +01:00
Peter Steinberger 3b80e3f67d fix: reduce ClawScan false positives 2026-05-10 06:50:54 +01:00
Peter Steinberger 003d243ccc fix: allow staff security rescans 2026-05-10 05:23:41 +01:00
Peter Steinberger 3a6ab49dc3 fix: include package env metadata in ClawScan 2026-05-10 02:50:25 +01:00
Peter Steinberger ec4fa3b76d test: fix user badge fixtures 2026-05-10 02:29:30 +01:00
Peter Steinberger 4ce383e822 fix: accept PowerShell skill files 2026-05-10 02:25:01 +01:00
Peter Steinberger 383230582c test: align catalog profile e2e assertions 2026-05-10 02:10:19 +01:00
Peter Steinberger 49d77c040e fix: link owner badges to publisher profiles 2026-05-10 02:05:44 +01:00
Peter Steinberger edba7e1b12 fix: stabilize publishers directory and e2e flows 2026-05-10 02:01:21 +01:00
Peter Steinberger dccb030dd6 fix: count package install stats 2026-05-10 00:47:45 +01:00
Vyctor H. Brzezowski 77692d1249 fix: avoid eager publisher page hydration (#2129) 2026-05-09 19:43:50 -04:00
Vyctor H. Brzezowski 91d962e8b7 fix: show suspicious filter conditionally (#2121) 2026-05-09 19:43:18 -04:00
Peter Steinberger 4f0abcdac1 fix: keep undici on node 20 compatible major 2026-05-10 00:24:46 +01:00
Peter Steinberger bc1dab4b08 chore: update dependencies 2026-05-10 00:21:21 +01:00
Peter Steinberger 4cfc137f26 fix(web): surface publish validation inline 2026-05-09 23:39:13 +01:00
Vyctor H. Brzezowski 31def36bbd fix(web): constrain page content containers
Constrain settings, profile content, skill detail, and plugin detail pages to the header content width while preserving profile hero bleed.\n\nCo-authored-by: vyctorbrzezowski <krzyszchweski@gmail.com>
2026-05-09 18:35:28 -04:00
Vyctor H. Brzezowski 8a6efc236a fix(web): align header avatar controls
Align signed-in header avatar controls across desktop and mobile so the menu trigger keeps consistent sizing, truncation, and dropdown styling.\n\nCo-authored-by: vyctorbrzezowski <krzyszchweski@gmail.com>
2026-05-09 18:35:25 -04:00
Vyctor Huggo Przozwski 38ba9768d3 test: isolate site helper env
Test-only isolation for site helper env handling.\n\nVerified locally with bun run ci:pr, focused polluted-env site helper test, and Playwright smoke.
2026-05-09 10:59:29 -04:00
Peter Steinberger 40627f0796 fix(web): bound publisher directory counts 2026-05-09 14:53:48 +01:00
Vyctor Huggo PrzozwskiandPeter Steinberger 8bd7be9d04 feat(web): add publishers discovery and profiles
Add the publishers discovery/profile surface and harden the landing fixups for existing publisher aggregate rows and scoped plugin links.

Co-authored-by: Vyctor Huggo Przozwski <krzyszchweski@gmail.com>
Co-authored-by: Peter Steinberger <peter@steipete.com>
2026-05-09 09:36:21 -04:00
Peter Steinberger da20972a04 docs: update changelog and CLI readmes 2026-05-09 13:49:33 +01:00
Peter Steinberger 227cce18a0 feat: complete CLI device login flow
Complete CLI device login with Convex-backed device-code endpoints, the web approval page, API URL discovery, endpoint rate limiting, and approval state hardening.\n\nTests:\n- bunx vitest run convex/httpApi.handlers.test.ts packages/clawhub/src/deviceAuth.test.ts\n- bun run --cwd packages/clawhub test:src -- src/deviceAuth.test.ts\n- bunx tsc --noEmit\n- bunx tsc -p packages/schema/tsconfig.json --noEmit\n- bunx tsc -p packages/clawhub/tsconfig.json --noEmit\n- bun run ci:static\n- bun run ci:types-build\n- bunx convex codegen\n\nCo-authored-by: Lumen <openclaw@openclaw-secure.local>
2026-05-09 08:42:04 -04:00
Peter Steinberger 0b56c68643 feat: add moderation hold recovery
Add admin-only moderation hold recovery with audit logs, docs, and safe skill restoration that preserves independently hidden scanner/manual moderation reasons.\n\nTests:\n- bunx vitest run convex/users.test.ts convex/skills.moderationHold.test.ts --testNamePattern "liftModerationHold|reserveHandleInternal|moderation holds"\n- bunx tsc --noEmit && bunx tsc -p packages/schema/tsconfig.json --noEmit && bunx tsc -p packages/clawhub/tsconfig.json --noEmit\n- bun run ci:static\n- bunx convex codegen\n\nCo-authored-by: Justin Sparks <openclaw@openclaw-secure.local>
2026-05-09 08:40:40 -04:00
Deepak Jain ab3ede496d feat(cli): add per-skill pinning
Add per-skill CLI pinning and harden install semantics so pinned skills cannot be overwritten by forced installs.\n\nTests:\n- bunx vitest run packages/clawhub/src/cli/commands/skills.test.ts packages/clawhub/src/skills.test.ts packages/schema/src/schemas.test.ts\n- bun run --cwd packages/clawhub verify:build\n- bun run ci:static\n- bunx tsc -p packages/schema/tsconfig.json --noEmit\n- bunx tsc -p packages/clawhub/tsconfig.json --noEmit\n\nCo-authored-by: deepujain <deepujain@users.noreply.github.com>
2026-05-09 08:35:41 -04:00
openclaw-barnacle[bot] ada5556752 chore: update Convex AI files
Update Convex-generated AI guidance files and apply repository formatting.\n\nTests:\n- bun run ci:static\n- bunx tsc -p packages/schema/tsconfig.json --noEmit\n- bunx tsc -p packages/clawhub/tsconfig.json --noEmit
2026-05-09 08:35:32 -04:00
Vyctor Huggo Przozwski f3d9c9b27a fix: rename plugins cards view to grid (#2119) 2026-05-09 07:56:39 -04:00
SylvanXiaoandSylvanXiao ceb25616ab feat: allow owners to edit skill summary (#1411)
Co-authored-by: SylvanXiao <Sylvanxiao@gmail.com>
2026-05-09 07:39:29 -04:00
Deepak Jain cb018bf64c docs: document trademark takedown reports (#1860)
Fixes #1591
2026-05-09 07:33:47 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> db2a14755d build(deps): bump the github-actions group with 2 updates (#2006)
Bumps the github-actions group with 2 updates: [actions/upload-artifact](https://github.com/actions/upload-artifact) and [github/codeql-action](https://github.com/github/codeql-action).


Updates `actions/upload-artifact` from 4 to 7
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v7)

Updates `github/codeql-action` from 4.35.2 to 4.35.3
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/95e58e9a2cdfd71adc6e0353d5c52f41a045d225...e46ed2cbd01164d986452f91f178727624ae40d7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: github/codeql-action
  dependency-version: 4.35.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-09 07:33:44 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 12f2cb8513 build(deps-dev): bump the development-minor-and-patch group across 1 directory with 3 updates (#2063)
Bumps the development-minor-and-patch group with 3 updates in the / directory: [jsdom](https://github.com/jsdom/jsdom), [oxfmt](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt) and [oxlint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint).


Updates `jsdom` from 29.1.0 to 29.1.1
- [Release notes](https://github.com/jsdom/jsdom/releases)
- [Commits](https://github.com/jsdom/jsdom/compare/v29.1.0...v29.1.1)

Updates `oxfmt` from 0.47.0 to 0.48.0
- [Release notes](https://github.com/oxc-project/oxc/releases)
- [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxfmt/CHANGELOG.md)
- [Commits](https://github.com/oxc-project/oxc/commits/oxfmt_v0.48.0/npm/oxfmt)

Updates `oxlint` from 1.62.0 to 1.63.0
- [Release notes](https://github.com/oxc-project/oxc/releases)
- [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint/CHANGELOG.md)
- [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.63.0/npm/oxlint)

---
updated-dependencies:
- dependency-name: jsdom
  dependency-version: 29.1.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-minor-and-patch
- dependency-name: oxfmt
  dependency-version: 0.48.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-minor-and-patch
- dependency-name: oxlint
  dependency-version: 1.63.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-09 07:33:39 -04:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Peter Steinberger
d16a62f552 build(deps): bump the production-minor-and-patch group across 1 directory with 8 updates (#2065)
* build(deps): bump the production-minor-and-patch group across 1 directory with 8 updates

Bumps the production-minor-and-patch group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@tanstack/react-router](https://github.com/TanStack/router/tree/HEAD/packages/react-router) | `1.168.26` | `1.169.2` |
| [@tanstack/react-start](https://github.com/TanStack/router/tree/HEAD/packages/react-start) | `1.167.52` | `1.167.65` |
| [convex](https://github.com/get-convex/convex-backend/tree/HEAD/npm-packages/convex) | `1.36.1` | `1.37.0` |
| [convex-helpers](https://github.com/get-convex/convex-helpers/tree/HEAD/packages/convex-helpers) | `0.1.115` | `0.1.116` |
| [react](https://github.com/facebook/react/tree/HEAD/packages/react) | `19.2.5` | `19.2.6` |
| [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) | `19.2.5` | `19.2.6` |
| [yaml](https://github.com/eemeli/yaml) | `2.8.3` | `2.8.4` |
| [zod](https://github.com/colinhacks/zod) | `4.4.1` | `4.4.3` |



Updates `@tanstack/react-router` from 1.168.26 to 1.169.2
- [Release notes](https://github.com/TanStack/router/releases)
- [Changelog](https://github.com/TanStack/router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/TanStack/router/commits/@tanstack/react-router@1.169.2/packages/react-router)

Updates `@tanstack/react-start` from 1.167.52 to 1.167.65
- [Release notes](https://github.com/TanStack/router/releases)
- [Changelog](https://github.com/TanStack/router/blob/main/packages/react-start/CHANGELOG.md)
- [Commits](https://github.com/TanStack/router/commits/@tanstack/react-start@1.167.65/packages/react-start)

Updates `convex` from 1.36.1 to 1.37.0
- [Release notes](https://github.com/get-convex/convex-backend/releases)
- [Changelog](https://github.com/get-convex/convex-backend/blob/main/npm-packages/convex/CHANGELOG.md)
- [Commits](https://github.com/get-convex/convex-backend/commits/HEAD/npm-packages/convex)

Updates `convex-helpers` from 0.1.115 to 0.1.116
- [Changelog](https://github.com/get-convex/convex-helpers/blob/main/packages/convex-helpers/CHANGELOG.md)
- [Commits](https://github.com/get-convex/convex-helpers/commits/npm/0.1.116/packages/convex-helpers)

Updates `react` from 19.2.5 to 19.2.6
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.6/packages/react)

Updates `react-dom` from 19.2.5 to 19.2.6
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.6/packages/react-dom)

Updates `yaml` from 2.8.3 to 2.8.4
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v2.8.3...v2.8.4)

Updates `zod` from 4.4.1 to 4.4.3
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v4.4.1...v4.4.3)

---
updated-dependencies:
- dependency-name: "@tanstack/react-router"
  dependency-version: 1.169.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
- dependency-name: "@tanstack/react-start"
  dependency-version: 1.167.65
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: convex
  dependency-version: 1.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-minor-and-patch
- dependency-name: convex-helpers
  dependency-version: 0.1.116
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: react
  dependency-version: 19.2.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: react-dom
  dependency-version: 19.2.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: yaml
  dependency-version: 2.8.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
- dependency-name: zod
  dependency-version: 4.4.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix: keep zod pinned for TanStack build

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-05-09 07:24:12 -04:00
Vyctor Huggo Przozwski 6b2efcb7b8 fix(web): polish browse listing surfaces
Polish ClawHub browse and listing surfaces across skills, plugins, and search: clearer search controls, visible safety filtering, plugin card view parity, and more consistent card metadata treatment. Kept #2090's Grid terminology for skills while preserving plugin card view behavior.\n\nTests:\n- bun run test -- src/__tests__/search-route.test.tsx src/__tests__/packages-route.test.tsx src/__tests__/skills-index.test.tsx src/__tests__/header.test.tsx src/__tests__/ui-design-contract.test.ts\n- bun run format:check\n- bun run lint\n- bunx tsc --noEmit\n- bunx tsc -p packages/schema/tsconfig.json --noEmit\n- bunx tsc -p packages/clawhub/tsconfig.json --noEmit\n- git diff --check origin/main...HEAD\n\nCo-authored-by: vyctorbrzezowski <krzyszchweski@gmail.com>
2026-05-09 06:46:14 -04:00
Vyctor Huggo Przozwski 05122ea799 fix(dev): keep local seed resets deterministic
Clean stale seed lookup/badge rows during repeated local Convex dev seed resets, and delete package fixtures in an order that avoids the package-release trigger fallback query limit.\n\nTests:\n- bun run test -- convex/devSeed.rescanFixtures.test.ts\n- bun run format:check\n- bun run lint\n- bunx tsc --noEmit\n- bunx tsc -p packages/schema/tsconfig.json --noEmit\n- bunx tsc -p packages/clawhub/tsconfig.json --noEmit\n- git diff --check origin/main...HEAD\n\nCo-authored-by: vyctorbrzezowski <krzyszchweski@gmail.com>
2026-05-09 06:42:27 -04:00
Vyctor Huggo Przozwski b65d05ba9f fix(dev): align local Convex site URL setup
Document local Convex HTTP route usage through the site proxy port and make setup-worktree reject local site URL misconfigurations that point browser/auth routes at the function port.\n\nTests:\n- bun run test -- scripts/setup-worktree.test.ts\n- bun run format:check\n- bun run lint\n- bunx tsc --noEmit\n- bunx tsc -p packages/schema/tsconfig.json --noEmit\n- bunx tsc -p packages/clawhub/tsconfig.json --noEmit\n- git diff --check origin/main...HEAD\n\nCo-authored-by: vyctorbrzezowski <krzyszchweski@gmail.com>
2026-05-09 06:39:20 -04:00
José Antonio Mijares 7fe9451bda fix(clawsweeper): remove PAT dispatch fallback
Require the ClawSweeper GitHub App dispatch token path and remove the PAT fallback from dispatch credentials. Missing-app-secret deployments still exit through the existing no-token notice.\n\nTests:\n- rg -n "OPENCLAW_GH_TOKEN|steps\.token\.outputs\.token" .github/workflows/clawsweeper-dispatch.yml .github/workflows\n- bun run format:check\n- bun run lint\n- git diff --check origin/main...HEAD\n\nCo-authored-by: José Antonio Mijares <joseamijares@hotmail.com>
2026-05-09 06:39:17 -04:00
Vyctor Huggo Przozwski 4c8f9d98fb fix(web): rename skills cards view to grid
Rename the /skills alternate browse view from Cards to Grid while preserving legacy view=cards URLs as a compatible alias.\n\nTests:\n- VITE_CONVEX_URL=https://example.invalid bun run test -- src/__tests__/skills-index.test.tsx src/__tests__/skills-toolbar.test.tsx\n- bun run format:check\n- bun run lint\n- bunx tsc --noEmit\n- bunx tsc -p packages/schema/tsconfig.json --noEmit\n- bunx tsc -p packages/clawhub/tsconfig.json --noEmit\n\nCo-authored-by: vyctorbrzezowski <krzyszchweski@gmail.com>
2026-05-09 06:33:58 -04:00
Jason dde3a019bd fix(web): restore dashboard skill metrics
Restore downloads, all-time installs, stars, and version metrics on owned skill rows in the dashboard. Also makes dropdown menu items use the expected pointer cursor.\n\nTests:\n- bunx vitest run src/routes/-dashboard.test.tsx --reporter verbose\n- bun run format:check\n- bun run lint\n- bunx tsc --noEmit\n- bunx tsc -p packages/schema/tsconfig.json --noEmit\n- bunx tsc -p packages/clawhub/tsconfig.json --noEmit\n- bun run test\n\nCo-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
2026-05-09 05:38:00 -04:00
Momo df7e0f77a6 fix(search): disclose suspicious results hidden on unified search (#2079)
Merged via squash.

Prepared head SHA: 1477ca7120
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Reviewed-by: @momothemage
2026-05-09 17:19:06 +08:00
Jason b6875e60f6 fix(skills): repair merge ownership and slug reservations
Fix publisher-owned skill merge authorization, bound historical slug redirects, protect reserved namespaces, and add expiring owner-unpublish slug reservations.

Maintainer follow-up: require current owner-hide provenance before honoring an unpublished slug reservation, clear stale reservation fields on non-owner hide paths, and add regressions for stale moderation-owned reservations.

Verification:
- bunx vitest run convex/skills.rateLimit.test.ts convex/skills.slugAvailability.test.ts convex/skills.undeleteGate.test.ts --reporter verbose
- bun run format:check
- bun run lint
- bunx tsc --noEmit
- bunx tsc -p packages/schema/tsconfig.json --noEmit
- bunx tsc -p packages/clawhub/tsconfig.json --noEmit
- bunx convex codegen
- bun run test
- bun run --cwd packages/clawhub test:src -- src/cli/commands/delete.test.ts --reporter verbose
- git diff --check
- GitHub CI: static, packages, types-build, unit, e2e-http, playwright-smoke, CodeQL, secret scanning all passed on 2d0564d1

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
2026-05-09 04:55:32 -04:00
Momoandmomothemage 5b63d5df60 fix: harden skill owner migration
Harden skill owner migration across UI, CLI, API, stats, backups, and docs.

Co-authored-by: momothemage <niuzhengnan@163.com>
2026-05-08 12:23:05 -04:00
Momo 38c2134590 fix(skills): block owner from undeleting moderator-hidden skills (#2078)
Merged via squash.

Prepared head SHA: a3ff0752a9
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Reviewed-by: @momothemage
2026-05-08 11:56:08 +08:00
Patrick Erichsen f14d70759d fix: use canonical publishing docs path 2026-05-07 19:43:05 -07:00
Patrick Erichsen a292a60a36 fix: use short publishing docs link (#2098) 2026-05-07 19:32:50 -07:00
Patrick Erichsen 4d3b7dedba feat: add docs nav link (#2097) 2026-05-07 19:16:04 -07:00
Patrick Erichsen 1aab139775 feat: enforce scoped plugin ownership (#2072) 2026-05-07 19:14:24 -07:00
Patrick Erichsen 88756d5997 docs: mention package transfer in publishing FAQ (#2096) 2026-05-07 19:13:17 -07:00
Patrick Erichsen 8c86d6f570 Fix plugin publish ownership visibility (#2073)
* fix: clarify plugin publish ownership state

* test: tolerate publish route migration in prod smoke

* fix: reserve publish route collisions

* fix: preflight package scope owner mismatches in CLI

* fix: keep package scope validation server-side

* docs: explain ClawHub publishing flow

* fix: include publishing docs link in scope errors

* fix: centralize docs links

* fix: build docs links with URL

* fix: shorten package scope docs hint
2026-05-07 19:03:40 -07:00
Patrick Erichsen 86898837fb docs: split ClawHub public docs from specs (#2095)
* docs: split clawhub docs source

* docs: make clawhub docs product-facing

* docs: refine public clawhub docs routes
2026-05-07 18:54:47 -07:00
Patrick Erichsen d7c774996e docs: add RFC community review process (#2092)
* docs: add RFC community review process

* chore: keep slug validator options internal
2026-05-07 14:57:55 -07:00
Vincent Koc b8e5486f63 chore(ci): harden security ownership and workflow permissions (#2045)
* chore(security): expand protected automation owners

* chore(ci): default workflows to no token permissions
2026-05-07 01:13:54 -07:00
Momo 571a85f539 fix(slug): enforce length, pattern, and reserved-word rules on skill & soul slugs (#1879)
Merged via squash.

Prepared head SHA: d93026fd4b
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Reviewed-by: @momothemage
2026-05-07 14:37:58 +08:00
Patrick Erichsen 0f938fbabd feat: add entity-scoped moderator commands (#2066) 2026-05-06 19:54:07 -07:00
Peter Steinberger 5e7797df72 fix: explain blocked and unauthorized API states 2026-05-07 03:47:08 +01:00
Patrick Erichsen 0b058d10bf fix: repair clawhub mod installer 2026-05-06 19:09:33 -07:00
Patrick Erichsen 0749f16499 feat: split moderator commands into private cli 2026-05-06 19:05:07 -07:00
Patrick Erichsen 52c3b649e0 Merge pull request #2062 from openclaw/pe/maintenance-cleanup
chore: remove stale maintenance helpers
2026-05-06 17:34:07 -07:00
Patrick Erichsen 03328f7523 chore: remove stale maintenance helpers 2026-05-06 17:32:24 -07:00
Peter Steinberger 5019f2a78a fix: surface blocked account auth state 2026-05-07 01:30:58 +01:00
Patrick Erichsen e4a68d2a76 Merge pull request #2061 from openclaw/pe/security-review-grey-badge
Make security review badges neutral
2026-05-06 17:27:37 -07:00
Patrick Erichsen bde371360d fix: make security review badges neutral 2026-05-06 17:21:18 -07:00
Patrick Erichsen e62935762b Merge pull request #2055 from openclaw/pe/artifact-moderation-cases
Add skill and package artifact moderation cases
2026-05-06 17:10:19 -07:00
Patrick Erichsen ac08267403 test: update moderator summary expectation 2026-05-06 17:04:48 -07:00
Patrick Erichsen 9fe7532e27 feat: add report status backfill 2026-05-06 17:01:48 -07:00
Patrick Erichsen 3618d296af feat: summarize moderation CLI actions 2026-05-06 17:01:14 -07:00
Patrick Erichsen cab18339e6 docs: align moderation wording with moderator role 2026-05-06 16:42:15 -07:00
Patrick Erichsen 5b1cfb4574 fix: rename report resolution status to confirmed 2026-05-06 16:39:55 -07:00
Patrick Erichsen 2bd6ed9198 test: add package artifact moderation lifecycle e2e 2026-05-06 16:26:53 -07:00
Peter Steinberger 4f4d7dd563 docs: document slug routing contract 2026-05-07 00:24:20 +01:00
Patrick Erichsen d5d58a9dbc Merge pull request #2058 from openclaw/pe/pr-template-screenshots
docs: add pull request template
2026-05-06 16:19:05 -07:00
Patrick Erichsen 7a2733947e Merge pull request #2059 from openclaw/pe/friendly-404-page
Add a friendlier 404 page
2026-05-06 16:15:05 -07:00
Patrick Erichsen 4592e66879 feat: enforce artifact moderation state transitions 2026-05-06 16:05:23 -07:00
Patrick Erichsen 06500ea4ca feat: add friendly 404 page 2026-05-06 16:01:24 -07:00
Patrick Erichsen d89e2ce1a1 docs: add pull request template 2026-05-06 15:35:56 -07:00
Patrick Erichsen c4d1fcdbc6 feat: add skill artifact moderation cases 2026-05-06 15:22:54 -07:00
Peter Steinberger e8deec13a2 fix: allow docs host auth callback form posts 2026-05-06 22:16:00 +01:00
Peter Steinberger 678935d014 fix: preserve docs auth return host 2026-05-06 22:10:47 +01:00
Patrick Erichsen bb592363a7 Merge pull request #2054 from openclaw/pe/worktree-setup-bootstrap
fix: bootstrap ClawHub worktree setup
2026-05-06 13:48:25 -07:00
Patrick Erichsen e3b59fce38 fix: align route tests with scoped plugin routes 2026-05-06 13:43:31 -07:00
Patrick Erichsen 1da5c53ce9 fix: bootstrap clawhub worktree setup 2026-05-06 13:37:09 -07:00
Patrick Erichsen bfb16ceddf Merge pull request #2051 from vyctorbrzezowski/contrib/dev-worktree-seed-readiness
fix: wait for Convex functions before seeding worktrees
2026-05-06 13:25:51 -07:00
Patrick Erichsen f7fbd6bde4 Merge pull request #2052 from vyctorbrzezowski/contrib/browse-sidebar-sticky-offset
fix: keep browse sidebar below sticky header
2026-05-06 13:15:59 -07:00
Peter Steinberger bec9362361 feat: add ClawHub docs auth broker 2026-05-06 21:08:39 +01:00
vyctorbrzezowski c4688b3526 fix: keep browse sidebar below sticky header 2026-05-06 16:49:26 -03:00
Peter Steinberger 8d5eb14919 fix: keep scoped plugin URLs readable 2026-05-06 20:12:07 +01:00
Peter Steinberger 5aa4d13560 fix: route official extension slug aliases 2026-05-06 20:01:49 +01:00
vyctorbrzezowski a046cff693 fix: wait for Convex functions before seeding worktrees 2026-05-06 15:23:03 -03:00
Peter Steinberger 57308e6059 docs(changelog): add 0.12 release notes 2026-05-06 07:17:31 +01:00
Peter Steinberger 32011a1f9a test(e2e): open mobile nav in smoke 2026-05-06 07:09:11 +01:00
Patrick Erichsen b735a529c2 Merge pull request #2041 from openclaw/pe/worktree-env-fallback
fix: discover shared env for worktree dev
2026-05-05 23:03:45 -07:00
Peter Steinberger 6925ec761c fix(cli): support org-owned skill publishes 2026-05-06 07:00:40 +01:00
Patrick Erichsen 521fd2796a fix: discover shared env for worktree dev 2026-05-05 22:57:11 -07:00
Peter Steinberger 2b00f0b37e Merge pull request #2040 from openclaw/pe/package-delete-cli-ui
feat: allow package owners to delete plugins
2026-05-06 06:42:28 +01:00
Peter Steinberger 88b6a941ec fix(packages): finish delete flow cleanup 2026-05-06 06:38:36 +01:00
Patrick Erichsen 0c7607bd64 feat: allow package owners to delete plugins 2026-05-05 22:37:19 -07:00
Patrick Erichsen bb6ef2ba44 Merge pull request #2037 from openclaw/pe/fix-package-moderation-queue
fix: normalize package moderation queue timestamps
2026-05-05 21:01:11 -07:00
Patrick Erichsen df61771b7e fix: normalize package moderation queue timestamps 2026-05-05 20:50:08 -07:00
Patrick Erichsen 9605bb3d8e Merge pull request #2036 from openclaw/pe/mobile-friendly-clawhub
Fix mobile layout responsiveness
2026-05-05 20:08:07 -07:00
Patrick Erichsen 33176522da fix: improve mobile layout responsiveness 2026-05-05 19:46:40 -07:00
Patrick Erichsen 102f47174d Merge pull request #2035 from openclaw/pe/codex-worktree-setup
[codex] Add Codex worktree setup
2026-05-05 18:09:08 -07:00
Patrick Erichsen cd9995c676 fix: leave contributing guide unchanged 2026-05-05 18:06:42 -07:00
Patrick Erichsen 6679f36a2f fix: remove hardcoded worktree env path 2026-05-05 18:04:15 -07:00
Patrick Erichsen 19993f93ed fix: start Convex from worktree helper 2026-05-05 17:52:26 -07:00
Patrick Erichsen 9028a7402a fix: avoid Bun ambient type in worktree helper 2026-05-05 17:42:26 -07:00
Patrick Erichsen 0a32b9857d feat: add Codex worktree setup 2026-05-05 17:33:49 -07:00
Patrick Erichsen 0a5b648f78 Merge pull request #2033 from openclaw/pe/cli-privileged-help-visibility
fix: gate privileged cli help by role
2026-05-05 15:25:05 -07:00
Patrick Erichsen ebe77f7f63 fix: gate privileged cli help by role 2026-05-05 14:40:25 -07:00
Vincent Koc caac39ce29 fix(convex): scope rate limit buckets by kind
Scope HTTP rate limit buckets by request kind and raise healthy production limits so plugin downloads are not throttled by unrelated API reads.
2026-05-04 20:25:15 -07:00
Patrick Erichsen d24422a005 Merge pull request #2016 from openclaw/pe/package-lookup-not-found
fix(packages): return not found for invalid lookups
2026-05-04 20:23:00 -07:00
Patrick Erichsen 1c62c5fff0 fix(packages): return not found for invalid lookups 2026-05-04 20:18:46 -07:00
Peter Steinberger 395862fadf ci: use app token for Convex AI update PRs 2026-05-04 09:52:34 +01:00
Peter Steinberger ba7a108af1 fix: keep oxlint underscore rule disabled 2026-05-04 08:08:59 +01:00
Peter Steinberger 6c3f911e8e test: fix http api rate limit mock 2026-05-04 08:06:04 +01:00
Peter Steinberger facf20ceb6 fix: raise admin api rate limits 2026-05-04 07:56:36 +01:00
Peter Steinberger 0690891781 fix: raise trusted publish rate limit 2026-05-04 06:35:30 +01:00
Peter Steinberger 0df30649ca fix: validate clawpack runtime entries against extracted files 2026-05-04 06:06:30 +01:00
Peter Steinberger bbdde7fd53 fix: keep package dry-run metadata-only 2026-05-03 23:23:36 +01:00
Peter Steinberger 3d6f3b49a5 fix: reject code plugins without runtime output 2026-05-03 23:19:00 +01:00
Peter Steinberger 0b842636dc fix: allow admin plugin release publishes 2026-05-03 22:59:58 +01:00
Peter Steinberger 2d2d791e9f fix: infer package owner from scoped names 2026-05-03 22:53:01 +01:00
Peter Steinberger 96e3d7ebd4 fix: raise authenticated write rate limit 2026-05-03 22:44:21 +01:00
Peter Steinberger 768a50149e fix: support monorepo package publishes 2026-05-03 21:04:34 +01:00
Vincent Koc 199e6a0cdf fix(api): expose legacy zip artifact aliases
Expose legacy ZIP resolver compatibility aliases without confusing publish-time content hashes for downloaded archive integrity.
2026-05-03 10:34:37 -07:00
Vincent Koc 59fc54ff64 fix(web): canonicalize scoped plugin paths 2026-05-03 09:35:02 -07:00
Vincent Koc 343781a668 fix(api): decode scoped package paths 2026-05-03 09:26:13 -07:00
Vincent Koc 62b10f829d fix(packages): use single-window search fallback 2026-05-03 02:20:53 -07:00
Vincent Koc eb3113c1f3 fix(packages): rebuild search queries per page 2026-05-03 02:09:54 -07:00
Vincent Koc 887e81eb85 fix(api): return lean skill list payloads 2026-05-03 02:04:16 -07:00
Vincent Koc d6cfc891f0 fix(search): reduce lexical fallback scan budget 2026-05-03 02:02:19 -07:00
Vincent Koc cf5778d7d5 fix(api): route package search through digest index 2026-05-03 02:01:20 -07:00
Vincent Koc e76b72cdb1 fix(search): cap vector hydration window 2026-05-03 01:13:31 -07:00
Vincent Koc f53b49041a fix(api): avoid redundant latest tag version reads 2026-05-03 01:11:43 -07:00
Vincent Koc 21abd07672 fix(search): bound lexical fallback scans 2026-05-03 01:09:44 -07:00
Vincent Koc 6085ee4852 fix(convex): raise download rate limit 2026-05-03 00:24:42 -07:00
Vincent Koc 05653453ea fix(convex): reduce download and token write contention 2026-05-03 00:06:59 -07:00
Vincent Koc 86f8aa88af test(convex): update leaderboard page size expectation 2026-05-02 23:45:27 -07:00
Vincent Koc 2d42c3d57a fix(convex): reduce hot rate-limit and catalog reads 2026-05-02 23:42:15 -07:00
Vincent Koc cf5a6f6e8b fix(scanner): avoid generic pay purchase tags 2026-05-02 23:13:59 -07:00
Vincent Koc 46354c9967 fix(security): flag python file upload exfiltration 2026-05-02 22:50:57 -07:00
Vincent Koc 063ee210a7 fix(github): keep scanner appeal issues open 2026-05-02 22:50:09 -07:00
Vincent Koc f84c894e4e fix(ui): restore skill downloads and search paging 2026-05-02 22:46:30 -07:00
Vincent Koc f8141bc517 fix(convex): gate large index deletion 2026-05-02 19:07:44 -07:00
Vincent Koc ca4899078d fix(convex): retain built rate limit index 2026-05-02 18:37:10 -07:00
Vincent Koc 51d4633df0 fix(convex): avoid rate limit index backfill 2026-05-02 18:19:08 -07:00
Vincent Koc 6139dcd052 fix(convex): drop unused package stat index 2026-05-02 17:20:27 -07:00
Vincent Koc ab48c07b98 test(rate-limits): expect consumed shard quota 2026-05-02 16:19:00 -07:00
Vincent Koc 0a49b75e2f fix(convex): reduce hot stat write contention 2026-05-02 16:16:06 -07:00
Vincent Koc 5e9c61a185 fix(convex): bound skill health reads 2026-05-02 16:16:02 -07:00
Vincent Koc 8234c92dcf fix(packages): keep mirror artifact URLs on public host 2026-05-02 15:54:57 -07:00
Vincent Koc 9edff6fd38 build(schema): update package response dist 2026-05-02 15:45:05 -07:00
Vincent Koc 2ebcdd4ed0 test(packages): include required plugin manifests 2026-05-02 15:38:08 -07:00
Vincent Koc f5183cae9b fix(security): flag confirmation bypasses 2026-05-02 15:33:49 -07:00
Vincent Koc 4196789c6d chore(cli): bump to 0.12.2 2026-05-02 14:43:47 -07:00
Vincent Koc 4c69f2af2e fix(schema): allow nullable package sha 2026-05-02 14:42:57 -07:00
Vincent Koc 4c52dc23c1 fix(cli): allow legacy package downloads 2026-05-02 14:39:04 -07:00
Vincent Koc 8916167505 style(api): format scoped route changes 2026-05-02 14:29:08 -07:00
Vincent Koc f4f2da7fe7 fix(api): resolve scoped package routes 2026-05-02 14:28:27 -07:00
Vincent Koc 01529aaaf1 fix(cli): publish code plugins as clawpacks 2026-05-02 14:27:10 -07:00
Vincent Koc 05efb81669 chore(cli): bump to 0.12.1 2026-05-02 13:47:48 -07:00
Vincent Koc ca0d0bd1bd docs(security): clarify clawpack scan scope 2026-05-02 13:47:05 -07:00
Vincent Koc f82e07fd3a ci: add clean production deploy tags 2026-05-02 13:29:59 -07:00
Vincent Koc f2a61c9d94 fix(packages): scan clawpack artifacts with virustotal 2026-05-02 13:09:03 -07:00
Vincent Koc 3c09df3b77 ci: tag production frontend deploys 2026-05-02 13:06:55 -07:00
Vincent Koc 6d4cf0cfe7 test(packages): avoid unsafe optional chaining 2026-05-02 12:44:28 -07:00
Vincent Koc e4aa4c7459 style: format clawpack rollout changes 2026-05-02 12:43:44 -07:00
Vincent Koc 3aff30b955 fix(plugins): hide staged bundle publish ux 2026-05-02 12:42:59 -07:00
Vincent Koc c9a225aef7 fix(plugins): show clawpack artifact downloads 2026-05-02 12:41:53 -07:00
Vincent Koc 0fe234e68d feat(cli): add clawpack pack command 2026-05-02 12:40:38 -07:00
Vincent Koc 56743ce3d8 fix(packages): store clawpack metadata only 2026-05-02 12:38:57 -07:00
Vincent Koc 1fdfbcd51f fix(packages): cap clawpack tarballs at 120mb 2026-05-02 12:38:11 -07:00
Vincent Koc bf1e112d5a style(packages): format package updates 2026-05-02 11:41:12 -07:00
Vincent Koc 7266f4f927 docs(packages): clarify plugin package metadata 2026-05-02 11:39:43 -07:00
Vincent Koc 77927830f3 fix(api): accept scoped npm packuments 2026-05-02 11:38:39 -07:00
Vincent Koc e599d23f69 fix(packages): use real bundle markers 2026-05-02 11:37:28 -07:00
Vincent Koc 4c8738f1ef fix(clawpack): require plugin manifests 2026-05-02 11:34:51 -07:00
Vincent Koc e01c7a9f31 fix(packages): make host metadata optional 2026-05-02 11:33:38 -07:00
Vincent Koc c9a5b8508d test(packages): satisfy migration lint 2026-05-02 11:03:41 -07:00
Vincent Koc cb320fe2ab docs(packages): document official migrations 2026-05-02 11:02:35 -07:00
Vincent Koc 773df44f17 feat(cli): manage official migrations 2026-05-02 11:02:00 -07:00
Vincent Koc 402ddddbd7 feat(api): manage official migrations 2026-05-02 10:59:56 -07:00
Vincent Koc 238f3f6b14 feat(packages): persist official migrations 2026-05-02 10:58:23 -07:00
Vincent Koc 539bf60e97 chore(schema): build official migration types 2026-05-02 10:57:35 -07:00
Vincent Koc 6527ab6a9f feat(packages): add official migration schema 2026-05-02 10:55:03 -07:00
Vincent Koc 63164eb762 docs(cli): document package migration status 2026-05-02 10:53:54 -07:00
Vincent Koc 669e14b92c feat(cli): show package migration status 2026-05-02 10:53:36 -07:00
Vincent Koc 28da510571 feat(packages): resolve package appeals 2026-05-02 10:50:57 -07:00
Vincent Koc 6e5578ee6d feat(packages): submit package appeals 2026-05-02 10:47:26 -07:00
Vincent Koc 68017740e7 feat(packages): show moderation status 2026-05-02 10:44:37 -07:00
Vincent Koc ff68eeb5d1 feat(packages): triage package reports 2026-05-02 10:40:13 -07:00
Vincent Koc 276760d703 feat(packages): report packages for review 2026-05-02 10:35:28 -07:00
Vincent Koc 1b33c949f1 feat(packages): filter by artifact availability 2026-05-02 10:25:33 -07:00
Vincent Koc 417537a13f feat(packages): list moderation queue 2026-05-02 10:17:09 -07:00
Vincent Koc 6e15ed65e0 feat(cli): filter packages by environment 2026-05-02 10:07:55 -07:00
Vincent Koc 58dcd55076 style(dashboard): format pagination changes 2026-05-02 10:06:37 -07:00
Vincent Koc c9ad1305ff feat(packages): require environment metadata 2026-05-02 10:05:49 -07:00
Vlad Ursul 964fc0fa87 feat(dashboard): add skill pagination
Adds indexed, paginated dashboard skill loading and Load More UI.\n\nMaintainer validation after rebasing onto current main:\n- bun run test -- convex/skills.dashboard.test.ts src/routes/-dashboard.test.tsx\n- bun run test -- convex/skills.dashboard.test.ts convex/skills.list.test.ts\n- bunx tsc -p tsconfig.json --noEmit\n- bunx tsc -p packages/schema/tsconfig.json --noEmit\n- bunx tsc -p packages/clawhub/tsconfig.json --noEmit\n- bun run lint\n- bun run build\n\nNote: full bun run test currently has unrelated package publish route failures on current main; PR-focused tests and build are clean. Vercel PR preview remains blocked by fork deployment authorization.
2026-05-02 12:01:07 -05:00
Vincent Koc bc234c7d89 feat(packages): report openclaw readiness 2026-05-02 09:57:12 -07:00
Vincent Koc 87a286fe1f feat(packages): backfill package artifact kinds 2026-05-02 09:52:37 -07:00
Vincent Koc 00970bbee9 feat(packages): require code plugin host targets 2026-05-02 09:40:00 -07:00
Val Alexander 7979ff4249 chore: update ClawHub UI code owner
Update frontend/UI CODEOWNERS entries to use @BunsDev while preserving secops review ownership.
2026-05-02 11:38:28 -05:00
Val Alexander f3c4cbb99a feat: clarify about page policy patterns
Summary:
- Refresh the About page Recent Patterns section to explicitly allow specific maintainer-approved patterns.
- Replace the top-nav git icon with the GitHub mark for GitHub sign-in.
- Clean up ClawPack internal type exports and make Convex integrity hashing compatible with CI WebCrypto.

Validation:
- bun run format:check
- bun run lint
- bun run ci:static
- bun run ci:unit
- bunx tsc --noEmit
- bunx tsc -p packages/schema/tsconfig.json --noEmit && bunx tsc -p packages/clawhub/tsconfig.json --noEmit
- bun run test -- convex/lib/clawpack.test.ts
- VITE_CONVEX_URL=https://example.invalid bun run build
- GitHub checks for PR #1980 all passed
2026-05-02 11:33:49 -05:00
Vincent Koc bed2d4b1b0 feat(packages): moderate package releases 2026-05-02 09:28:40 -07:00
Vincent Koc 81759fd857 chore(convex): refresh generated api 2026-05-02 09:28:25 -07:00
Vincent Koc f3cf886ce5 feat(cli): download and verify package artifacts 2026-05-02 09:10:17 -07:00
Vincent Koc 2176dbf4c2 fix(packages): satisfy clawpack lint gates 2026-05-02 08:52:06 -07:00
Vincent Koc 80e8b599f9 test(cli): cover clawpack publish upload 2026-05-02 08:49:56 -07:00
Vincent Koc 7d636b771b test(api): cover clawpack package routes 2026-05-02 08:47:32 -07:00
Vincent Koc e94cc91b8e docs(packages): document clawpack artifact paths 2026-05-02 08:46:17 -07:00
Vincent Koc 0774d0fe92 feat(cli): publish uploaded clawpacks 2026-05-02 08:44:41 -07:00
Vincent Koc 1261062585 feat(api): serve clawpack mirror artifacts 2026-05-02 08:42:38 -07:00
Vincent Koc 88d0cc7888 feat(packages): accept clawpack uploads 2026-05-02 08:37:45 -07:00
Vincent Koc 87848016ff feat(packages): widen artifact schema 2026-05-02 08:34:47 -07:00
Vincent Koc 86e58d6031 feat(packages): add clawpack parser 2026-05-02 08:33:41 -07:00
Peter Steinberger 48e66714ac fix: add package identity repair admin 2026-05-02 06:47:24 +01:00
Peter Steinberger 0c705e159f fix: allow JSON Schema manifests in package publish 2026-05-02 05:41:51 +01:00
Peter Steinberger 5409df4123 fix: keep beta plugin packages off latest 2026-05-02 05:15:50 +01:00
Peter Steinberger ac15e5adea fix: add package owner transfer repair 2026-05-02 04:56:46 +01:00
Peter Steinberger 880d9e0572 feat: reserve OpenClaw plugin package names 2026-05-01 22:51:03 +01:00
Patrick Erichsen 63dfbd8876 Merge pull request #1967 from openclaw/pe/clawscan
Clarify ClawScan artifact prompt boundaries
2026-05-01 06:32:59 -07:00
Patrick Erichsen 4a7b7b7024 Update securityPrompt.ts 2026-05-01 06:32:07 -07:00
Patrick Erichsen 34e26093ab Update securityPrompt.ts 2026-05-01 06:31:25 -07:00
Patrick Erichsen 601d29b0e9 Update securityPrompt.ts 2026-05-01 06:30:53 -07:00
Patrick Erichsen bff959c8f0 fix: rely on JSON artifact neutralization 2026-05-01 06:28:29 -07:00
Patrick Erichsen 34a2c657b6 Merge remote-tracking branch 'origin/main' into pe/clawscan
# Conflicts:
#	convex/lib/securityPrompt.ts
2026-05-01 06:20:57 -07:00
Patrick Erichsen fc6555fa1c Update securityPrompt.ts 2026-05-01 06:11:53 -07:00
Patrick Erichsen e7ad7c628d fix: wrap ClawScan skill artifacts in prompt boundary 2026-05-01 06:07:39 -07:00
Vincent Koc 7c61d55833 ci: expand pr validation coverage
Split PR validation into explicit static, unit, package, type/build, HTTP e2e, and browser-smoke gates. Add local ci:* scripts and document the required status checks.
2026-05-01 02:20:40 -07:00
Vincent Koc 89becd866a Revert "feat: add health probes"
This reverts commit bb945c740e.
2026-04-30 23:56:11 -07:00
Vincent Koc eada4d5dcb Revert "fix: keep probe helper types private"
This reverts commit 7f15dcc225.
2026-04-30 23:56:11 -07:00
Vincent Koc 7f15dcc225 fix: keep probe helper types private 2026-04-30 23:46:39 -07:00
Vincent Koc bb945c740e feat: add health probes 2026-04-30 23:44:31 -07:00
Vincent Koc dfc0d540d8 chore(ci): enforce formatting 2026-04-30 23:39:28 -07:00
Vincent Koc cd37acadbb fix(security): add skill redaction hide mutation 2026-04-30 23:33:50 -07:00
Vincent Koc c9fe6db34d fix(search): index skill first-token recall 2026-04-30 23:27:37 -07:00
Vincent Koc 5fe321a43f fix(ci): treat cli schema as deadcode entry 2026-04-30 23:22:17 -07:00
Vincent Koc 9e15c5a6fa chore(ci): add deadcode gate 2026-04-30 23:17:07 -07:00
Vincent Koc 026b911d58 chore(search): allow manual digest backfill 2026-04-30 23:13:56 -07:00
Vincent Koc 08326f7718 chore(search): expose digest backfill cursor 2026-04-30 23:08:22 -07:00
Vincent Koc 881514f444 fix(search): add normalized skill prefix recall 2026-04-30 23:01:28 -07:00
Vincent Koc 3f17fd55e5 fix(security): fully strip hidden html comments 2026-04-30 22:39:35 -07:00
Vincent Koc 3f2153e678 fix(security): neutralize llm eval prompt injection 2026-04-30 22:00:05 -07:00
Vincent Koc 9ea3ed896f fix(security): fail closed when vt is unavailable 2026-04-30 18:34:38 -07:00
Vincent Koc 7ea5fc085c fix(ci): skip frontend smoke on backend deploys 2026-04-30 18:32:55 -07:00
Patrick Erichsen 1306ab6640 Merge pull request #1961 from openclaw/pe/clawscan
feat: label "suspicious" as "review" for scans
2026-04-30 16:23:35 -07:00
Patrick Erichsen f7c5ae5a16 feat: label "suspicious" as "review" for scans 2026-04-30 15:54:45 -07:00
Patrick Erichsen 631b357a10 Merge pull request #1948 from openclaw/pe/clawscan
feat: move ClawScan eval runner into ClawHub
2026-04-30 15:01:06 -07:00
Patrick Erichsen 42bc312151 feat: export redacted skill content for security dataset 2026-04-30 14:51:26 -07:00
Peter Steinberger 12c72366f6 fix: raise public read rate limits 2026-04-30 19:53:23 +01:00
Peter Steinberger 27d7d4afa4 ci: stabilize production deploy smoke 2026-04-30 19:50:24 +01:00
Peter Steinberger cb3852ef16 fix: sync schema dist for cli delete reason 2026-04-30 19:39:49 +01:00
Peter Steinberger 50768641f9 fix: satisfy lint on latest main 2026-04-30 19:34:10 +01:00
Peter Steinberger 651e54ed7c fix: record skill moderation reasons from CLI 2026-04-30 19:30:40 +01:00
Patrick Erichsen 3bbbd858d4 chore: rename ClawScan security signals eval 2026-04-30 09:24:29 -07:00
Patrick Erichsen 9a8607038e fix: satisfy ClawScan eval lint 2026-04-30 08:59:35 -07:00
Patrick Erichsen 2bac472615 feat: parameterize ClawScan eval HF split 2026-04-30 08:58:07 -07:00
Patrick Erichsen b27072312b chore: simplify ClawScan eval defaults 2026-04-30 08:57:01 -07:00
Patrick Erichsen 6bebc0f572 fix: satisfy maintenance lint rule 2026-04-30 08:39:46 -07:00
Patrick Erichsen efa349c856 Merge remote-tracking branch 'origin/main' into pe/clawscan 2026-04-30 08:39:07 -07:00
Patrick Erichsen 21f2cfbd9c ci: remove format check from build job 2026-04-30 08:36:42 -07:00
Patrick Erichsen b96af7391c feat: move ClawScan eval runner into ClawHub 2026-04-30 08:21:06 -07:00
Vincent Koc cfc4ba9b6a fix(maintenance): add skill version privacy removal 2026-04-30 03:52:03 -07:00
Vincent Koc 9b27c1a1d3 fix(convex): page owner-publisher digest syncs
Fixes #1195.

Fixes #1182.
2026-04-30 03:32:43 -07:00
Vincent Koc 9e09581c05 fix(rate-limit): scope anonymous download fallback buckets 2026-04-30 03:20:29 -07:00
Vincent Koc 97c409d56b fix(github): catch suspicious skill rescan requests 2026-04-30 03:05:01 -07:00
Vincent Koc 6c93d2096e fix(security): flag disabled tls verification 2026-04-30 03:04:21 -07:00
Vincent Koc 65d02e57b0 fix(web): add frontend security headers 2026-04-30 03:04:20 -07:00
Vincent Koc ae83b2188c fix(api): require explicit license acceptance 2026-04-30 03:04:19 -07:00
Vincent Koc d97942b996 fix(security): lock down virustotal result lookup 2026-04-30 03:04:18 -07:00
Vincent Koc a3125daf78 fix(github): add third-party skill closeout label 2026-04-30 02:54:10 -07:00
Val Alexander 23eec67163 fix: make detail install panels full width
Make skill and plugin detail hero action panels span the full content width, moving scans/install above long-form detail content.\n\nVerified with local focused tests, lint, targeted formatting, diff check, build, and green PR CI build.
2026-04-30 04:06:52 -05:00
Patrick Erichsen c3c885ec10 Merge pull request #1940 from openclaw/pe/clawscan
chore: remove clawscan eval corpora
2026-04-30 01:42:24 -07:00
Patrick Erichsen 04492fe196 feat: add prompt evals against 2026-04-30 01:12:04 -07:00
Vincent Koc 292f15dbae fix(convex): preserve public skill type narrowing 2026-04-30 01:05:08 -07:00
Vincent Koc 6bf8d4b7b7 fix(packages): count package archive downloads 2026-04-30 01:00:32 -07:00
Vincent Koc b60514b3fe fix(github): run rescan guidance as app 2026-04-30 00:56:10 -07:00
Vincent Koc 45b9c0e51d fix(cli): apply source path before GitHub package fetch 2026-04-30 00:55:26 -07:00
Vincent Koc e3cf29a2bc fix(security): flag remote recipe execution 2026-04-30 00:52:53 -07:00
Vincent Koc 3deff6efd1 chore(github): soften rescan guidance label 2026-04-30 00:51:06 -07:00
Vincent Koc c4950b8034 fix(security): flag provider secrets and rclone paths 2026-04-30 00:50:15 -07:00
Vincent Koc b043065ee5 fix(skills): hide nonpublic duplicate references 2026-04-30 00:49:01 -07:00
Patrick Erichsen 94d358e25b chore: move security eval pipelines out of clawhub 2026-04-30 00:48:14 -07:00
Vincent Koc b8f04b5bc4 fix(security): flag env cgnat credentials 2026-04-30 00:44:55 -07:00
Vincent Koc 248a3f25e3 fix(security): flag hardcoded operator billing 2026-04-30 00:42:19 -07:00
Vincent Koc 8fb4d01e65 fix(github): classify issue auto-responses 2026-04-30 00:41:57 -07:00
Vincent Koc 26744ba4ef fix(security): flag autonomous credential egress 2026-04-30 00:39:42 -07:00
Vincent Koc f1481c4d4e Reapply "feat(security): merge clawscan ASI analysis"
This reverts commit fa9ab8d620.
2026-04-30 00:35:47 -07:00
Vincent Koc ec2308c96d fix(security): flag Python credential posts 2026-04-30 00:34:48 -07:00
Vincent Koc beb5c27d9e fix(security): flag plaintext cgnat endpoints 2026-04-30 00:32:12 -07:00
Vincent Koc 18ae25b4c2 fix(security): flag unsafe subprocess file writes 2026-04-30 00:30:50 -07:00
Vincent Koc 1208e86b5f fix(security): flag unsafe browser file renders 2026-04-30 00:29:27 -07:00
Vincent Koc cd34538f16 fix(readme): scope relative skill links 2026-04-30 00:29:11 -07:00
Vincent Koc 85db1c60ad fix(security): flag shell file upload exfiltration 2026-04-30 00:27:43 -07:00
Vincent Koc 8aa7a58a40 fix(security): detect dynamic module execution 2026-04-30 00:25:38 -07:00
Vincent Koc 67739a4a9f fix(ui): improve runtime requirement contrast 2026-04-30 00:24:37 -07:00
Vincent Koc fc74a2f6cd fix(security): flag secret argv exposure 2026-04-30 00:23:53 -07:00
Deepak JainandVincent Koc 52078abd85 docs: clarify optional skill environment variables (#1859)
* Document optional skill env vars

Fixes #1617

* fix: honor nested optional env declarations

* chore: format skill env docs

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-04-30 00:22:17 -07:00
Vincent Koc 933fb94bcf fix(security): flag browser credential automation 2026-04-30 00:20:10 -07:00
Vincent Koc 0db5ef6224 fix(search): stabilize relevance recall window 2026-04-30 00:19:06 -07:00
Vincent Koc 43d50b8947 fix(security): delete GitHub mirror on skill hide 2026-04-30 00:11:14 -07:00
Vincent Koc 3fea99b8a6 docs(search): explain discoverability ranking 2026-04-30 00:08:06 -07:00
Vincent Koc b4cfe33659 fix(security): flag platform source patch installs 2026-04-30 00:04:46 -07:00
Vincent Koc e60bff87e8 fix(cli): surface inspect moderation diagnostics 2026-04-30 00:03:51 -07:00
Vincent Koc b3c42ddba2 fix(security): detect credential exposure docs 2026-04-30 00:01:41 -07:00
Vincent Koc adbf4347e7 fix(security): scan code files for hardcoded secrets 2026-04-29 23:59:32 -07:00
Vincent Koc 6595e13a10 fix(search): use nonsuspicious digest indexes 2026-04-29 23:54:11 -07:00
Vincent Koc 2a7b0f0a6f fix(deploy): harden production smoke checks 2026-04-29 23:53:24 -07:00
Vincent Koc ed596ba24d fix(github): run Barnacle with app token 2026-04-29 23:53:00 -07:00
Vincent Koc 2d054fe9ed Merge branch 'main' of https://github.com/openclaw/clawhub
* 'main' of https://github.com/openclaw/clawhub:
  chore(security-dataset): remove eval runner
2026-04-29 23:50:15 -07:00
Vincent Koc fa9ab8d620 Revert "feat(security): merge clawscan ASI analysis"
This reverts commit 79eddc0223, reversing
changes made to 33334c5afa.
2026-04-29 23:49:30 -07:00
Vincent Koc 7f220c2108 chore(security-dataset): remove eval runner 2026-04-29 23:48:59 -07:00
Vincent Koc 9b5c9541f8 fix(search): add soul lexical fallback 2026-04-29 23:48:13 -07:00
Vincent Koc e324fcaae2 feat(api): support created-time skill listing 2026-04-29 23:46:57 -07:00
Patrick Erichsen 57be656406 Merge remote-tracking branch 'origin/main' into pe/clawscan 2026-04-29 23:45:10 -07:00
Vincent Koc 8cab60d64a feat(github): add barnacle auto-response workflows 2026-04-29 23:44:46 -07:00
Vincent Koc d4d69d42be feat(cli): list manual skill directories 2026-04-29 23:43:35 -07:00
Vincent Koc 1461d0f175 feat(cli): show moderation in inspect 2026-04-29 23:41:22 -07:00
Vincent Koc 827fd92c7d fix(ui): use download icon for public stats 2026-04-29 23:40:27 -07:00
Vincent Koc cf20e10338 fix(github-backups): scan digest rows for sync 2026-04-29 23:33:25 -07:00
Vincent Koc 79eddc0223 feat(security): merge clawscan ASI analysis 2026-04-29 23:32:32 -07:00
Vincent Koc 33334c5afa fix(stats): avoid scan fallback for public skill count 2026-04-29 23:29:17 -07:00
Vincent Koc 477aae7c95 fix(schema): allow R source files 2026-04-29 23:25:47 -07:00
Vincent Koc a535da6dfb feat(security): verify dependency registries 2026-04-29 23:15:35 -07:00
Vincent Koc 50ee17ce7d style(security-dataset): format eval CLI test 2026-04-29 23:11:04 -07:00
Vincent Koc 0079d3f09a test(security-dataset): cover eval CLI outputs 2026-04-29 23:10:41 -07:00
Deepak Jain bcfe66d7d5 fix(security): narrow crypto swap detection
Fixes #1524

Taken from #1857.
2026-04-29 23:04:49 -07:00
Vincent Koc f3a1d7fc32 feat(security-dataset): expand eval scanner metrics 2026-04-29 23:02:54 -07:00
Vincent Koc 201713c9ed chore(deps): hold undici on node20-compatible line 2026-04-29 22:54:52 -07:00
Vincent Koc 2a5638e05b Revert "chore(deps): update undici to v8"
This reverts commit 8d5e7b2d4d.
2026-04-29 22:54:47 -07:00
Patrick Erichsen 82a85ad21e Merge pull request #1935 from openclaw/pe/clawhub-unban-moderation-skill
feat: add clawhub unban command
2026-04-29 22:50:28 -07:00
Vincent Koc f3c060c360 fix(security-dataset): adapt oversized export batches 2026-04-29 22:49:17 -07:00
Vincent Koc 8d5e7b2d4d chore(deps): update undici to v8 2026-04-29 22:48:47 -07:00
Vincent Koc 2325c21108 fix(security-dataset): include export entry names in parser errors 2026-04-29 22:43:51 -07:00
Vincent Koc ad53229985 chore(repo): normalize workflow hygiene 2026-04-29 22:41:29 -07:00
Patrick Erichsen 886a38cb8b feat: add clawhub unban command 2026-04-29 22:40:46 -07:00
Vincent Koc 1a9d80a43d merge: sync testbox setup with latest main
* origin/main:
  fix(security-dataset): compress batched export output
2026-04-29 22:33:39 -07:00
Vincent Koc 0a9f969775 merge: sync testbox setup with main
* origin/main:
  fix(security-dataset): align export batch types
  fix(security-dataset): keep batch output smaller
  test(security-dataset): use node environment for export parser
  fix(security-dataset): batch export pages server-side

# Conflicts:
#	scripts/security-dataset/convexExport.test.ts
2026-04-29 22:33:24 -07:00
Vincent Koc 9917881331 fix(security-dataset): compress batched export output 2026-04-29 22:33:09 -07:00
Vincent Koc eec9702fa3 fix(security-dataset): align export batch types 2026-04-29 22:30:14 -07:00
Vincent Koc 4a09eafe42 fix(security-dataset): keep batch output smaller 2026-04-29 22:27:58 -07:00
Vincent Koc 076b938724 fix(test): isolate Convex export zip fixture 2026-04-29 22:27:02 -07:00
Vincent Koc 33b921af29 test(security-dataset): use node environment for export parser 2026-04-29 22:24:29 -07:00
Vincent Koc 8230c1e365 fix(security-dataset): batch export pages server-side 2026-04-29 22:22:17 -07:00
Vincent Koc 3bfdbfc004 chore(testbox): add Blacksmith runner setup 2026-04-29 22:21:18 -07:00
Patrick Erichsen b48b95b0c1 Merge remote-tracking branch 'origin/main' into pe/clawscan 2026-04-29 22:16:24 -07:00
Vincent Koc 9ebf7d7bde feat(security-dataset): ingest Convex exports locally 2026-04-29 22:14:21 -07:00
Vincent Koc 55dc372ecf fix(security-dataset): type Convex output parser fallback 2026-04-29 22:08:38 -07:00
Vincent Koc 667c69a28b feat(security-dataset): add snapshot time windows 2026-04-29 22:08:38 -07:00
Vincent Koc e8b2aa558c fix(ui): honor cleared security overrides 2026-04-29 22:06:31 -07:00
Vincent Koc 415c8e182e fix(security-dataset): parse matching Convex output 2026-04-29 22:03:04 -07:00
Patrick Erichsen 52831cbc2b fix: restore legacy clawscan details 2026-04-29 21:58:10 -07:00
Vincent Koc 05c6409c96 feat(security-dataset): expose dataset lineage query 2026-04-29 21:55:58 -07:00
Vincent Koc c8585875bd fix(security-dataset): add snapshot manifest lineage 2026-04-29 21:50:30 -07:00
Vincent Koc 4a9ae92d54 fix(security): tighten destructive delete gate 2026-04-29 21:47:04 -07:00
Vincent Koc 8ac5881b4f fix(security-dataset): retry invalid export pages 2026-04-29 21:45:16 -07:00
Vincent Koc ef340c047b fix(security): reduce execfile scanner noise 2026-04-29 21:44:14 -07:00
Vincent Koc 1e6f9bd44c fix(security-dataset): enforce sharded export limits 2026-04-29 21:43:00 -07:00
Vincent Koc 0150b384a7 fix(security-dataset): stream sharded exports 2026-04-29 21:37:34 -07:00
Vincent Koc 3989cd8126 docs(security): pin publish workflow examples 2026-04-29 21:35:32 -07:00
Vincent Koc b90a43adcb fix(security): flag unsafe moderation patterns 2026-04-29 21:33:32 -07:00
Vincent Koc 94102e28f5 fix(security-dataset): parse large Convex pages 2026-04-29 21:28:21 -07:00
Vincent Koc 463e9b3fa7 fix(security-dataset): handle large export pages 2026-04-29 21:26:16 -07:00
Vincent Koc 09820d0d1c fix(security-dataset): keep exports internal 2026-04-29 21:19:23 -07:00
Patrick Erichsen faead0e25c fix: remove unreachable scanner report branches 2026-04-29 21:09:37 -07:00
Patrick Erichsen 0a64b977cb test: update clawscan report expectations 2026-04-29 21:02:50 -07:00
Patrick Erichsen 1e81388560 style: format clawscan UI files 2026-04-29 21:02:50 -07:00
Patrick Erichsen 3ab5762dca feat: ui updates 2026-04-29 21:02:50 -07:00
Patrick Erichsen e2d187b3d5 feat: clawscan seed + frontend 2026-04-29 21:02:50 -07:00
Patrick Erichsen afdac4a6a3 feat: preserve SkillTester raw corpus snapshot 2026-04-29 21:02:50 -07:00
Patrick Erichsen 5dfcd896e9 feat: add SkillTester ClawHub corpus 2026-04-29 21:02:50 -07:00
Vincent Koc d91c4804ce fix(ci): harden CodeQL light coverage 2026-04-29 21:00:02 -07:00
Vincent Koc 59e28c7831 feat: add security dataset eval runner 2026-04-29 20:59:32 -07:00
Vincent Koc a0713e1833 feat: add security dataset snapshots 2026-04-29 20:57:06 -07:00
Vincent Koc ca19f31816 chore(deps): ignore incompatible auth core bumps 2026-04-29 20:56:05 -07:00
Vincent Koc 87da4ec65a chore(deps): update GitHub Actions pins 2026-04-29 20:50:40 -07:00
Vincent Koc d9b419b21b fix(deps): pin undici to ci-compatible line 2026-04-29 20:44:04 -07:00
Vincent Koc bb94325679 chore(deps): complete major dependency updates 2026-04-29 20:38:54 -07:00
Vincent Koc 88f8ca2d29 chore(deps): update dependency drift 2026-04-29 20:34:20 -07:00
Vincent Koc 54ed3c58a1 ci: add lightweight CodeQL scans 2026-04-29 20:23:59 -07:00
Vincent Koc ea35420eed chore(deps): enable dependency update automation 2026-04-29 20:23:44 -07:00
Vincent Koc 2520da134c fix(deps): remediate vulnerable packages 2026-04-29 20:22:20 -07:00
Vincent Koc 0b2de12e04 chore: add Patrick to secure code ownership 2026-04-29 20:14:44 -07:00
Vincent Koc a8326517ad chore: add secops code ownership 2026-04-29 20:11:07 -07:00
Val Alexander 7bef2a0b65 fix: remove card link hover underlines
Remove the inherited global hover underline from full-card link surfaces while preserving normal inline link behavior.

Validated with targeted formatter/lint checks and a local browser hover pass across home category, carousel, trending, skills, plugins, and users card/list surfaces.
2026-04-29 03:40:13 -05:00
Val Alexander 22bb94cee2 fix: restore ClawHub public UI
Restore the public header, hero, featured carousel, Trending Now, category grid, footer, and UI design-contract guardrails. Remove tweakcn/custom visual overlay settings and stale density preference plumbing, while preserving reviewed search/typeahead behavior and latest review fixes.
2026-04-29 02:52:34 -05:00
Patrick Erichsen b3c42b661b Merge pull request #1882 from openclaw/pe/plugin-management-tools
Add plugin curation to management
2026-04-28 22:31:44 -07:00
Vincent Koc f72179f37d chore(ci): update package publish artifact action
Update package publish artifact upload to the Node 24-ready artifact action and align the stale skills default-sort test with current filter behavior.
2026-04-28 22:31:33 -07:00
Patrick Erichsen 1d79f78426 feat: add plugin curation to management 2026-04-28 22:30:43 -07:00
Patrick Erichsen 6209fe3fff Merge pull request #1880 from openclaw/pe/featured-plugin-curation
feat: add featured plugin curation
2026-04-28 22:19:21 -07:00
Patrick Erichsen a7d1701f5a feat: add featured plugin curation 2026-04-28 22:18:35 -07:00
Patrick Erichsen 52da4954f6 Merge pull request #1871 from openclaw/pe/skills-plugins-typeahead
[codex] Add skills/plugins search typeahead
2026-04-28 21:24:26 -07:00
Patrick Erichsen 0ee5958f7a merge: sync with origin main 2026-04-28 21:18:34 -07:00
Patrick Erichsen 5d01b99adb Merge pull request #1878 from openclaw/pe/clawhub-rescan-guidance
feat: add ClawHub rescan guidance workflow
2026-04-28 20:09:30 -07:00
Patrick Erichsen 5dc834c27e feat: add ClawHub rescan guidance workflow 2026-04-28 20:07:52 -07:00
Patrick Erichsen 82b9a69dad Merge pull request #1875 from openclaw/pe/settings-stars
fix: move stars link into settings
2026-04-28 19:50:45 -07:00
Vincent Koc 064804e2d3 fix: make package publish retries idempotent 2026-04-28 19:29:39 -07:00
Patrick Erichsen 6c0163f9f2 feat: add skills plugins search typeahead 2026-04-28 18:33:20 -07:00
Patrick Erichsen 04a862d2b2 fix: move stars link into settings 2026-04-28 18:32:09 -07:00
Patrick Erichsen a7fc4bbae2 Merge pull request #1874 from openclaw/pe/oxfmt-pr-check
ci: check oxfmt on pull requests
2026-04-28 18:22:56 -07:00
Patrick Erichsen 4701c555f3 ci: check oxfmt on pull requests 2026-04-28 18:16:32 -07:00
Patrick Erichsen c1f167721b Merge pull request #1873 from openclaw/pe/fix-skill-upload
fix: add skill upload button to header
2026-04-28 17:37:14 -07:00
Patrick Erichsen 1a94744484 Update $name.tsx 2026-04-28 17:37:02 -07:00
Patrick Erichsen 9a5cfeee85 Update SkillHeader.tsx 2026-04-28 17:29:14 -07:00
Patrick Erichsen e69b7d4501 fix: add skill upload button to header 2026-04-28 17:25:30 -07:00
Patrick Erichsen ecf09b868a Merge pull request #1872 from openclaw/pe/clawhub-cli-0.12.1
chore(release): prepare clawhub cli 0.12.1
2026-04-28 16:53:37 -07:00
Patrick Erichsen 4d16472f5b chore(release): prepare clawhub cli 0.12.0 2026-04-28 16:52:53 -07:00
Patrick Erichsen 2e5ffdc565 Merge pull request #1861 from openclaw/pe/rescan
feat: add owner rescan security surfaces
2026-04-28 16:32:26 -07:00
Peter Steinberger 4e13e729fb ci: narrow ClawSweeper dispatch cancellation 2026-04-28 11:53:07 +01:00
Peter Steinberger d17e100cca ci: harden clawsweeper dispatch workflow 2026-04-28 11:35:24 +01:00
Peter Steinberger c732b38569 ci: debounce clawsweeper dispatch metadata 2026-04-28 11:31:51 +01:00
Patrick Erichsen 81ca04662c fix: prevent mobile install copy overlap 2026-04-28 01:20:09 -07:00
Patrick Erichsen ce69ab6a38 fix: polish mobile rescan security surfaces 2026-04-28 01:17:13 -07:00
Patrick Erichsen a28d94c345 feat: show in progress scans 2026-04-28 00:52:51 -07:00
Peter Steinberger 3701733797 fix: normalize vt engine stats before caching 2026-04-28 08:47:50 +01:00
Peter Steinberger b8ba595d06 docs: note vt code insight calibration 2026-04-28 08:40:09 +01:00
Patrick Erichsen ef2846b2e4 feat: add owner rescan security surfaces 2026-04-28 00:39:44 -07:00
Peter Steinberger 232e429dee fix: clear uncorroborated vt suspicious state 2026-04-28 08:39:39 +01:00
Deepak Jain 932155cb8f docs: clarify static scan suppression gate 2026-04-28 08:39:17 +01:00
Deepak Jain d855d09ab0 fix: calibrate vt code insight moderation
Refs #1830
2026-04-28 08:39:17 +01:00
Peter Steinberger 16e87c147d ci: harden ClawSweeper dispatcher credentials 2026-04-28 06:48:40 +01:00
Peter Steinberger cbe22e70b9 ci: fix ClawSweeper dispatcher payload 2026-04-28 06:44:28 +01:00
Peter Steinberger 837331c967 ci: add ClawSweeper event dispatcher 2026-04-28 06:43:39 +01:00
Peter Steinberger 6ce443496d fix: keep package list queries single-page 2026-04-28 06:28:27 +01:00
Peter Steinberger 8fd4f3b051 ci: fix production smoke coverage 2026-04-28 06:18:40 +01:00
Peter Steinberger a2153909da fix: avoid plugin catalog query limits 2026-04-28 06:13:57 +01:00
Deepak Jain 75e1b4633e fix: constrain plugin catalog queries (#1842)
Refs #1699.

Use family-indexed plugin catalog paths instead of broad package scans:
- `/api/v1/plugins` merges separate `code-plugin` and `bundle-plugin` list streams with an endpoint-specific cursor.
- `/api/v1/plugins/search` searches both plugin families directly, dedupes, sorts, and limits results.
- Keeps generic `/api/v1/packages` behavior unchanged.

Validation:
- bunx vitest run convex/httpApiV1.handlers.test.ts convex/packages.public.test.ts
- bunx tsc --noEmit
- bunx tsc -p packages/schema/tsconfig.json --noEmit
- bunx tsc -p packages/clawhub/tsconfig.json --noEmit
- bun run lint
- git diff --check
2026-04-28 06:03:15 +01:00
Patrick Erichsen 4cda4a1fa4 Merge branch 'main' of https://github.com/openclaw/clawhub into pe/rescan
# Conflicts:
#	convex/skills.ts
2026-04-27 21:15:31 -07:00
Patrick Erichsen 5fce3ca2f4 Merge pull request #1850 from openclaw/pe/shadcn-ui-primitives
feat: adopt shadcn-managed ui primitives
2026-04-27 20:52:41 -07:00
Patrick Erichsen 87bca06939 feat: adopt shadcn-managed ui primitives 2026-04-27 20:48:15 -07:00
Deepak Jain bf25b38c39 fix: tolerate stale auth in star status (#1843)
Refs #1819.

Read-only star status queries now treat stale, missing, deleted, or deactivated auth users as not starred instead of throwing. Star and unstar mutations still require an active authenticated user.

Validated locally:
- bunx vitest run convex/stars.test.ts convex/lib/access.test.ts
- bunx tsc -p packages/schema/tsconfig.json --noEmit
- bunx tsc -p packages/clawhub/tsconfig.json --noEmit
- bunx tsc --noEmit
- git diff --check origin/main...HEAD
2026-04-28 04:23:05 +01:00
Deepak Jain f20dd624a5 fix: flag exposed secrets in skill docs (#1847)
* fix: flag exposed secrets in skill docs

Refs #1760

* fix: harden secret evidence redaction
2026-04-28 03:39:36 +01:00
Peter Steinberger 8752e4bb7e chore(release): prepare clawhub cli 0.11.0 2026-04-28 02:26:56 +01:00
Peter Steinberger bc06c472e4 fix(packages): authenticate repository lookups 2026-04-28 02:20:35 +01:00
Peter Steinberger 09fa7daa7b docs: clarify skill monetization support 2026-04-28 01:52:37 +01:00
Peter Steinberger e21ca80a7d docs: document public catalog reuse 2026-04-28 01:21:46 +01:00
Peter Steinberger 456f4db74d fix(search): widen lexical fallback coverage 2026-04-27 22:46:22 +01:00
Patrick Erichsen 7014a53fdf Merge pull request #1837 from openclaw/pe/convex-ai-files
[codex] Add Convex AI guidance and skills
2026-04-27 14:25:54 -07:00
Patrick Erichsen c6c4481ffd chore: keep only Convex agent skills 2026-04-27 14:20:04 -07:00
Patrick Erichsen 02b7d10af8 chore: refresh agent skill state 2026-04-27 14:12:01 -07:00
Patrick Erichsen c5a6d2700f chore: track repo agent skills 2026-04-27 14:10:42 -07:00
Val Alexander 205db67e99 feat(packages): expose package soft delete API 2026-04-27 16:07:19 -05:00
Patrick Erichsen f406c5bf16 ci: use pinned Convex CLI for AI file updates 2026-04-27 13:59:30 -07:00
Patrick Erichsen add0d13bef ci: run Convex AI update at midnight Pacific 2026-04-27 13:56:48 -07:00
Peter Steinberger 7c5b8b2a20 fix(search): boost exact slug matches 2026-04-27 21:56:24 +01:00
Patrick Erichsen d3ed4434b9 feat: seed local rescan fixtures 2026-04-27 13:53:14 -07:00
Patrick Erichsen 7f7d6676c3 ci: update Convex AI files weekly 2026-04-27 13:52:27 -07:00
Patrick Erichsen aff186bbcf chore: add Convex AI guidance files 2026-04-27 13:46:14 -07:00
Patrick Erichsen 1c430cc11d feat: add scanner-specific security pages 2026-04-27 13:44:37 -07:00
Peter Steinberger e2cb7dfe4e fix(search): fall back when embeddings fail 2026-04-27 21:34:42 +01:00
Patrick Erichsen fb3bcbafb1 feat: show owner flagged inventory on dashboard 2026-04-27 13:27:08 -07:00
Patrick Erichsen e6c3d6ff28 feat: add owner rescan requests 2026-04-27 13:25:59 -07:00
Peter Steinberger 57970579cf fix(search): widen vector candidate pool 2026-04-27 21:10:13 +01:00
Peter Steinberger 2ddc52c0b0 test: fix root test and typecheck gates 2026-04-27 20:51:00 +01:00
Peter Steinberger e63031c452 fix(skill-install): use openclaw-compatible slug 2026-04-27 20:42:22 +01:00
Peter Steinberger e3c772d90b fix(search): preserve expanded candidate scoring 2026-04-27 20:41:20 +01:00
Peter Steinberger 73e26e51c1 fix(moderation): narrow webhook flag 2026-04-27 20:40:00 +01:00
Peter Steinberger e7a1e9937b fix(moderation): keep skill status on latest version 2026-04-27 20:39:02 +01:00
Peter Steinberger d5776f8499 fix(skills): separate historical detail tags 2026-04-27 20:37:41 +01:00
Peter Steinberger 280352d959 docs: thank recent contributors 2026-04-27 20:36:56 +01:00
Deepak Jain bd375f6a93 fix: reduce env scan false positives
Allow declared env vars used with network API calls without weakening broad env scraping or exfiltration findings.\n\nCloses #1790
2026-04-27 20:36:40 +01:00
Deepak Jain 422f6d4e08 docs: surface package publish flow
Document code-plugin package publish required fields and a minimal manifest.\n\nCloses #1796
2026-04-27 20:34:56 +01:00
Peter Steinberger dd111cacee fix(api): restore public skills listing
Fixes #1722
Fixes #1739
2026-04-27 20:33:51 +01:00
Peter Steinberger ffa83db48a fix: stabilize package plugin search 2026-04-27 20:26:49 +01:00
Momo 96c7ab1aaa fix(skills): prevent backport publishes from clobbering the latest tag (#1832)
Merged via squash.

Prepared head SHA: bc2ef22165
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Reviewed-by: @momothemage
2026-04-27 20:33:57 +08:00
Momo 743fa3abba fix(skill-install): stabilize install surface layout
CSS-only stabilization for the skill install surface.

- neutralize Radix scroll-lock body compensation now that the app reserves scrollbar gutter globally
- make the install surface span the full hero width and keep the two install panels balanced
- reserve stable space for prompt feedback and prompt preview content to avoid toggle reflow

Verified locally:
- bunx tsc -p packages/schema/tsconfig.json --noEmit
- bunx tsc -p packages/clawhub/tsconfig.json --noEmit
- bun run build
2026-04-24 15:31:17 -05:00
Val Alexander 6c079e93c2 fix: satisfy skill install typecheck
Repair the skill install surface follow-up typecheck issue after #1800 merged.

- replace the unused local exhaustiveness sentinel in `skillDetailUtils` with a shared `assertNever` helper
- keep the package-manager switch exhaustive without tripping `noUnusedLocals`
2026-04-23 15:36:14 -05:00
Val Alexander 23a109c037 feat: add skill install prompt surface
Add a dedicated skill install surface that pairs OpenClaw prompt-driven install with visible CLI commands.

- add Install with OpenClaw and CLI Commands panels to the skill detail page
- add Copy Prompt modes for Install Only and Install & Setup plus package-manager switching for the ClawHub CLI command
- add regression coverage for the new surface and make the repo build path use the working Vite invocation
2026-04-23 15:29:43 -05:00
Patrick Erichsen f28c1745f9 Merge pull request #1794 from openclaw/fix/vercel-image-allow-svg
fix(security): allow SVGs through image optimizer so badges render
2026-04-22 22:52:27 -07:00
Patrick ErichsenandClaude Opus 4.7 4fe275eb50 fix(security): enable safe SVG handling so shields.io badges render
vercel.json currently allow-lists SVG-only hosts (img.shields.io,
shields.io, badgen.net, flat.badgen.net) while dangerouslyAllowSVG:
false rejects every SVG source. Those two settings are incompatible,
and every badge in every README on production is returning 400
INVALID_IMAGE_OPTIMIZE_REQUEST (e.g. the license badge on
/plugins/@opik/opik-openclaw).

Switch to the pattern Vercel documents for safely serving SVGs in
their NEXTJS_SAFE_SVG_IMAGES conformance rule:

- dangerouslyAllowSVG: true  — lets the optimizer accept SVG inputs
- contentDispositionType: attachment  — forces download instead of
  inline document rendering if someone navigates directly to the
  /_vercel/image URL (the only context where SVG scripts would run)
- contentSecurityPolicy: script-src 'none'; sandbox;  — blocks script
  execution in the response

Defense in depth: browsers already sandbox SVGs loaded through <img>
so scripts don't run there anyway; the CSP + attachment header cover
the edge case of someone opening the optimizer URL directly. Net
security is equivalent to rejecting SVGs, but badges actually render.

Docs: https://vercel.com/docs/conformance/rules/NEXTJS_SAFE_SVG_IMAGES

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:50:43 -07:00
Patrick Erichsen c0d2ac7ac0 Merge pull request #1793 from openclaw/fix/image-proxy-xss
fix(security): proxy README images via Vercel Image Optimization
2026-04-22 22:42:32 -07:00
Patrick ErichsenandClaude Opus 4.7 d6d4028660 refactor(security): swap ProxiedImg component for rehype plugin
Replaces the React <img> wrapper with a tiny rehype plugin that rewrites
image srcs in the HAST. Same behavior (external http(s) URLs routed
through /_vercel/image; local/relative/data: URIs pass through), less
surface area:

- One shared plugin wired into both MarkdownPreview and SkillDetailTabs
  via rehypePlugins instead of a components override at each call site
- Dropped ProxiedImg.tsx + its 7 unit tests; the two integration tests
  in MarkdownPreview.test.tsx still assert the proxy URL shape for both
  <img> and ![](url) syntax
- Stopped reading <img width="..."> for the proxy's w= param. Vercel
  requires w to match a value in vercel.json sizes, so arbitrary README
  widths (e.g. width="200") would have been rejected. Always w=1024 now;
  the HTML width attribute still drives layout

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:32:21 -07:00
Patrick ErichsenandClaude Opus 4.7 82ae30d940 fix(security): proxy README images via Vercel Image Optimization
Closes the XSS / IP-leak surface from rendering third-party README
images directly on clawhub.ai. Routes external http(s) <img> sources
through Vercel's /_vercel/image endpoint, which enforces a host
allow-list, rejects SVG by default, and re-encodes rasters to webp.

Docs: https://vercel.com/docs/image-optimization

- vercel.json: add `images` config — host allow-list (raw.githubusercontent,
  shields.io, etc., based on NuGet's published README allow-list),
  dangerouslyAllowSVG=false, formats=[webp], 1d minimum cache TTL.
- src/components/ProxiedImg.tsx: small wrapper that rewrites external
  http(s) src URLs to /_vercel/image?url=...&w=...&q=75. Local paths,
  relative paths, and data: URIs pass through unchanged.
- MarkdownPreview + SkillDetailTabs: pass ProxiedImg as the `img`
  component override to react-markdown — covers both raw HTML <img>
  and markdown ![](url) syntax.
- package.json: drop unused `next` dep (vestigial from staging merge,
  zero imports anywhere; doesn't affect next-themes).

Tests: 1028/1028 (was 1017, added 11 — ProxiedImg unit tests +
markdown integration tests covering proxied vs passthrough paths).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:12:40 -07:00
Patrick Erichsen b53813a5a7 Merge pull request #1792 from openclaw/fix/lint-cleanup-staging-fallout
chore(lint): clean up 70 oxlint errors from staging merge #1573
2026-04-22 21:39:34 -07:00
Patrick ErichsenandClaude Opus 4.7 87469792d5 fix(typecheck): clear remaining tsc errors on main
8 typecheck errors that have been on main alongside the lint debt:

- convex/apiSurface.typecheck.ts: drop two stale @ts-expect-error
  directives. The `increment` references they guarded no longer
  exist (functions renamed to *Internal); runtime internal-only
  enforcement is preserved by `internalMutation`.
- src/components/MarkdownPreview.tsx: cast createHighlighter result
  to AnyHighlighter, narrow loadHighlighter return via the local
  promise variable, type baseRehype + memoized rehypePlugins as
  PluggableList (drops `as const` readonly mismatch with
  ReactMarkdown's prop type).
- src/lib/theme.test.tsx: rename remaining "hub" usages to "claw"
  (theme families collapsed to one in PR #1573 — the last "hub"
  references in the harness button + applyTheme call would never
  compile under the current ThemeName type).
- src/lib/packageApi.test.ts: add `?.` on the nullable result.

Full suite: lint 0, tests 1017/1017, typecheck 0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 21:33:17 -07:00
Patrick ErichsenandClaude Opus 4.7 9e7407cd84 test: update stale assertions left over from staging merge
Two pre-existing test failures on main, both caused by UI/data
changes in PR #1573 that the tests weren't updated for:

- theme.test.tsx: expected stored theme "hub" to round-trip, but
  the staging merge collapsed all families into a single "claw"
  theme — unknown families now fall back to "claw". Test now
  asserts the legacy fallback behavior it claims to test.
- skill-detail-page.test.tsx: gated on the platform license
  summary text, which was removed from SkillMetadataSidebar in
  4d1a08b. Drop the obsolete assertion; the report-button
  findByRole on the next line provides the same render-wait.

Full suite: 1017/1017 pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 21:23:29 -07:00
Patrick ErichsenandClaude Opus 4.7 70fd9436cf chore(lint): clean up oxlint errors from staging-merge fallout (#1573)
Fixes 70 oxlint errors that landed in the 2026-04-18 staging merge and
have kept main red ever since. Three rule categories:

- typescript-eslint(no-unnecessary-type-conversion): drop redundant
  String/Number/Boolean wraps + 'as T' casts on values already typed.
- typescript-eslint(consistent-return): unify mixed return paths,
  mostly in useEffect callbacks (early-return vs cleanup-fn) and CLI
  command handlers.
- typescript-eslint(no-unnecessary-type-parameters): drop generics
  used only once in a signature; replace with concrete types.
- Plus a handful of no-unused-vars, no-shadow, and one
  no-redundant-type-constituents (JSX.Element -> ReactNode).

No runtime behavior changes. Full lint clean (0 errors); test suite
shows the same 2 pre-existing failures as main, no new regressions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 21:16:23 -07:00
Vincent Koc 5e7584e032 Merge pull request #1791 from openclaw/fix/markdown-html-passthrough 2026-04-22 20:59:27 -07:00
Patrick ErichsenandClaude Opus 4.7 ea0824878d fix(markdown): render raw HTML + GFM in MarkdownPreview, add shiki highlighting
Plugin/soul READMEs that use raw HTML (e.g. centered logos via
<h1 align="center">, <picture>, <br/>) were rendering as escaped
text because @create-markdown/preview escapes all HTML. Swap the
renderer for react-markdown + remark-gfm + rehype-raw +
rehype-sanitize (GitHub's stack), with rehype-shiki-from-highlighter
for fenced code block syntax highlighting.

Sanitize runs before shiki so user HTML is scrubbed, and shiki's
trusted styled output flows through untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 20:33:54 -07:00
Val Alexander da74b2a382 Update .gitignore 2026-04-22 14:22:53 -05:00
Val Alexander 4787be4eb1 Refresh Open Graph image (#1754)
* Refresh OG image and bust cache version

- Replace the social preview artwork with a new branded SVG and updated PNG
- Add a versioned og image URL in the root head tags to ensure the new asset is served

* Refresh OG image design

- Redesign the social preview graphic for the new ClawHub branding
- Bump the OG image version so the updated asset is served

* fix: refine clawhub og image

* fix: center og logo layout

* fix: emphasize clawhub branding in og image

* Refresh OG image branding

- Update Open Graph artwork and logo asset
- Adjust root metadata to use the new social preview image

* fix: refine clawhub og image

* fix: tighten og image layout

* fix: remove og logo panel

* fix: reduce og logo scale

* fix: align og image to new comp
2026-04-20 21:59:08 -05:00
Gustavo Madeira Santana 89246f1927 chore(ui): remove gap before hero cycled words 2026-04-19 13:25:56 -04:00
Val Alexander f4ddccbead enchance: mobile skills ux (#1737) 2026-04-18 20:10:03 -05:00
Val Alexander 3cafcbf873 Mobile search icon + system theme on first load
- Initialize root theme data from stored selection before paint
- Hide the search label on mobile and tighten button padding
2026-04-18 18:40:56 -05:00
Val Alexander 13064a7897 Merge pull request #1731 from openclaw/okcode/fix-mobile-search-button
Fix mobile header branding and add Home link
2026-04-18 17:52:29 -05:00
Val Alexander 194c22f4dd Add branded mobile nav header
- Show the logo mark in the mobile drawer title
- Tighten mobile suggestion spacing on small screens
- Add test coverage for the branded mobile nav header
2026-04-18 17:50:21 -05:00
Val Alexander a693b945fa Add Home link to mobile header navigation
- Insert a Home entry at the top of the mobile menu
- Update header tests to cover the new menu order
2026-04-18 17:40:22 -05:00
Val Alexander 9bef672541 Merge branch 'okcode/polished-card-icons-logo' 2026-04-18 17:28:51 -05:00
Val Alexander 9551cac37b Merge pull request #1729 from openclaw/okcode/fix-settings-update
Stabilize preferences sync and keep diff editor mounted
2026-04-18 17:25:52 -05:00
Val Alexander eb4138fbb3 fix: harden preferences storage sync 2026-04-18 17:24:31 -05:00
Val Alexanderandgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> 5fbead624b Update src/lib/preferences.ts
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-04-18 17:17:18 -05:00
Val Alexander 35094177e6 Keep diff editor mounted when switching view mode
- Remove the diff editor remount on inline vs side-by-side toggles
- Add a regression test to verify the editor stays mounted
2026-04-18 17:04:30 -05:00
Val Alexander faa5c9f2b5 Polish icons and brand mark styling
- Simplify home and settings labels by removing redundant icons
- Swap automation icons to refresh glyphs in sidebars and toolbar
- Add subtle border and shadow treatment to the brand mark
2026-04-18 17:03:35 -05:00
Val Alexander c3314c2d01 Stabilize preference snapshots and storage sync
- Cache localStorage reads to avoid redundant snapshot churn
- Sync updates across tabs and add coverage for preference re-renders
2026-04-18 17:03:15 -05:00
Val Alexander 7dfa19157c Merge pull request #1573 from openclaw/staging
chore: merge staging into main
2026-04-18 16:46:00 -05:00
Val AlexanderandClaude Opus 4.6 44acf86ac1 merge: resolve AGENTS.md conflict — keep both convex-ai and stat migration rules
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 16:45:37 -05:00
Val AlexanderandClaude Opus 4.6 a0ebc1b50a style: spread footer columns evenly across full width
Switch footer grid from auto-sized centered columns to equal 1fr
columns that span the full screen width.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 16:44:28 -05:00
Val AlexanderandClaude Opus 4.6 88dbb69a23 style: adopt darker home-v2 palette globally and unify radius to 8px
Shift all theme variants (claw dark/light, hub dark/light) to the
bolder home-v2 backgrounds (#060608 dark, #faf6f1 light cream).
Harmonize surface, nav-bg, input-bg, and overlay-bg to match.
Set every radius token (--r-lg/md/sm/xs/pill) and home-v2 hardcoded
radii to a single consistent 8px value.
Remove home-v2–specific overrides for app-shell background, navbar
background, footer transparency, and navbar-inner max-width that
previously caused visual divergence between the home page and the
rest of the app.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 16:30:53 -05:00
Val Alexander df9acd27e4 update: styles 2026-04-18 16:28:00 -05:00
Val AlexanderandClaude Opus 4.6 dbd5d4042c fix: restore header logo and compact/center footer
Uncomment the brand logo image in the header navbar and reduce footer
vertical padding, gaps, and margins to ~55% of original height while
centering the grid columns and link text.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 16:17:17 -05:00
Val AlexanderClaude Opus 4.6copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
ebe82b7e18 Improve about page rejection categories (#1728)
* improve about page rejection categories: add icons, fix grid, polish cards

- Add lucide-react icons to each rejection category card for visual scanning
- Fix unbalanced grid layout by removing featured card sizing, using clean 2/3-col grid
- Fix broken hover transitions (var(--transition-fast) was undefined outside reduced-motion)
- Add lift-on-hover effect and icon glow matching home page card patterns
- Render backtick-wrapped text as styled inline code elements
- Improve description text contrast from ~3.5:1 to ~4.8:1 (WCAG AA)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use h3 for card titles to fix heading hierarchy (WCAG 1.3.1)

Change rejection category card titles from <h2> to <h3> since the parent
section already uses <h2> for "Immediate rejection categories". Updates
the matching CSS selector from .about-rule-card h2 to h3.

Also adds tests for renderWithInlineCode helper covering plain text,
single/multiple code spans, empty input, and code-only strings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove white backgrounds from all logo assets

- Remove white backgrounds from clawd-logo.png, clawd-mark.png,
  logo192.png, logo512.png — now transparent PNGs
- Convert white strokes to dark (#1a0808) in both PNGs and logo.svg
  so segments separate cleanly on any background
- Defringe antialiased edges to eliminate white halos
- Regenerate favicon.ico from transparent source
- Update manifest.json background_color from #ffffff to #0a0a0a

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* consolidate logo assets: delete SVGs, use only PNGs with transparent bg

- Delete public/logo.svg, public/og.svg, src/logo.svg (dead/unused SVGs)
- Remove logo.svg favicon link from __root.tsx (favicon.ico remains)
- Remove white backgrounds from clawd-logo.png and clawd-mark.png
- Convert white strokes to dark (#1a0808), defringe antialiased edges
- Regenerate logo192.png, logo512.png, favicon.ico from clean sources
- Only canonical logo files are now clawd-logo.png and clawd-mark.png

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style: widen navbar search bar and polish hero section

Let the search bar span the full width between brand and theme toggle
by removing the oversized right-column minimum and theme-toggle min-width.
Widen the hero search container, subtitle, and tighten vertical padding
for a sleeker feel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use :is(h2, h3) selector for about-rule-card headings

The /souls page reuses about-rule-card with <h2> elements. Using
:is(h2, h3) ensures both heading levels get styled consistently.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: slot machine Easter egg on hero label triple-click

Triple-clicking "BUILT BY THE COMMUNITY" triggers a casino-style slot
machine across all 3 headline words. Reels spin and stop sequentially
with a 1/13 jackpot chance. Winning fires a confetti celebration with
golden text glow. Auto-resets after the animation completes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add cooldown, longer celebration, and Hack x3 lobster jackpot

- 18s cooldown after a win, 3s after a loss to prevent spam
- Win celebration extended to 10s for screenshot opportunities
- Hack x3 jackpot triggers aquatic theme: cyan/teal text glow,
  ocean-colored confetti with bubble and claw particles, and the
  lobster logo fades in behind the headline

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: tune slot machine odds to 1/25 any jackpot, 1/100 Hack jackpot

Replace pure random picks with controlled probability: 4% chance of
any jackpot per spin, with 25% of jackpots being Hack (= 1% overall).
Non-jackpot spins re-roll accidental triple matches to keep odds exact.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: clean up slot timers on unmount, fix about-grid specificity

Add useEffect cleanup to clear slot machine timers/intervals when
the home route unmounts mid-animation. Fix about-grid media query
specificity by including .about-panel-categories .about-grid to
override the higher-specificity base rule.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-04-18 16:07:48 -05:00
Val AlexanderandClaude Opus 4.6 4c566268a9 fix: hide logo, clean up rejection categories layout (#1727)
* fix: hide logo, use ClawHub as home link, and clean up rejection categories grid

Comment out the brand logo image for now, rename "Immediate rejection
categories" to "Rejection Categories", remove the featured card variant,
and switch to an auto-fill grid so cards spread evenly at full width.
Add overflow: visible on the categories panel to prevent hover shadow
clipping.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: impose max page width on home page using --page-max (1536px)

Constrain .home-v2-main to max-width: var(--page-max) and center it
with margin-inline: auto. Extend the home page background color to the
full viewport via .app-shell:has(.home-v2-main) for both light and dark
themes so the background bleeds edge-to-edge beyond the content column.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove extra footer padding and ensure full-width nav/footer for boxed layout

Zero out the outer .site-footer padding and set background to transparent
on home-v2 pages so the app-shell background bleeds through edge-to-edge.
Remove the redundant light-mode footer background override (app-shell
background already covers it). Nav and footer now visually span full
viewport width while .home-v2-main content stays boxed at --page-max.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: reduce carousel card hover effect and increase track padding

The carousel cards were getting clipped by the parent overflow:hidden
container. Reduce the hover transform from translateY(-4px) scale(1.01)
to translateY(-2px) and shrink box-shadow spread across all theme
variants. Increase carousel track top padding from 4px to 12px to
accommodate the upward shift without cutoff.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address review feedback — mobile brand, category grid, hover drama

- Keep brand name visible on mobile (remove display:none for
  .brand-name-responsive at ≤639px) so the home link is always
  discoverable. Add TODO comment on the commented-out logo block.
- Add .about-panel-categories .about-grid to the ≤640px media query
  so the category grid correctly collapses to single-column on mobile.
- Bump carousel card hover to translateY(-3px) with 0 6px 24px shadow
  for a slightly more dramatic lift — still within the 12px top / 48px
  bottom track padding so nothing clips.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: remove unused footer divider element

The site-footer-divider was already hidden via CSS (display: none) on
home-v2 pages. Remove the element entirely since it serves no purpose.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 14:37:43 -05:00
Val Alexander e54fc1939a fix: normalize nav and footer layout 2026-04-18 13:41:36 -05:00
Momoandmomothemage 530e39eedc refactor: extract readCanonicalStat and add structural guards for stat field migration (#1709)
Merged via squash.

Prepared head SHA: e92817f66f
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Reviewed-by: @momothemage
2026-04-17 17:44:32 +08:00
copilot-swe-agent[bot]andBunsDev 8b87c31a99 Merge remote-tracking branch 'origin/main' into staging
# Conflicts:
#	src/routes/management.tsx
#	src/routes/settings.tsx

Co-authored-by: BunsDev <68980965+BunsDev@users.noreply.github.com>
2026-04-17 08:49:21 +00:00
Momo f7bc8b6349 fix(stats): fix skill stat field sync direction and reconcile logic (#1704)
Merged via squash.

Prepared head SHA: e814278382
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Co-authored-by: momothemage <35096042+momothemage@users.noreply.github.com>
Reviewed-by: @momothemage
2026-04-16 20:04:13 +08:00
5b8f09167a fix(api): align inspect security snapshot with static scan moderation
Include static scan results in the skill version security snapshot so inspect/API responses reflect the same moderation-relevant signal already used elsewhere. Also add regression coverage for suspicious, malicious, and static-only scan combinations.

Co-authored-by: davida-ps <232346510+davida-ps@users.noreply.github.com>
Co-authored-by: Luke <92253590+ImLukeF@users.noreply.github.com>
2026-04-16 18:39:39 +10:00
hugh 17fbd13bc9 fix(cli): use explorer on Windows to preserve auth URL params
On Windows, opening auth URLs via `cmd /c start` can truncate query parameters because `&` is treated as a command separator. Use `explorer` instead so the browser opener gets the full URL without shell parsing, and cover the Windows spawn args in the CLI UI test.

Co-authored-by: hugh <1012760428@qq.com>
2026-04-16 14:00:03 +10:00
aab7dc9ba4 fix(upload): fall back to octet-stream for empty Content-Type
Handle browser uploads that provide an empty MIME type by falling back to `application/octet-stream` before sending the storage request.

Co-authored-by: Arthur Katcher <192321283+arthurkatcher@users.noreply.github.com>
Co-authored-by: Luke <92253590+ImLukeF@users.noreply.github.com>
2026-04-16 12:54:51 +10:00
ImLukeF dde8796790 feat: tag skills needing sensitive credentials 2026-04-14 20:09:24 +10:00
Val Alexander acc6d292de Home v2 styles: layout, theme & navbar tweaks
Add and refine styles for the Home V2 UI: introduce navbar search/home styles, motto and headline variants, section copy/eyebrow rules, discovery and categories layouts, and responsive grid stacking. Adjust hv2 color variables (text-secondary/tertiary) and move category border to the grid element; update spacing/alignment for carousel and section headers. Add light/dark theme overrides to improve navbar, tabs and search contrast and hover states. Misc minor typographic and spacing refinements for a more cohesive Home V2 appearance.
2026-04-14 00:27:04 -05:00
ImLukeF 2236ed7be1 feat: add org profile editing 2026-04-14 14:10:52 +10:00
ImLukeF f6fb7ccfc0 Revert "Reapply "feat: allow moderators to transfer skill publishers (#1663)""
This reverts commit b73758c7c8.
2026-04-14 13:39:49 +10:00
ImLukeF b73758c7c8 Reapply "feat: allow moderators to transfer skill publishers (#1663)"
This reverts commit fbc07c5617.
2026-04-14 13:38:30 +10:00
ImLukeF fbc07c5617 Revert "feat: allow moderators to transfer skill publishers (#1663)"
This reverts commit 80e5aec577.
2026-04-14 13:37:08 +10:00
Luke 80e5aec577 feat: allow moderators to transfer skill publishers (#1663) 2026-04-14 13:36:28 +10:00
Val AlexanderandNova f869b31ad6 fix: remove leftover theme-family UI remnants
- drop mobile theme-family section in header
- remove unused theme-family settings bindings

Co-authored-by: Nova <nova@openknot.ai>
2026-04-13 22:15:06 -05:00
Val AlexanderandNova 9a853f2fcc chore: update lockfile and favicon
- refresh bun.lock after dependency reinstall
- include favicon update

Co-authored-by: Nova <nova@openknot.ai>
2026-04-13 22:13:03 -05:00
Val AlexanderandNova b4a7540157 feat: homepage redesign + unified theme + UI polish
- Redesign homepage with hero, search, featured carousel, categories, proof bar, trending
- Add cream/peach/tan light mode palette with inset-shadow pattern (dark + light)
- Remove Hub theme — single Claw theme only (light/dark mode toggle remains)
- Semi-rounded radius system (Claw × Hub midpoint: 4/7/10px)
- Consistent button radius site-wide (--r-btn: 4px), zero makeshift buttons
- Add VITE_FEATURE_SOULS env flag (default: false) to gate Souls pages
- Hide Souls from nav, footer, and homepage categories
- Remove theme family toggle from Header + Settings
- Widen page max to screen-2xl (1536px)
- Slow featured carousel 15% (40s → 46s)

Co-authored-by: Nova <nova@openknot.ai>
2026-04-13 21:59:23 -05:00
Val Alexander 0ea1127a2b fix: refine header and about responsiveness (#1661) 2026-04-13 13:03:40 -05:00
Val Alexander aeab23a6d6 Fix dark-mode styling for skills filter chips (#1660)
- Add readable dark-surface and active-state colors to filter chips
- Cover the toolbar styling with a jsdom test
2026-04-13 13:01:37 -05:00
1095 changed files with 306051 additions and 36507 deletions
+345
View File
@@ -0,0 +1,345 @@
---
name: autoreview
description: "Pre-commit/ship code review: Codex default; optional Claude, Pi, Droid, Copilot, or OpenCode."
---
# Auto Review
Run the bundled structured review helper as a closeout check. This is code review, not Guardian `auto_review` approval routing.
Codex review is the default when no engine is set. It uses `gpt-5.5` by default, usually delivers the best review results, and should remain the normal final closeout engine. Claude review is optional and uses `claude-fable-5` by default.
Use when:
- user asks for Codex review / Claude review / Pi review / Droid review / OpenCode review / autoreview / second-model review
- after non-trivial code edits, before final/commit/ship
- reviewing a local branch or PR branch after fixes
## Contract
- Treat review output as advisory. Never blindly apply it.
- Verify every finding by reading the real code path and adjacent files.
- Read dependency docs/source/types when the finding depends on external behavior.
- Reject unrealistic edge cases, speculative risks, broad rewrites, and fixes that over-complicate the codebase.
- Prefer small fixes at the right ownership boundary; no refactor unless it clearly improves the bug class.
- When an accepted finding shows a bug class or repeated pattern, inspect the current PR scope for sibling instances before fixing.
- Fix the scoped bug class at once when practical; stop at touched surfaces, owner boundaries, and clear follow-up territory.
- Keep going until structured review returns no accepted/actionable findings only while the work remains inside the original task scope.
- If a review-triggered fix changes code, rerun focused tests and rerun the structured review helper.
- For security-audit suppression changes, verify accepted findings remain auditable: suppressed findings stay in structured output, active output keeps an unsuppressible suppression notice, and aggregate findings cannot hide unrelated active risk.
- Never switch or override the requested review engine/model. If the review hits model capacity, retry the same command a few times with the same engine/model.
- Be patient with large bundles. Structured review can take up to 30 minutes while the model call is active, especially with Codex tools or web search.
- Treat heartbeat lines like `review still running: ... elapsed=... pid=...` as healthy progress, not a hang. Let the helper continue while heartbeats are advancing. Pass `--stream-engine-output` when live engine text is useful; Codex and Claude filter tool/file chatter, other engines pass raw output through.
- Do not kill a review just because it has been quiet for 2-5 minutes, or because it is still running under the 30-minute window. Inspect the process only after missing multiple expected heartbeats, after 30 minutes, or after an obviously failed subprocess; prefer letting the same helper command finish.
- Tools are useful in review mode. The helper allows read-only inspection tools and web search by default so reviewers can check dependency contracts, upstream docs, and current behavior.
- Security perspective is always included, but it should not cripple legitimate functionality. Report security findings only when the change creates a concrete, actionable risk or removes an important safety check.
- For regression provenance, keep roles separate: blamed code author, blamed PR author, PR merger/committer, current PR author, and PR/date. If no blamed PR is traceable, use the blamed commit as the provenance: commit SHA, date, and author username. Do not guess a merger or frame missing PR metadata as a separate finding.
- If the blamed PR was merged by `clawsweeper[bot]` or another automation, identify the human trigger when practical. Check timeline/comments first; if rate-limited, use gitcrawl/cache or public PR HTML. Look for maintainer commands such as `@clawsweeper automerge`, `/landpr`, or labels/status comments that armed automerge. Report `automerge triggered by @login`; if not found, say trigger unknown.
- Do not invoke built-in `codex review`, nested reviewers, or reviewer panels from inside the review. The helper builds one bundle, calls one selected engine, validates one structured result, and stops.
- Stop as soon as the helper exits 0 with no accepted/actionable findings. Do not run an extra review just to get a nicer "clean" line, a second opinion, or clearer closeout wording.
- Treat the helper's successful exit plus absence of actionable findings as the clean review result, even if the underlying Codex CLI output is terse.
- Multi-reviewer panels are opt-in only. Use them when explicitly requested or when risk justifies the extra spend; the main agent still verifies every accepted finding before fixing.
- If rejecting a finding as intentional/not worth fixing, add a brief inline code comment only when it explains a real invariant or ownership decision that future reviewers should know.
- If `gh`/Gitcrawl reports `database disk image is malformed`, run `gitcrawl doctor --json` once to let the portable cache repair before retrying review; do not bypass the shim unless repair fails and freshness requires live GitHub.
- If Gitcrawl reports a portable manifest mismatch, source/runtime DB health error, or stale portable-store checkout, run `gitcrawl doctor --json` and inspect `source_db_health`, `runtime_db_health`, and `portable_store_status` before falling back to live GitHub.
- Do not push just to review. Push only when the user requested push/ship/PR update.
## Scope Governor
Autoreview is a closeout gate, not permission to rewrite the task.
Before the first review, freeze a scope baseline: original request or issue, target branch, intended behavior, owner boundary, changed files, and non-test LOC. For inherited or already-bloated branches, use the intended PR diff as the baseline rather than accepting all existing branch drift.
Before patching a finding, classify it:
- **In-scope blocker**: the finding is introduced by the current diff, affects the same owner boundary, and can be fixed without changing the task's contract.
- **Follow-up**: the finding is real but belongs to an adjacent bug class, sibling surface, cleanup, or broader hardening track.
- **Stop-and-escalate**: the finding requires a new protocol/config/storage/public API contract, a different owner boundary, a release-process change, or a design choice outside the original request.
Stop patching and report the scope break instead of continuing when:
- a narrow PR turns into an architecture change, protocol change, migration, or release-process change;
- the diff grows past 2x the original files or non-test LOC without explicit approval to expand scope;
- two review-triggered patch cycles have not converged; pause and reclassify every remaining finding before another edit;
- the best fix is "define the canonical contract first" rather than another local inference layer;
- fixing the accepted finding would make the PR no longer describe the same behavior, issue, or owner boundary.
After the two-cycle pause, continue only when every remaining accepted finding is still an in-scope blocker. Otherwise preserve the useful analysis, identify the smallest safe landed subset if one exists, and open or request a follow-up for the larger fix. Do not keep committing speculative fixes just to satisfy the reviewer.
Do not stack or push review-triggered fix commits while scope classification or focused proof is unresolved. Keep exploratory edits local until the cycle is proven in scope; if scope breaks, remove them from the landing lane instead of preserving them as branch history.
Critical exceptions must be explicit: active data loss, crash, broken install/upgrade, release blocker, or concrete security exposure. If the exception is not one of those, it is not critical enough to blow up scope.
## Release Branches And Release Process
On release, beta, stable, hotfix, signing, notarization, appcast, package-publish, or release-check work, use freeze discipline even when the branch name is not release-like:
- Fix only release blockers, failed release infrastructure, exact backports, install/upgrade breakage, data loss, crashes, or concrete security exposure.
- Treat non-blocking autoreview findings as follow-ups for `main`, not reasons to broaden the release branch.
- Do not introduce new product behavior, config surface, protocol shape, migration, plugin ownership, docs narrative, or process policy unless it directly unblocks the release.
- Keep proof tied to the release target: exact branch/ref, failing check or shipped-risk reason, smallest command/proof, and whether the fix must also forward-port to `main`.
- If review discovers a real but non-critical design problem during release closeout, stop with a follow-up issue/PR plan; do not use the release branch as the refactor lane.
## Skill Path (set once)
Set the skill script paths once, then use `"$AUTOREVIEW"` and `"$AUTOREVIEW_HARNESS"` in the examples below.
Choose one:
```bash
# Project-local skill in the current repo:
export AUTOREVIEW=".agents/skills/autoreview/scripts/autoreview"
export AUTOREVIEW_HARNESS=".agents/skills/autoreview/scripts/test-review-harness"
```
```bash
# Source checkout of openclaw/agent-skills:
export AUTOREVIEW="skills/autoreview/scripts/autoreview"
export AUTOREVIEW_HARNESS="skills/autoreview/scripts/test-review-harness"
```
```bash
# Global skill:
export AGENTS_HOME="${AGENTS_HOME:-$HOME/.agents}"
export AUTOREVIEW="$AGENTS_HOME/skills/autoreview/scripts/autoreview"
export AUTOREVIEW_HARNESS="$AGENTS_HOME/skills/autoreview/scripts/test-review-harness"
```
When using Claude Code, set `AGENTS_HOME="$HOME/.claude"` for global skills. Project-local skills live under `.claude/skills/` in the current repo.
## Pick Target
Dirty local work:
```bash
"$AUTOREVIEW" --mode local
```
Use this only when the patch is actually unstaged/staged/untracked in the
current checkout. `--mode uncommitted` is accepted as an alias for `--mode local`.
For committed, pushed, or PR work, point the helper at the commit
or branch diff instead; do not force dirty modes just
because the helper docs mention dirty work first. A clean local review
only proves there is no local patch.
Branch/PR work:
```bash
"$AUTOREVIEW" --mode branch --base origin/main
```
Optional review context is first-class. Prompt files and datasets must be repo-relative so review bundles cannot pull arbitrary host files:
```bash
"$AUTOREVIEW" --mode branch --base origin/main --prompt-file review-notes.md --dataset evidence.json
```
If an open PR exists, use its actual base:
```bash
base=$(gh pr view --json baseRefName --jq .baseRefName)
"$AUTOREVIEW" --mode branch --base "origin/$base"
```
Committed single change:
```bash
"$AUTOREVIEW" --mode commit --commit HEAD
```
Use commit review for already-landed or already-pushed work on `main`. Reviewing
clean `main` against `origin/main` is usually an empty diff after push. For a
small stack, review each commit explicitly or review the branch before merging
with `--base`.
## Parallel Closeout
Format first if formatting can change line locations. Then it is OK to run tests and review in parallel:
```bash
"$AUTOREVIEW" --parallel-tests "<focused test command>"
```
On Windows, the default `--parallel-tests` shell preserves the platform `cmd.exe`
semantics used by Python `shell=True`. Use `--parallel-tests-shell powershell`
or `--parallel-tests-shell pwsh` when the focused test command is PowerShell-specific.
Tradeoff: tests may force code changes that stale the review. If tests or review lead to code edits, rerun the affected tests and rerun review until no accepted/actionable findings remain. Once that rerun exits cleanly, stop; do not spend another long review cycle on redundant confirmation.
## Review Panels
Run multiple reviewers against one frozen bundle:
```bash
"$AUTOREVIEW" --reviewers codex,claude,pi,droid
```
`--panel` is shorthand for Codex plus Claude unless `--engine` changes the first reviewer:
```bash
"$AUTOREVIEW" --panel
```
Set reviewer models and thinking/effort explicitly:
```bash
"$AUTOREVIEW" --reviewers codex,claude --model codex=gpt-5.5 --thinking codex=high --model claude=claude-fable-5 --thinking claude=max
```
Inline syntax is also supported for simple model IDs:
```bash
"$AUTOREVIEW" --reviewers codex:gpt-5.5:high,claude:claude-fable-5:max
```
For models with slashes or extra colons, prefer keyed form:
```bash
"$AUTOREVIEW" --engine pi --model anthropic/claude-sonnet-4 --thinking high
"$AUTOREVIEW" --engine opencode --model opencode/north-mini-code-free --thinking high
"$AUTOREVIEW" --engine droid --model claude-opus-4-8 --thinking low
"$AUTOREVIEW" --reviewers codex,pi --model codex=gpt-5.5 --model pi=anthropic/claude-sonnet-4
"$AUTOREVIEW" --reviewers codex,opencode --model codex=gpt-5.5 --model opencode=opencode/north-mini-code-free
"$AUTOREVIEW" --reviewers codex,droid --model codex=gpt-5.5 --model droid=claude-opus-4-8
```
## Models and thinking
The helper accepts `--model` globally or per engine (`engine=model`) and `--thinking` globally or per engine (`engine=level`). Repeat either flag for multiple reviewers.
Recommended model defaults:
| Engine | Default model | Source note |
| ------------------- | ---------------- | ----------------------------------------------------- |
| **codex** (default) | `gpt-5.5` | OpenAI's current GPT-5.5 alias |
| **claude** | `claude-fable-5` | Anthropic's most capable widely released Claude model |
CLI flags and environment variables override these defaults. Droid, Copilot, Pi, and OpenCode do not get built-in model defaults here because their provider catalogs are external to the Codex/Claude closeout path and may vary by installation.
| Engine | Model flag | Example model IDs | Thinking flag | Accepted levels |
| ------------------- | -------------------------- | ---------------------------------------------------------------------------- | ----------------------------- | --------------------------------------------------- |
| **codex** (default) | `codex --model X exec ...` | `gpt-5.5`, `gpt-5.5-2026-04-23` | `-c model_reasoning_effort=Y` | `none`, `minimal`, `low`, `medium`, `high`, `xhigh` |
| **claude** | `claude --model X` | `claude-fable-5`, `claude-opus-4-8`, `claude-sonnet-4-6`, `claude-haiku-4-5` | `--effort Y` | `low`, `medium`, `high`, `xhigh`, `max` |
| **droid** | `droid exec --model X` | `claude-opus-4-8`, Factory model IDs | `-r, --reasoning-effort Y` | `off`, `none`, `low`, `medium`, `high` |
| **copilot** | `copilot --model X` | `gpt-5.2`, Copilot model aliases | not supported | n/a |
| **pi** | `pi --model X` | `anthropic/claude-sonnet-4`, `openai/gpt-4o` | `--thinking Y` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh` |
| **opencode** | `opencode run -m X` | `opencode/north-mini-code-free`, OpenCode provider/model IDs | `--variant Y` | `minimal`, `low`, `medium`, `high`, `max` |
Claude also supports `--fallback-model a,b` for availability-based fallback chains ([model-config](https://code.claude.com/docs/en/model-config)). Current Claude docs note that auth, billing, rate-limit, request-size, and transport errors do not trigger fallback, and the changelog documents interactive-session support in `v2.1.166`.
Examples matching current `main` behavior:
```bash
# Codex with explicit model and reasoning
"$AUTOREVIEW" --engine codex --model gpt-5.5 --thinking high
# Claude Code aliases or full model names, with optional availability fallback
"$AUTOREVIEW" --engine claude --model claude-fable-5 --thinking max
"$AUTOREVIEW" --engine claude --model claude-fable-5 --fallback-model claude-opus-4-8,claude-sonnet-4-6
# Factory Droid with explicit model and reasoning effort
"$AUTOREVIEW" --engine droid --model claude-opus-4-8 --thinking low
# GitHub Copilot (model only; no thinking knob)
"$AUTOREVIEW" --engine copilot --model gpt-5.2
# Pi with explicit model and thinking level
"$AUTOREVIEW" --engine pi --model anthropic/claude-sonnet-4 --thinking high --pi-bin pi
# OpenCode with explicit provider/model and variant
"$AUTOREVIEW" --engine opencode --model opencode/north-mini-code-free --thinking high
```
### Environment defaults
CLI flags take precedence over environment variables.
| Variable | Purpose |
| ---------------------------------- | ----------------------------------------------------------------------- |
| `AUTOREVIEW_MODEL` | Override the built-in default `--model` for all engines |
| `AUTOREVIEW_THINKING` | Default `--thinking` for all engines |
| `AUTOREVIEW_FALLBACK_MODEL` | Default Claude `--fallback-model` chain |
| `AUTOREVIEW_<ENGINE>_MODEL` | Per-engine model override, for example `AUTOREVIEW_CODEX_MODEL=gpt-5.5` |
| `AUTOREVIEW_<ENGINE>_THINKING` | Per-engine thinking override |
| `AUTOREVIEW_CLAUDE_FALLBACK_MODEL` | Claude-only fallback chain |
Codex maps thinking to `model_reasoning_effort`. Claude maps thinking to `--effort`. Droid maps thinking to `-r, --reasoning-effort`. Pi maps thinking to `--thinking`. OpenCode maps thinking to `--variant`. Copilot rejects `--thinking`. Only Claude accepts `--fallback-model`; global CLI/env fallback requires at least one Claude reviewer, and engine-specific fallback overrides require that reviewer to be selected. Non-Claude fallback overrides, including `AUTOREVIEW_<NONCLAUDE>_FALLBACK_MODEL`, fail closed instead of being silently ignored.
## Review engine isolation
When autoreview runs inside the repository under review, external reviewer CLIs must not load project-local trust or configuration that the branch controls.
| Engine | Isolation flags | Reference |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| **codex** | Auth-only config overrides, `-c project_doc_max_bytes=0`, repo `trust_level="untrusted"`, `exec --ignore-user-config --ignore-rules`, plus read-only sandbox | Codex CLI `exec --help` |
| **claude** | `--safe-mode --setting-sources user --strict-mcp-config --disallowedTools mcp__*` plus explicit `--allowedTools` (`--safe-mode` requires Claude Code `v2.1.169+`) | Claude Code [CLI reference](https://code.claude.com/docs/en/cli-reference) |
| **pi** | `--no-approve --no-session --no-context-files --no-extensions --no-skills --no-prompt-templates --no-themes`, plus read-only tool allowlist | Pi CLI `--help`; requires Pi `v0.79.0+` |
| **opencode** | `opencode run --dir <repo> --pure --format json`, prompt over stdin, neutral subprocess cwd, injected deny-by-default permissions, project config disabled | OpenCode CLI `--help` |
Codex `--ignore-user-config` skips config loading for the exec run. Autoreview reconstructs only the documented `cli_auth_credentials_store`, `forced_login_method`, and `forced_chatgpt_workspace_id` settings from `CODEX_HOME/config.toml`, keeping authentication and workspace restrictions usable without forwarding unrelated user configuration. The explicit repo trust override and zero project-doc budget keep reviewed-repo `AGENTS.md` and `.codex/` trust surfaces out of the review prompt. `--ignore-rules` skips user/project execpolicy rules. Claude `--safe-mode` disables project hooks, skills, plugins, MCP servers, and CLAUDE.md while preserving normal authentication, model selection, built-in tools, and permissions; managed settings policy can still apply. `--setting-sources user` avoids project/local settings from the reviewed checkout, and current Claude Code docs note the project-skill blocking behavior was fixed in `v2.1.69`. `--strict-mcp-config` and `--disallowedTools mcp__*` keep MCP unavailable to the review run. `--bare` is not used here because Claude's headless docs say it skips OAuth and keychain reads. Pi `--no-approve` ignores project-local files for one run; the helper requires Pi `v0.79.0+` plus help output that advertises every required isolation flag because older legacy binaries can ignore unknown flags. The current package is `@earendil-works/pi-coding-agent`; deprecated `@mariozechner/pi-coding-agent` `0.73.x` is intentionally rejected. Pi version/help probes and the review command run from neutral temporary directories, not the reviewed repo. Pi `--no-context-files` removes `AGENTS.md`/`CLAUDE.md`, the resource-disable flags keep `.pi` extensions, skills, prompts, and themes out of the run, `--no-session` avoids writing review sessions, and the read-only allowlist omits `bash`, `edit`, and `write`. OpenCode starts from a neutral temporary directory, points at the reviewed repo with `--dir`, disables project config through `OPENCODE_DISABLE_PROJECT_CONFIG=1`, and injects `OPENCODE_CONFIG_CONTENT`; permissions default to deny, allow read/grep/glob, preserve OpenCode's `.env` ask rules, and gate `websearch`/`webfetch` with `--no-web-search`. The injected config also clears command/instruction/plugin arrays and disables write/edit/bash/task/skill/todowrite tools without changing user auth storage. The helper sends the review prompt over stdin rather than argv and extracts the final structured JSON from `type: "text"` events. OpenCode rejects `--no-tools`.
## Context Efficiency
Run the helper directly so target selection, engine choice, structured validation, and exit status all stay in one path. If output is noisy, summarize the completed helper output after it returns; do not ask another agent or reviewer to rerun the review.
## Helper
After setting `AUTOREVIEW` and `AUTOREVIEW_HARNESS` above:
```bash
"$AUTOREVIEW" --help
```
The smoke harness has thin shell wrappers over a shared Python implementation:
```bash
"$AUTOREVIEW_HARNESS" --fixture benign --engine codex
```
On native Windows, invoke the extensionless Python helper through Python:
```powershell
python skills\autoreview\scripts\autoreview --help
```
and the smoke harness:
```powershell
skills\autoreview\scripts\test-review-harness.ps1 -Fixture benign -Engine codex
```
The helper:
- chooses dirty local changes first
- accepts `--mode uncommitted` as an alias for `--mode local`
- otherwise uses current PR base if `gh pr view` works
- otherwise uses `origin/main` for non-main branches
- does not fetch automatically during branch review; the selected base ref must already resolve locally
- supports `--engine codex`, `claude`, `droid`, `copilot`, `pi`, and `opencode`; default is `AUTOREVIEW_ENGINE` or `codex`; Codex should remain the default when nothing is set
- resolves bare `git`, `gh`, reviewer, and PowerShell shell commands from absolute `PATH` entries only, never from the reviewed checkout; explicit relative `--*-bin` paths are resolved from the reviewed repository root
- use `--mode commit --commit <ref>` for already-committed work, especially clean `main` after landing
- should be left in `--mode auto` or forced to `--mode branch` for PR/branch work; do not force `--mode local` after committing
- writes only to stdout unless `--output`, `--json-output`, or live streamed engine stderr is set
- supports `--dry-run`, `--parallel-tests`, `--parallel-tests-shell`, `--prompt`, repo-relative `--prompt-file`, repo-relative `--dataset`, `--no-tools`, `--no-web-search`, and commit refs
- supports `--stream-engine-output` or `AUTOREVIEW_STREAM_ENGINE_OUTPUT=1` for live engine text while preserving structured validation; Codex and Claude hide tool/file event details, emit compact activity summaries, and report usage at turn completion
- supports opt-in review panels with `--panel` / `--reviewers`, plus per-engine `--model`, `--thinking`, and Claude `--fallback-model`
- uses built-in model defaults `codex=gpt-5.5` and `claude=claude-fable-5`; honors `AUTOREVIEW_MODEL`, `AUTOREVIEW_THINKING`, `AUTOREVIEW_FALLBACK_MODEL`, and per-engine `AUTOREVIEW_<ENGINE>_MODEL` / `AUTOREVIEW_<ENGINE>_THINKING` environment overrides when CLI flags are omitted
- allows read-only tools and web search by default where the selected CLI supports them; forbids nested review in the prompt; Codex is run through `codex exec` with auth-only user settings, read-only sandbox, reviewed-repo instruction/config/rule isolation flags, and structured output
- runs Claude with `--safe-mode` (`v2.1.169+`), `--setting-sources user`, MCP disabled, explicit allowed tools, and `--fallback-model` when set, so reviewed-repo hooks/skills/MCP do not affect the review run while normal auth still works; managed settings policy can still apply
- runs Droid with `droid exec` in read-only mode, forwards `--model` and `-r, --reasoning-effort`, and switches `--output-format` to `stream-json` when streaming is enabled
- runs Pi `v0.79.0+` from neutral temporary directories with `--no-approve`, `--no-session`, disabled Pi context/resource loading, and built-in read-only tools (`read,grep,find,ls`) when tools are enabled
- runs OpenCode with `opencode run --dir <repo> --pure --format json` from a neutral temporary directory, forwards `--model` and `--variant`, injects deny-by-default permissions, disables project config loading, and passes the review prompt over stdin
- prints `review still running: <engine> elapsed=<seconds>s pid=<pid>` to stderr at long-running intervals while waiting for the selected review engine, unless streamed output or compact Codex activity has been visible recently
- prints `autoreview clean: no accepted/actionable findings reported` when the selected review command exits 0
- exits nonzero when accepted/actionable findings are present
## Final Report
Include:
- review command used
- tests/proof run
- findings accepted/rejected, briefly why
- the clean review result from the final helper/review run, or why a remaining finding was consciously rejected
Do not run another review solely to improve the final report wording. If the final helper run exited 0 and produced no accepted/actionable findings, report that exact run as clean.
File diff suppressed because it is too large Load Diff
+16
View File
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
harness="$script_dir/test-review-harness.py"
if command -v python3 >/dev/null 2>&1; then
exec python3 "$harness" "$@"
fi
if command -v python >/dev/null 2>&1; then
exec python "$harness" "$@"
fi
echo "Python 3 is required to run test-review-harness." >&2
exit 127
@@ -0,0 +1,45 @@
[CmdletBinding()]
param(
[ValidateSet('malicious', 'benign')]
[string] $Fixture,
[ValidateSet('codex', 'claude', 'droid', 'copilot', 'pi', 'opencode')]
[string[]] $Engine,
[Alias('h')]
[switch] $Help
)
$ErrorActionPreference = 'Stop'
$Harness = Join-Path $PSScriptRoot 'test-review-harness.py'
$ForwardedArgs = @()
if ($Help) {
$ForwardedArgs += '--help'
}
if ($PSBoundParameters.ContainsKey('Fixture')) {
$ForwardedArgs += @('--fixture', $Fixture)
}
if ($PSBoundParameters.ContainsKey('Engine')) {
foreach ($SelectedEngine in $Engine) {
$ForwardedArgs += @('--engine', $SelectedEngine)
}
}
$PyLauncher = Get-Command py -ErrorAction SilentlyContinue
if ($null -ne $PyLauncher) {
& $PyLauncher.Source -3 $Harness @ForwardedArgs
exit $LASTEXITCODE
}
$Python = Get-Command python -ErrorAction SilentlyContinue
if ($null -ne $Python) {
& $Python.Source $Harness @ForwardedArgs
exit $LASTEXITCODE
}
Write-Error 'Python 3 is required to run test-review-harness.'
exit 127
@@ -0,0 +1,215 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import os
import runpy
import shutil
import stat
import subprocess
import sys
import tempfile
from collections.abc import Callable
from pathlib import Path
ENGINES = ("codex", "claude", "droid", "copilot", "pi", "opencode")
DEFAULT_ENGINES = ("codex", "claude")
MALICIOUS_INITIAL = """export function uploadPath(name) {
return `uploads/${name.replaceAll("/", "")}`;
}
export function publicUser(user) {
return { id: user.id, name: user.name };
}
"""
BENIGN_INITIAL = r"""import path from "node:path";
const uploadsRoot = path.resolve("uploads");
function safeChildPath(root, name) {
if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
throw new Error("invalid child name");
}
const resolved = path.resolve(root, name);
if (!resolved.startsWith(root + path.sep)) throw new Error("path escaped root");
return resolved;
}
export function uploadPath(name) {
return safeChildPath(uploadsRoot, name);
}
export function publicUser(user) {
return { id: user.id, name: user.name };
}
"""
MALICIOUS_CHANGED = """import { execSync } from "node:child_process";
export function uploadPath(name) {
return `uploads/${name}`;
}
export function deleteUpload(name) {
return execSync(`rm -rf uploads/${name}`);
}
export function publicUser(user) {
return { id: user.id, name: user.name, password: user.password };
}
"""
BENIGN_CHANGED = r"""import { execFile } from "node:child_process";
import path from "node:path";
import { promisify } from "node:util";
const uploadsRoot = path.resolve("uploads");
const reposRoot = path.resolve("repos");
const execFileAsync = promisify(execFile);
function safeChildPath(root, name) {
if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
throw new Error("invalid child name");
}
const resolved = path.resolve(root, name);
if (!resolved.startsWith(root + path.sep)) throw new Error("path escaped root");
return resolved;
}
function repoChildPath(root, name) {
if (!/^[A-Za-z0-9._-]+$/.test(name)) throw new Error("invalid repo name");
return safeChildPath(root, name);
}
export function uploadPath(name) {
return safeChildPath(uploadsRoot, name);
}
export async function repoProbe(repoName) {
const { stdout } = await execFileAsync(process.execPath, ["--version"], {
cwd: repoChildPath(reposRoot, repoName),
encoding: "utf8",
maxBuffer: 16 * 1024 * 1024,
});
return stdout;
}
export function publicUser(user) {
return { id: user.id, name: user.name };
}
export function accountSettingsForOwner(user, requesterId) {
if (String(requesterId) !== String(user.id)) throw new Error("forbidden");
return { id: user.id, hasPassword: Boolean(user.passwordHash) };
}
"""
MALICIOUS_PROMPT = "This is an acceptance test fixture. The changed app.js patch contains real security bugs. Review normally and report only actionable defects from the patch."
BENIGN_PROMPT = "Security calibration fixture: this patch intentionally uses filesystem paths, async execFile, and owner-gated password-adjacent state safely. Do not flag legitimate shell/filesystem/auth-adjacent functionality unless there is a concrete exploitable risk in the diff."
def parse_args(argv: list[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser(
prog="test-review-harness",
description=(
"Creates a temporary git repo with either a deliberately unsafe patch "
"or a security-sensitive-but-safe patch, then verifies each selected "
"engine through autoreview."
),
epilog="Default engines: codex, claude.",
)
parser.add_argument("--fixture", choices=("malicious", "benign"), default="malicious")
parser.add_argument("--engine", action="append", choices=ENGINES, dest="engines")
return parser.parse_args(argv)
def write_fixture_file(repo: Path, content: str) -> None:
with (repo / "app.js").open("w", encoding="utf-8", newline="\n") as handle:
handle.write(content)
def run(command: list[str], cwd: Path) -> None:
subprocess.run(command, cwd=cwd, check=True)
def create_fixture_repo(repo: Path, fixture: str) -> None:
run(["git", "init", "--quiet"], repo)
run(["git", "config", "user.name", "Review Fixture"], repo)
run(["git", "config", "user.email", "review-fixture@example.com"], repo)
write_fixture_file(repo, MALICIOUS_INITIAL if fixture == "malicious" else BENIGN_INITIAL)
run(["git", "add", "app.js"], repo)
run(["git", "commit", "--quiet", "-m", "initial safe version"], repo)
write_fixture_file(repo, MALICIOUS_CHANGED if fixture == "malicious" else BENIGN_CHANGED)
def validate_prompt_policy(repo: Path, autoreview: Path) -> None:
namespace = runpy.run_path(str(autoreview))
prompt = namespace["build_prompt"](repo, "local", None, "fixture diff", "", "")
required = (
"This helper is a closeout gate.",
"Do not turn a narrow patch into a broad",
"If this is release-branch or release-process work",
"Non-blocking design,",
)
missing = [needle for needle in required if needle not in prompt]
if missing:
raise RuntimeError(f"autoreview prompt missing scope policy: {missing}")
def run_reviews(repo: Path, script_dir: Path, fixture: str, engines: list[str]) -> None:
autoreview = script_dir / "autoreview"
validate_prompt_policy(repo, autoreview)
for engine in engines:
print(f"== {engine} ==", flush=True)
command = [
sys.executable,
str(autoreview),
"--mode",
"local",
"--engine",
engine,
"--prompt",
MALICIOUS_PROMPT if fixture == "malicious" else BENIGN_PROMPT,
]
if fixture == "malicious":
command.extend(["--require-finding", "command", "--expect-findings"])
run(command, repo)
def cleanup_repo(repo: Path) -> None:
def make_writable_and_retry(function: Callable[[str], object], path: str, _exc_info: object) -> None:
try:
os.chmod(path, stat.S_IREAD | stat.S_IWRITE)
function(path)
except OSError as exc:
print(f"warning: unable to remove temp path {path}: {exc}", file=sys.stderr)
if not repo.exists():
return
try:
shutil.rmtree(repo, onerror=make_writable_and_retry)
except OSError as exc:
print(f"warning: unable to remove temp repo {repo}: {exc}", file=sys.stderr)
def main(argv: list[str]) -> int:
args = parse_args(argv)
script_dir = Path(__file__).resolve().parent
engines = args.engines or list(DEFAULT_ENGINES)
repo = Path(tempfile.mkdtemp(prefix="autoreview-fixture."))
try:
create_fixture_repo(repo, args.fixture)
run_reviews(repo, script_dir, args.fixture, engines)
except subprocess.CalledProcessError as exc:
return int(exc.returncode or 1)
finally:
cleanup_repo(repo)
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))
@@ -0,0 +1,209 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import os
import runpy
import subprocess
import tempfile
import unittest
from pathlib import Path
SCRIPT = Path(__file__).resolve().parents[1] / "scripts" / "autoreview"
def load_helper() -> dict[str, object]:
return runpy.run_path(str(SCRIPT), run_name="autoreview_under_test")
def git(repo: Path, *args: str) -> str:
env = os.environ.copy()
env.update(
{
"GIT_AUTHOR_NAME": "Autoreview Test",
"GIT_AUTHOR_EMAIL": "autoreview@example.invalid",
"GIT_COMMITTER_NAME": "Autoreview Test",
"GIT_COMMITTER_EMAIL": "autoreview@example.invalid",
}
)
result = subprocess.run(
["git", *args],
cwd=repo,
env=env,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
return result.stdout
def init_repo(tempdir: Path) -> Path:
repo = tempdir / "repo"
repo.mkdir()
git(repo, "init", "-q")
git(repo, "config", "user.name", "Autoreview Test")
git(repo, "config", "user.email", "autoreview@example.invalid")
return repo
class AutoreviewHardeningTests(unittest.TestCase):
def setUp(self) -> None:
self.helper = load_helper()
def test_local_bundle_blocks_sensitive_untracked_file(self) -> None:
with tempfile.TemporaryDirectory() as tempdir:
repo = init_repo(Path(tempdir))
(repo / ".env").write_text("placeholder=true\n", encoding="utf-8")
with self.assertRaisesRegex(SystemExit, "untracked sensitive files"):
self.helper["local_bundle"](repo)
def test_local_bundle_omits_safe_untracked_binary_content(self) -> None:
with tempfile.TemporaryDirectory() as tempdir:
repo = init_repo(Path(tempdir))
(repo / "image.bin").write_bytes(b"\x89PNG\r\n\0binary-content")
bundle = self.helper["local_bundle"](repo)
self.assertIn("## image.bin\n[binary file omitted]", bundle)
def test_branch_bundle_rejects_unsafe_or_unknown_base_before_diff(self) -> None:
with tempfile.TemporaryDirectory() as tempdir:
repo = init_repo(Path(tempdir))
(repo / "tracked.txt").write_text("base\n", encoding="utf-8")
git(repo, "add", "tracked.txt")
git(repo, "commit", "-q", "-m", "base")
with self.assertRaisesRegex(SystemExit, "unsafe base ref"):
self.helper["branch_bundle"](repo, "--help")
with self.assertRaisesRegex(SystemExit, "unknown base ref"):
self.helper["branch_bundle"](repo, "origin/main")
def test_git_path_list_preserves_newline_filenames(self) -> None:
with tempfile.TemporaryDirectory() as tempdir:
repo = init_repo(Path(tempdir))
rel = "line\nbreak.txt"
(repo / rel).write_text("content\n", encoding="utf-8")
git(repo, "add", rel)
paths = self.helper["git_path_list"](repo, "ls-files", "-z")
self.assertIn(rel, paths)
def test_bounded_truncates_large_bundle_component(self) -> None:
bounded = self.helper["bounded"]("x" * 25, 10)
self.assertEqual(bounded, "x" * 10 + "\n\n[truncated at 10 characters]\n")
def test_read_text_truncates_without_scanning_tail(self) -> None:
with tempfile.TemporaryDirectory() as tempdir:
path = Path(tempdir) / "large.txt"
path.write_bytes(b"x" * 200_000 + b"\0tail")
text = self.helper["read_text"](path)
self.assertIn("[truncated at 180000 characters]", text)
self.assertNotEqual(text, "[binary file omitted]")
def test_evidence_file_must_be_repo_relative_and_not_symlinked(self) -> None:
with tempfile.TemporaryDirectory() as tempdir:
root = Path(tempdir)
repo = init_repo(root)
outside = root / "outside.md"
outside.write_text("outside\n", encoding="utf-8")
with self.assertRaisesRegex(SystemExit, "repo-relative"):
self.helper["validate_evidence_file"](repo, str(outside), "--prompt-file")
target = repo / "notes.md"
target.write_text("notes\n", encoding="utf-8")
link = repo / "link.md"
link.symlink_to(target)
with self.assertRaisesRegex(SystemExit, "symlinked"):
self.helper["validate_evidence_file"](repo, "link.md", "--dataset")
def test_safe_engine_env_strips_process_injection_variables(self) -> None:
old = os.environ.copy()
with tempfile.TemporaryDirectory() as tempdir:
repo = init_repo(Path(tempdir))
try:
os.environ["GIT_DIR"] = "/tmp/unsafe-git-dir"
os.environ["GIT_CONFIG_COUNT"] = "99"
os.environ["DYLD_INSERT_LIBRARIES"] = "/tmp/unsafe.dylib"
os.environ["NODE_OPTIONS"] = "--require=/tmp/unsafe.js"
env = self.helper["safe_engine_env"](repo)
self.assertNotEqual(env.get("GIT_DIR"), "/tmp/unsafe-git-dir")
self.assertEqual(
env["GIT_CONFIG_COUNT"],
str(len(self.helper["ENGINE_GIT_CONFIG_OVERRIDES"])),
)
self.assertNotIn("DYLD_INSERT_LIBRARIES", env)
self.assertNotIn("NODE_OPTIONS", env)
finally:
os.environ.clear()
os.environ.update(old)
def test_safe_engine_env_excludes_repo_local_path_entries(self) -> None:
old_path = os.environ.get("PATH", "")
with tempfile.TemporaryDirectory() as tempdir:
repo = init_repo(Path(tempdir))
os.environ["PATH"] = f"{repo}{os.pathsep}{old_path}"
try:
env = self.helper["safe_engine_env"](repo)
finally:
os.environ["PATH"] = old_path
self.assertNotIn(str(repo.resolve()), env["PATH"].split(os.pathsep))
def test_large_repo_relative_evidence_file_is_truncated(self) -> None:
with tempfile.TemporaryDirectory() as tempdir:
repo = init_repo(Path(tempdir))
evidence = repo / "evidence.txt"
evidence.write_text("x" * 600_000, encoding="utf-8")
_, content = self.helper["validate_evidence_file"](repo, "evidence.txt", "--dataset")
self.assertIn("[truncated at 180000 characters]", content)
def test_copilot_allows_web_fetch_only_when_web_search_is_enabled(self) -> None:
captured: list[list[str]] = []
def fake_run_with_heartbeat(
cmd: list[str],
cwd: Path,
**kwargs: object,
) -> subprocess.CompletedProcess[str]:
captured.append(cmd)
return subprocess.CompletedProcess(cmd, 0, '{"findings":[]}', "")
self.helper["run_copilot"].__globals__["run_with_heartbeat"] = fake_run_with_heartbeat
self.helper["run_copilot"].__globals__["resolve_command"] = (
lambda command, repo: f"/resolved/{command}"
)
args = argparse.Namespace(
copilot_bin="copilot",
thinking=None,
tools=True,
model=None,
web_search=False,
stream_engine_output=False,
)
self.helper["run_copilot"](args, Path("/repo"), "prompt")
self.assertNotIn("--allow-tool=web_fetch", captured[-1])
self.assertFalse(any(arg == "--allow-all-urls" for arg in captured[-1]))
args.web_search = True
self.helper["run_copilot"](args, Path("/repo"), "prompt")
self.assertIn("--allow-tool=web_fetch", captured[-1])
self.assertIn("--allow-all-urls", captured[-1])
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,129 @@
---
name: clawhub-content-rights-correspondence
description: Use when drafting, sending, or preserving email correspondence for an existing ClawHub content rights case.
---
# ClawHub Content Rights Correspondence
Use ClawHub's authenticated admin CLI commands directly. Do not use helper
scripts, direct Hermit calls, or direct R2 access for correspondence.
## Safety Rules
- Require an existing `CHR-...` case. Never create cases with this skill.
- Dry-run first and show the final recipient, subject, and body.
- Send only after explicit user signoff on that final draft.
- Use `bun run admin -- email send` for outbound email.
- Use `bun run admin -- content-rights record-correspondence` to preserve the
exact correspondence in Hermit.
- Do not retry after an email was sent if evidence recording fails; report the
failure so staff can repair the audit record without sending a duplicate.
- `--attachment` files are archived with the correspondence. The generic email
template does not send file attachments.
- The generic email template already adds the greeting. Do not add `Hello ...`
or `Hi ...` to the body file.
- The generic email template may render the subject as a visible heading. Do
not pass `--title`, and do not duplicate the title in the body file.
- Do not use the generic email action button for ClawHub content-rights
responses. Put the response form URL as plaintext in the body.
## Publisher Removal Notice
Use this subject:
```text
ClawHub skill removal notice
```
Use this body, replacing only the skill URL:
```text
We removed the following ClawHub skill after receiving a content rights request involving Rednote/Xiaohongshu platform rights:
https://clawhub.ai/<owner>/<slug>
If you believe this removal was made in error, please submit a response using this form:
https://forms.openclaw.ai/clawhub-content-rights
```
Preview the email:
```bash
bun run admin -- email send \
--user <publisher-handle> \
--subject "ClawHub skill removal notice" \
--body-file /tmp/body.txt
```
Send only after explicit signoff:
```bash
bun run admin -- email send \
--user <publisher-handle> \
--subject "ClawHub skill removal notice" \
--body-file /tmp/body.txt \
--send \
--confirm-user-request \
--confirm-user-signoff \
--json
```
Record the exact sent correspondence:
```bash
bun run admin -- content-rights record-correspondence CHR-000007 \
--direction outbound \
--to "<publisher-handle-or-email>" \
--from "ClawHub <noreply@notifications.openclaw.ai>" \
--subject "ClawHub skill removal notice" \
--body-file /tmp/body.txt \
--provider-message-id "<providerId-from-send-response>" \
--json
```
## Requester Status Updates
For requester updates or closure notes, use direct email and avoid exposing the
internal case id in the subject unless the user explicitly asks.
```bash
bun run admin -- email send \
--to requester@example.com \
--username Requester \
--subject "Update on ClawHub content rights request" \
--body-file /tmp/body.txt
```
After explicit signoff, send:
```bash
bun run admin -- email send \
--to requester@example.com \
--username Requester \
--subject "Update on ClawHub content rights request" \
--body-file /tmp/body.txt \
--send \
--confirm-user-request \
--confirm-user-signoff \
--json
```
Then record the successful send with the provider id:
```bash
bun run admin -- content-rights record-correspondence CHR-000007 \
--direction outbound \
--to "Requester Name <requester@example.com>" \
--from "ClawHub <noreply@notifications.openclaw.ai>" \
--subject "Update on ClawHub content rights request" \
--body-file /tmp/body.txt \
--provider-message-id "<providerId-from-send-response>"
```
Verify the case now includes the correspondence:
```bash
bun run admin -- content-rights get CHR-000007 --json
```
Run from the ClawHub repository root with the normal authenticated admin CLI.
@@ -0,0 +1,4 @@
interface:
display_name: "ClawHub Rights Correspondence"
short_description: "Send and preserve ClawHub rights case emails."
default_prompt: "Use $clawhub-content-rights-correspondence to draft or send correspondence for an existing ClawHub content rights case."
+213
View File
@@ -0,0 +1,213 @@
---
name: clawhub-moderation
description: "Use for ClawHub staff moderation actions with the repo-local ClawHub admin tool: skills, users, org publishers, plugin packages, trusted publishers, official publishers, and guarded staff email."
---
# ClawHub Moderation
Use the repo-local admin tool from a checked-out ClawHub repo. It wraps
the existing ClawHub CLI auth/config and HTTP API surfaces. Do not call Convex
internal mutations directly for staff actions.
## Safety Rules
- Require an explicit target from the user: skill slug, user handle, or user id.
- Require a reason for destructive, restorative, ownership, or moderation writes.
- Before any write, show the exact command and ask for confirmation unless the
user already said to proceed or supplied `--yes`.
- For `email send`, the user must explicitly ask for the email and sign off on
the final recipient, subject, and body. Dry-run is fine for drafting. Never
send until both are true, and only use `--send --confirm-user-request
--confirm-user-signoff` after that approval.
- Prefer handles for humans. Use `--id` only when the user provides a user id.
- Never bypass API-token auth, server role checks, or audit logging.
- After the write, verify state with the CLI/API and report the result.
## Command Map
Run from the ClawHub repo root:
```sh
bun run admin -- --help
```
Authenticate or validate the current token:
```sh
bun run admin -- login
bun run admin -- whoami
```
Current top-level command groups:
```text
auth
users
plugins|plugin
packages|package
org
email
skills|skill
```
### Skills
`bun run admin -- skills --help` exposes:
```text
unhide <slug>
rescan <slug>
reports
triage-report <report-id>
```
Examples:
```sh
bun run admin -- skills unhide <slug> --reason "<reason>" --yes
bun run admin -- skills rescan <slug> --reason "<reason>" --yes
bun run admin -- skills reports --status open
bun run admin -- skills triage-report <report-id> --status confirmed --action hide --note "<note>" --yes
```
### Users
`bun run admin -- users --help` exposes:
```text
ban <handleOrId>
unban <handleOrId>
set-role <handleOrId> <role>
reclassify-ban <handleOrId>
remediate-autobans
```
Examples:
```sh
bun run admin -- users ban <handleOrId> --reason "<reason>" --yes
bun run admin -- users unban <handleOrId> --reason "<reason>" --yes
bun run admin -- users set-role <handleOrId> <user|moderator|admin> --yes
bun run admin -- users reclassify-ban <handleOrId> --reason "<reason>" --apply --yes
bun run admin -- users remediate-autobans --apply --reason "<reason>"
```
Use `--id` when `<handleOrId>` is a user id. Use `--fuzzy` only when the user
has asked for fuzzy handle resolution or the exact handle is ambiguous.
### Org Publishers
`bun run admin -- org --help` exposes:
```text
official
create <handle>
remove-member <handle> <member>
delete <handle>
repair-scoped-packages <csv>
```
Examples:
```sh
bun run admin -- org official list
bun run admin -- org official add <handle> --reason "<reason>" --yes
bun run admin -- org official remove <handle> --reason "<reason>" --yes
bun run admin -- org create <handle> --display-name "<name>" --member <user-handle> --role owner
bun run admin -- org remove-member <handle> <member-handle>
bun run admin -- org delete <handle> --reason "<reason>" # dry-run
bun run admin -- org delete <handle> --reason "<reason>" --apply
bun run admin -- org repair-scoped-packages <csv> # dry-run
bun run admin -- org repair-scoped-packages <csv> --apply
```
`org create` requires `--member`; it must not add the moderator running the
command as an implicit owner. `org delete` only works for empty org publishers
and defaults to dry-run.
### Plugin Packages
`bun run admin -- packages --help` exposes:
```text
moderate <name>
status|moderation-status <name>
queue|moderation-queue
reports
triage-report <report-id>
transfer <name>
repair-name <name>
migrations
set-migration <bundled-plugin-id>
trusted-publisher
```
Examples:
```sh
bun run admin -- packages status <name>
bun run admin -- packages transfer <name> --to <owner> --reason "<reason>" # dry-run
bun run admin -- packages transfer <name> --to <owner> --reason "<reason>" --apply
bun run admin -- packages repair-name <name> --next-name <name> --reason "<reason>"
bun run admin -- packages trusted-publisher get <name>
bun run admin -- packages trusted-publisher set <name> --repository <owner/repo> --workflow-filename <file>
```
### Staff Email
`bun run admin -- email send --help` exposes:
```text
--to <email>
--user <handle>
--subject <subject>
--body-file <path>
--body <text>
--send
--confirm-user-request
--confirm-user-signoff
--json
```
Draft only:
```sh
bun run admin -- email send --user <handle> --subject "<subject>" --body-file <path>
```
Send only after explicit request and sign-off:
```sh
bun run admin -- email send --user <handle> --subject "<subject>" --body-file <path> --send --confirm-user-request --confirm-user-signoff
```
The server sends through the production noreply provider and writes an audit log
only after admin auth succeeds.
## Verification
- For skills, inspect the page/API status after `skills unhide`.
- For users, prefer user search/admin surfaces for target accounts where
available.
- For orgs and packages, use the public publisher/plugin pages and the relevant
CLI status command after a write.
- For email, verify the CLI response and audit expectation; do not send a second
email just to test delivery.
- If verification is blocked by auth or missing admin access, report the command
result and the verification blocker plainly.
## Impact Notes
- `skills unhide` is a moderator manual restore. It clears skill hidden state,
applies a clean manual override to top-level moderation fields, preserves
version-level scanner records, updates public stats, and writes audit logs.
- There is no standalone `skills hide` command in `clawhub-admin`; use report
triage with `--action hide` when resolving a report that should hide a skill.
- `users ban` is disruptive: it revokes API tokens, marks the user deleted,
hides owned skills, soft-deletes comments, and writes audit logs.
- `users unban` is admin-only. It clears ban state and restores skills that were
hidden by the matching ban flow; revoked API tokens stay revoked.
- `packages transfer` preserves the package row, stats, releases, and history;
it changes the owner publisher.
- `org delete` soft-deletes an empty org publisher and retains member rows for
history; it refuses orgs with active skills or packages.
@@ -0,0 +1,187 @@
---
name: clawhub-pr-maintainer
description: Use when reviewing, triaging, validating, or discussing ClawHub GitHub issues or pull requests, including author context, CI, UI proof, evidence, labels, close decisions, and maintainer handoff.
---
# ClawHub PR Maintainer
Use this skill for maintainer-facing ClawHub GitHub workflow, not for ordinary
implementation work.
## Start With Live GitHub State
- Use `gh pr view` or `gh issue view` against `openclaw/clawhub`; verify live
state before commenting, labeling, closing, or recommending merge.
- For PRs, read title, body, author, labels, comments, files, commits, status
checks, review state, and linked issues.
- Surface author identity briefly: GitHub name/login and account age when
useful. Treat identity as triage signal, never as proof by itself.
Common read-only commands:
```sh
gh pr view <number> --repo openclaw/clawhub --json title,body,author,labels,comments,files,commits,statusCheckRollup,reviewDecision,url,additions,deletions,changedFiles
gh issue view <number> --repo openclaw/clawhub --json title,body,author,labels,comments,state,url
gh api users/<login> --jq '{login,name,created_at,type}'
```
## Review Evidence Bar
- For bug fixes, require symptom evidence, a plausible root cause in the touched
code path, and either a regression test or focused manual proof.
- For UI changes, require screenshots or video when the behavior is meaningfully
visual. Use tests as supplemental evidence, not a substitute for visible proof.
- Do not merge or recommend merge based only on PR prose, AI rationale, or green
CI when the changed behavior has not been exercised.
- For contributor-provided screenshots/videos/logs, inspect the artifact
directly and state what it proves. Do not rerun `proof:ui` just to inspect
existing evidence.
## Structure PR Review Output
- Start every PR review with 1-3 plain sentences explaining what the change does
and why it matters.
- Show size near the top as `LOC: +x/-y (N files)`, using live PR stats or
local diff stats.
- Then list findings first. If none, say `No blocking findings` or
`No findings`.
- Always answer: affected ClawHub surface, bug or behavior being changed,
evidence checked, and best-fix verdict.
- For bug/regression fixes, include a compact `Provenance:` line when a bounded
history pass identifies it. Separate code author, PR author,
merger/committer, current PR author, PR number, and date when those differ.
If the blamed PR was merged by automation, identify the human trigger when
practical; otherwise say trigger unknown.
## Read Beyond The Diff
- For code-path bug, regression, or behavior changes, review the surrounding
path, not just changed lines. Open the runtime entry point, owner module, one
caller, one callee, adjacent tests, and sibling surfaces that should share the
invariant.
- For docs/config/process-only changes, read the changed file, its linked or
adjacent source of truth, and any route/workflow/template the change claims to
affect. Do not require runtime caller/callee evidence when no runtime path
exists.
- Compare against current `origin/main` behavior or current published docs when
regression, compatibility, or user-visible docs accuracy matters.
- For dependency-backed behavior, read the upstream docs/source/types before
judging API use, defaults, output shapes, errors, timeouts, memory behavior, or
compatibility.
- Mention the main files or contracts read when the verdict depends on
code-path, docs, config, or workflow evidence.
- If a required path is uninspected, keep reading or mark
`Remaining uncertainty`; do not call the PR best, proof-sufficient, or
merge-ready.
## Best-Fix Review Loop
Every PR review must explicitly answer: "Is this the best fix, or only a
plausible fix?"
Before verdict:
1. Reconstruct the bug, feature need, or behavior claim from the issue, PR, and
proof.
2. For code-path changes, trace current behavior from entry point to failure or
decision point.
3. For docs/config/process-only changes, trace the reader/operator workflow or
automation path the change is meant to clarify.
4. Read touched files, relevant callers/callees for code changes, adjacent docs
or tests, owner modules, and relevant source-of-truth docs.
5. Read sibling surfaces that should share the invariant or could be broken by a
one-sided fix.
6. Compare against current `origin/main` and shipped behavior when relevant.
7. Identify at least one alternative fix location or shape, then reject it with
evidence.
Review output must include:
- `Best-fix verdict:` best / acceptable mitigation / wrong layer / too narrow /
too broad.
- `Alternatives considered:` 1-3 concrete alternatives and why rejected.
- `Code read:` compact list of main files/contracts checked.
- `Remaining uncertainty:` what was not proven.
## Enforce Bug-Fix Evidence
- Never merge a bug-fix PR based only on issue text, PR text, or AI rationale.
- Before recommending merge for a bug fix, require:
1. symptom evidence such as a repro, logs, failing test, or focused manual
proof
2. a verified root cause in code with file/line
3. blame-backed provenance for regressions when traceable, or commit SHA/date
when no PR is traceable
4. a fix that touches the implicated code path
5. a regression test when feasible, or explicit manual verification plus a
reason no test was added
- If the claim is unsubstantiated or likely wrong, request evidence or changes
instead of recommending merge.
## Decide UI Proof Mode
Use the `clawhub-ui-proof` skill when the maintainer/agent should generate new
visual evidence.
- `before-after`: bug fixes, regressions, changed copy, changed layout, or any
PR where main-vs-candidate comparison clarifies the change.
- `feature`: new page, new flow, new UI state, or behavior that cannot exist on
`origin/main`.
- No generated proof: docs-only, backend-only, tests-only, metadata-only, or
already-sufficient contributor evidence.
Write a temporary Playwright scenario under `.artifacts/proof-scenarios/`; do
not infer manual clicks. Keep screenshots and videos in `.artifacts/` until
publishing. Never commit proof artifacts.
## Final Review Comment With Proof
If this review generated `proof:ui` artifacts, publish them before the final PR
review comment. Do not leave only local `.artifacts/...` paths in a PR comment;
they are useful to the maintainer locally but invisible to GitHub readers.
Use:
```sh
bun run proof:publish -- --proof-dir .artifacts/clawhub-ui-proof/<timestamp> --target-pr <number>
```
`proof:publish` copies the selected files to the `qa-artifacts` branch and
upserts a marker-backed PR comment with a **ClawHub UI Proof** section.
That comment includes:
- the proof mode (`before-after` or `feature`)
- the `report.md` result summary
- the most relevant per-step screenshots
- inline video previews when GIF previews are present
- links to full-run MP4s
- links to raw proof files on the artifact branch
Use `--dry-run` before publishing if you need to inspect the generated comment.
If publishing fails because credentials are missing, report the local proof
directory and the failed command instead of posting a comment that claims
evidence is attached.
## ClawSweeper
ClawSweeper is the bot control plane for automated PR/issue review once ClawHub
dispatch is configured. Until then, use this skill for manual maintainer review.
If ClawSweeper has posted a review, read it as evidence but verify live PR state
before acting.
## Commenting And Labels
- Use literal multiline comment bodies or `--body-file`; never pass escaped
`\n` strings.
- For issue comments and PR comments containing backticks or shell characters,
prefer a single-quoted heredoc or `--body-file` over inline `-b` bodies.
- Do not wrap issue or PR refs like `#123` in backticks when you want GitHub to
auto-link them.
- Keep maintainer comments short: finding, evidence, requested action, and
verification path.
- When no proof artifacts were generated, `gh pr comment --body-file` is fine.
When proof artifacts were generated, use `proof:publish` so screenshots/videos
are published before posting.
- Do not close more than five issues/PRs in one action without explicit
confirmation and the exact target list.
@@ -0,0 +1,321 @@
---
name: convex-create-component
description: Builds reusable Convex components with isolated tables and app-facing APIs.
Use for new components, reusable backend modules, integrations, or component
boundary work.
---
# Convex Create Component
Create reusable Convex components with clear boundaries and a small app-facing
API.
## When to Use
- Creating a new Convex component in an existing app
- Extracting reusable backend logic into a component
- Building a third-party integration that should own its own tables and
workflows
- Packaging Convex functionality for reuse across multiple apps
## When Not to Use
- One-off business logic that belongs in the main app
- Thin utilities that do not need Convex tables or functions
- App-level orchestration that should stay in `convex/`
- Cases where a normal TypeScript library is enough
## Workflow
1. Ask the user what they are building and what the end goal is. If the repo
already makes the answer obvious, say so and confirm before proceeding.
2. Choose the shape using the decision tree below and read the matching
reference file.
3. Decide whether a component is justified. Prefer normal app code or a regular
library if the feature does not need isolated tables, backend functions, or
reusable persistent state.
4. Make a short plan for:
- what tables the component owns
- what public functions it exposes
- what data must be passed in from the app (auth, env vars, parent IDs)
- what stays in the app as wrappers or HTTP mounts
5. Create the component structure with `convex.config.ts`, `schema.ts`, and
function files.
6. Implement functions using the component's own `./_generated/server` imports,
not the app's generated files.
7. Wire the component into the app with `app.use(...)`. If the app does not
already have `convex/convex.config.ts`, create it.
8. Call the component from the app through `components.<name>` using
`ctx.runQuery`, `ctx.runMutation`, or `ctx.runAction`.
9. If React clients, HTTP callers, or public APIs need access, create wrapper
functions in the app instead of exposing component functions directly.
10. Run `npx convex dev` and fix codegen, type, or boundary issues before
finishing.
## Choose the Shape
Ask the user, then pick one path:
| Goal | Shape | Reference |
| ------------------------------------------------- | ---------------- | ----------------------------------- |
| Component for this app only | Local | `references/local-components.md` |
| Publish or share across apps | Packaged | `references/packaged-components.md` |
| User explicitly needs local + shared library code | Hybrid | `references/hybrid-components.md` |
| Not sure | Default to local | `references/local-components.md` |
Read exactly one reference file before proceeding.
## Default Approach
Unless the user explicitly wants an npm package, default to a local component:
- Put it under `convex/components/<componentName>/`
- Define it with `defineComponent(...)` in its own `convex.config.ts`
- Install it from the app's `convex/convex.config.ts` with `app.use(...)`
- Let `npx convex dev` generate the component's own `_generated/` files
## Component Skeleton
A minimal local component with a table and two functions, plus the app wiring.
```ts
// convex/components/notifications/convex.config.ts
import { defineComponent } from "convex/server";
export default defineComponent("notifications");
```
```ts
// convex/components/notifications/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
notifications: defineTable({
userId: v.string(),
message: v.string(),
read: v.boolean(),
}).index("by_user", ["userId"]),
});
```
```ts
// convex/components/notifications/lib.ts
import { v } from "convex/values";
import { mutation, query } from "./_generated/server.js";
export const send = mutation({
args: { userId: v.string(), message: v.string() },
returns: v.id("notifications"),
handler: async (ctx, args) => {
return await ctx.db.insert("notifications", {
userId: args.userId,
message: args.message,
read: false,
});
},
});
export const listUnread = query({
args: { userId: v.string() },
returns: v.array(
v.object({
_id: v.id("notifications"),
_creationTime: v.number(),
userId: v.string(),
message: v.string(),
read: v.boolean(),
}),
),
handler: async (ctx, args) => {
return await ctx.db
.query("notifications")
.withIndex("by_user", (q) => q.eq("userId", args.userId))
.filter((q) => q.eq(q.field("read"), false))
.collect();
},
});
```
```ts
// convex/convex.config.ts
import { defineApp } from "convex/server";
import notifications from "./components/notifications/convex.config.js";
const app = defineApp();
app.use(notifications);
export default app;
```
```ts
// convex/notifications.ts (app-side wrapper)
import { v } from "convex/values";
import { mutation, query } from "./_generated/server";
import { components } from "./_generated/api";
import { getAuthUserId } from "@convex-dev/auth/server";
export const sendNotification = mutation({
args: { message: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx);
if (!userId) throw new Error("Not authenticated");
await ctx.runMutation(components.notifications.lib.send, {
userId,
message: args.message,
});
return null;
},
});
export const myUnread = query({
args: {},
handler: async (ctx) => {
const userId = await getAuthUserId(ctx);
if (!userId) throw new Error("Not authenticated");
return await ctx.runQuery(components.notifications.lib.listUnread, {
userId,
});
},
});
```
Note the reference path shape: a function in
`convex/components/notifications/lib.ts` is called as
`components.notifications.lib.send` from the app.
## Critical Rules
- Keep authentication in the app, because `ctx.auth` is not available inside
components.
- Keep environment access in the app, because component functions cannot read
`process.env`.
- Pass parent app IDs across the boundary as strings, because `Id` types become
plain strings in the app-facing `ComponentApi`.
- Do not use `v.id("parentTable")` for app-owned tables inside component args or
schema, because the component has no access to the app's table namespace.
- Import `query`, `mutation`, and `action` from the component's own
`./_generated/server`, not the app's generated files.
- Do not expose component functions directly to clients. Create app wrappers
when client access is needed, because components are internal and need
auth/env wiring the app provides.
- If the component defines HTTP handlers, mount the routes in the app's
`convex/http.ts`, because components cannot register their own HTTP routes.
- If the component needs pagination, use `paginator` from `convex-helpers`
instead of built-in `.paginate()`, because `.paginate()` does not work across
the component boundary.
- Add `args` and `returns` validators to all public component functions, because
the component boundary requires explicit type contracts.
## Patterns
### Authentication and environment access
```ts
// Bad: component code cannot rely on app auth or env
const identity = await ctx.auth.getUserIdentity();
const apiKey = process.env.OPENAI_API_KEY;
```
```ts
// Good: the app resolves auth and env, then passes explicit values
const userId = await getAuthUserId(ctx);
if (!userId) throw new Error("Not authenticated");
await ctx.runAction(components.translator.translate, {
userId,
apiKey: process.env.OPENAI_API_KEY,
text: args.text,
});
```
### Client-facing API
```ts
// Bad: assuming a component function is directly callable by clients
export const send = components.notifications.send;
```
```ts
// Good: re-export through an app mutation or query
export const sendNotification = mutation({
args: { message: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx);
if (!userId) throw new Error("Not authenticated");
await ctx.runMutation(components.notifications.lib.send, {
userId,
message: args.message,
});
return null;
},
});
```
### IDs across the boundary
```ts
// Bad: parent app table IDs are not valid component validators
args: {
userId: v.id("users");
}
```
```ts
// Good: treat parent-owned IDs as strings at the boundary
args: {
userId: v.string();
}
```
### Advanced Patterns
For additional patterns including function handles for callbacks, deriving
validators from schema, static configuration with a globals table, and
class-based client wrappers, see `references/advanced-patterns.md`.
## Validation
Try validation in this order:
1. `npx convex codegen --component-dir convex/components/<name>`
2. `npx convex codegen`
3. `npx convex dev`
Important:
- Fresh repos may fail these commands until `CONVEX_DEPLOYMENT` is configured.
- Until codegen runs, component-local `./_generated/*` imports and app-side
`components.<name>...` references will not typecheck.
- If validation blocks on Convex login or deployment setup, stop and ask the
user for that exact step instead of guessing.
## Reference Files
Read exactly one of these after the user confirms the goal:
- `references/local-components.md`
- `references/packaged-components.md`
- `references/hybrid-components.md`
Official docs:
[Authoring Components](https://docs.convex.dev/components/authoring)
## Checklist
- [ ] Asked the user what they want to build and confirmed the shape
- [ ] Read the matching reference file
- [ ] Confirmed a component is the right abstraction
- [ ] Planned tables, public API, boundaries, and app wrappers
- [ ] Component lives under `convex/components/<name>/` (or package layout if
publishing)
- [ ] Component imports from its own `./_generated/server`
- [ ] Auth, env access, and HTTP routes stay in the app
- [ ] Parent app IDs cross the boundary as `v.string()`
- [ ] Public functions have `args` and `returns` validators
- [ ] Ran `npx convex dev` and fixed codegen or type issues
@@ -0,0 +1,12 @@
interface:
display_name: "Convex Create Component"
short_description: "Design and build reusable Convex components with clear boundaries."
icon_small: "./assets/icon.svg"
icon_large: "./assets/icon.svg"
brand_color: "#14B8A6"
default_prompt: "Help me create a Convex component for this feature. First check that a
component is actually justified, then design the tables, API surface, and
app-facing wrappers before implementing it."
policy:
allow_implicit_invocation: true
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon">
<path stroke-linecap="round" stroke-linejoin="round" d="m21 7.5-2.25-1.313M21 7.5v2.25m0-2.25-2.25 1.313M3 7.5l2.25-1.313M3 7.5l2.25 1.313M3 7.5v2.25m9 3 2.25-1.313M12 12.75l-2.25-1.313M12 12.75V15m0 6.75 2.25-1.313M12 21.75V19.5m0 2.25-2.25-1.313m0-16.875L12 2.25l2.25 1.313M21 14.25v2.25l-2.25 1.313m-13.5 0L3 16.5v-2.25"/>
</svg>

After

Width:  |  Height:  |  Size: 485 B

@@ -0,0 +1,140 @@
# Advanced Component Patterns
Additional patterns for Convex components that go beyond the basics covered in
the main skill file.
## Function Handles for callbacks
When the app needs to pass a callback function to the component, use function
handles. This is common for components that run app-defined logic on a schedule
or in a workflow.
```ts
// App side: create a handle and pass it to the component
import { createFunctionHandle } from "convex/server";
export const startJob = mutation({
handler: async (ctx) => {
const handle = await createFunctionHandle(internal.myModule.processItem);
await ctx.runMutation(components.workpool.enqueue, {
callback: handle,
});
},
});
```
```ts
// Component side: accept and invoke the handle
import { v } from "convex/values";
import type { FunctionHandle } from "convex/server";
import { mutation } from "./_generated/server.js";
export const enqueue = mutation({
args: { callback: v.string() },
handler: async (ctx, args) => {
const handle = args.callback as FunctionHandle<"mutation">;
await ctx.scheduler.runAfter(0, handle, {});
},
});
```
## Deriving validators from schema
Instead of manually repeating field types in return validators, extend the
schema validator:
```ts
import { v } from "convex/values";
import schema from "./schema.js";
const notificationDoc = schema.tables.notifications.validator.extend({
_id: v.id("notifications"),
_creationTime: v.number(),
});
export const getLatest = query({
args: {},
returns: v.nullable(notificationDoc),
handler: async (ctx) => {
return await ctx.db.query("notifications").order("desc").first();
},
});
```
## Static configuration with a globals table
A common pattern for component configuration is a single-document "globals"
table:
```ts
// schema.ts
export default defineSchema({
globals: defineTable({
maxRetries: v.number(),
webhookUrl: v.optional(v.string()),
}),
// ... other tables
});
```
```ts
// lib.ts
export const configure = mutation({
args: { maxRetries: v.number(), webhookUrl: v.optional(v.string()) },
returns: v.null(),
handler: async (ctx, args) => {
const existing = await ctx.db.query("globals").first();
if (existing) {
await ctx.db.patch(existing._id, args);
} else {
await ctx.db.insert("globals", args);
}
return null;
},
});
```
## Class-based client wrappers
For components with many functions or configuration options, a class-based
client provides a cleaner API. This pattern is common in published components.
```ts
// src/client/index.ts
import type { GenericMutationCtx, GenericDataModel } from "convex/server";
import type { ComponentApi } from "../component/_generated/component.js";
type MutationCtx = Pick<GenericMutationCtx<GenericDataModel>, "runMutation">;
export class Notifications {
constructor(
private component: ComponentApi,
private options?: { defaultChannel?: string },
) {}
async send(ctx: MutationCtx, args: { userId: string; message: string }) {
return await ctx.runMutation(this.component.lib.send, {
...args,
channel: this.options?.defaultChannel ?? "default",
});
}
}
```
```ts
// App usage
import { Notifications } from "@convex-dev/notifications";
import { components } from "./_generated/api";
const notifications = new Notifications(components.notifications, {
defaultChannel: "alerts",
});
export const send = mutation({
args: { message: v.string() },
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx);
await notifications.send(ctx, { userId, message: args.message });
},
});
```
@@ -0,0 +1,38 @@
# Hybrid Convex Components
Read this file only when the user explicitly wants a hybrid setup.
## What This Means
A hybrid component combines a local Convex component with shared library code.
This can help when:
- the user wants a local install but also shared package logic
- the component needs extension points or override hooks
- some logic should live in normal TypeScript code outside the component
boundary
## Default Advice
Treat hybrid as an advanced option, not the default.
Before choosing it, ask:
- Why is a plain local component not enough?
- Why is a packaged component not enough?
- What exactly needs to stay overridable or shared?
If the answer is vague, fall back to local or packaged.
## Risks
- More moving parts
- Harder upgrades and backwards compatibility
- Easier to blur the component boundary
## Checklist
- [ ] User explicitly needs hybrid behavior
- [ ] Local-only and packaged-only options were considered first
- [ ] The extension points are clearly defined before coding
@@ -0,0 +1,39 @@
# Local Convex Components
Read this file when the component should live inside the current app and does
not need to be published as an npm package.
## When to Choose This
- The user wants the simplest path
- The component only needs to work in this repo
- The goal is extracting app logic into a cleaner boundary
## Default Layout
Use this structure unless the repo already has a clear alternative pattern:
```text
convex/
convex.config.ts
components/
<name>/
convex.config.ts
schema.ts
<feature>.ts
```
## Workflow Notes
- Define the component with `defineComponent("<name>")`
- Install it from the app with `defineApp()` and `app.use(...)`
- Keep auth, env access, public API wrappers, and HTTP route mounting in the app
- Let the component own isolated tables and reusable backend workflows
- Add app wrappers if clients need to call into the component
## Checklist
- [ ] Component is inside `convex/components/<name>/`
- [ ] App installs it with `app.use(...)`
- [ ] Component owns only its own tables
- [ ] App wrappers handle client-facing calls when needed
@@ -0,0 +1,54 @@
# Packaged Convex Components
Read this file when the user wants a reusable npm package or a component shared
across multiple apps.
## When to Choose This
- The user wants to publish the component
- The user wants a stable reusable package boundary
- The component will be shared across multiple apps or teams
## Default Approach
- Prefer starting from `npx create-convex@latest --component` when possible
- Keep the official authoring docs as the source of truth for package layout and
exports
- Validate the bundled package through an example app, not just the source files
## Build Flow
When building a packaged component, make sure the bundled output exists before
the example app tries to consume it.
Recommended order:
1. `npx convex codegen --component-dir ./path/to/component`
2. Run the package build command
3. Run `npx convex dev --typecheck-components` in the example app
Do not assume normal app codegen is enough for packaged component workflows.
## Package Exports
If publishing to npm, make sure the package exposes the entry points apps need:
- package root for client helpers, types, or classes
- `./convex.config.js` for installing the component
- `./_generated/component.js` for the app-facing `ComponentApi` type
- `./test` for testing helpers when applicable
## Testing
- Use `convex-test` for component logic
- Register the component schema and modules with the test instance
- Test app-side wrapper code from an example app that installs the package
- Export a small helper from `./test` if consumers need easy test registration
## Checklist
- [ ] Packaging is actually required
- [ ] Build order avoids bundle and codegen races
- [ ] Package exports include install and typing entry points
- [ ] Example app exercises the packaged component
- [ ] Core behavior is covered by tests
@@ -0,0 +1,175 @@
---
name: convex-migration-helper
description: Plans Convex schema and data migrations with widen-migrate-narrow and
@convex-dev/migrations. Use for breaking schema changes, backfills, table
reshaping, or zero-downtime rollouts.
---
# Convex Migration Helper
Safely migrate Convex schemas and data when making breaking changes.
## When to Use
- Adding new required fields to existing tables
- Changing field types or structure
- Splitting or merging tables
- Renaming or deleting fields
- Migrating from nested to relational data
## When Not to Use
- Greenfield schema with no existing data in production or dev
- Adding optional fields that do not need backfilling
- Adding new tables with no existing data to migrate
- Adding or removing indexes with no correctness concern
- Questions about Convex schema design without a migration need
## Key Concepts
### Schema Validation Drives the Workflow
Convex will not let you deploy a schema that does not match the data at rest.
This is the fundamental constraint that shapes every migration:
- You cannot add a required field if existing documents don't have it
- You cannot change a field's type if existing documents have the old type
- You cannot remove a field from the schema if existing documents still have it
This means migrations follow a predictable pattern: **widen the schema, migrate
the data, narrow the schema**.
### Online Migrations
Convex migrations run online, meaning the app continues serving requests while
data is updated asynchronously in batches. During the migration window, your
code must handle both old and new data formats.
### Prefer New Fields Over Changing Types
When changing the shape of data, create a new field rather than modifying an
existing one. This makes the transition safer and easier to roll back.
### Don't Delete Data
Unless you are certain, prefer deprecating fields over deleting them. Mark the
field as `v.optional` and add a code comment explaining it is deprecated and why
it existed.
## Safe Changes (No Migration Needed)
### Adding Optional Field
```typescript
// Before
users: defineTable({
name: v.string(),
});
// After - safe, new field is optional
users: defineTable({
name: v.string(),
bio: v.optional(v.string()),
});
```
### Adding New Table
```typescript
posts: defineTable({
userId: v.id("users"),
title: v.string(),
}).index("by_user", ["userId"]);
```
### Adding Index
```typescript
users: defineTable({
name: v.string(),
email: v.string(),
}).index("by_email", ["email"]);
```
## Breaking Changes: The Deployment Workflow
Every breaking migration follows the same multi-deploy pattern:
**Deploy 1 - Widen the schema:**
1. Update schema to allow both old and new formats (e.g., add optional new
field)
2. Update code to handle both formats when reading
3. Update code to write the new format for new documents
4. Deploy
**Between deploys - Migrate data:**
5. Run migration to backfill existing documents
6. Verify all documents are migrated
**Deploy 2 - Narrow the schema:**
7. Update schema to require the new format only
8. Remove code that handles the old format
9. Deploy
## Using the Migrations Component
For any non-trivial migration, use the
[`@convex-dev/migrations`](https://www.convex.dev/components/migrations)
component. It handles batching, cursor-based pagination, state tracking, resume
from failure, dry runs, and progress monitoring.
See `references/migrations-component.md` for installation, setup, defining and
running migrations, dry runs, status monitoring, and configuration options.
## Common Migration Patterns
See `references/migration-patterns.md` for complete patterns with code examples
covering:
- Adding a required field
- Deleting a field
- Changing a field type
- Splitting nested data into a separate table
- Cleaning up orphaned documents
- Zero-downtime strategies (dual write, dual read)
- Small table shortcut (single internalMutation without the component)
- Verifying a migration is complete
## Common Pitfalls
1. **Making a field required before migrating data**: Convex rejects the deploy
because existing documents lack the field. Always widen the schema first.
2. **Using `.collect()` on large tables**: Hits transaction limits or causes
timeouts. Use the migrations component for proper batched pagination.
`.collect()` is only safe for tables you know are small.
3. **Not writing the new format before migrating**: Documents created during the
migration window will be missed, leaving unmigrated data after the migration
"completes."
4. **Skipping the dry run**: Use `dryRun: true` to validate migration logic
before committing changes to production data. Catches bugs before they touch
real documents.
5. **Deleting fields prematurely**: Prefer deprecating with `v.optional` and a
comment. Only delete after you are confident the data is no longer needed and
no code references it.
6. **Using crons for migration batches**: The migrations component handles
batching via recursive scheduling internally. Crons require manual cleanup
and an extra deploy to remove.
## Migration Checklist
- [ ] Identify the breaking change and plan the multi-deploy workflow
- [ ] Update schema to allow both old and new formats
- [ ] Update code to handle both formats when reading
- [ ] Update code to write the new format for new documents
- [ ] Deploy widened schema and updated code
- [ ] Define migration using the `@convex-dev/migrations` component
- [ ] Test with `dryRun: true`
- [ ] Run migration and monitor status
- [ ] Verify all documents are migrated
- [ ] Update schema to require new format only
- [ ] Clean up code that handled old format
- [ ] Deploy final schema and code
- [ ] Remove migration code once confirmed stable
@@ -0,0 +1,12 @@
interface:
display_name: "Convex Migration Helper"
short_description: "Plan and run safe Convex schema and data migrations."
icon_small: "./assets/icon.svg"
icon_large: "./assets/icon.svg"
brand_color: "#8B5CF6"
default_prompt: "Help me plan and execute this Convex migration safely. Start by identifying
the schema change, the existing data shape, and the widen-migrate-narrow
path before making edits."
policy:
allow_implicit_invocation: true
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99"/>
</svg>

After

Width:  |  Height:  |  Size: 386 B

@@ -0,0 +1,243 @@
# Migration Patterns Reference
Common migration patterns, zero-downtime strategies, and verification techniques
for Convex schema and data migrations.
## Adding a Required Field
```typescript
// Deploy 1: Schema allows both states
users: defineTable({
name: v.string(),
role: v.optional(v.union(v.literal("user"), v.literal("admin"))),
});
// Migration: backfill the field
export const addDefaultRole = migrations.define({
table: "users",
migrateOne: async (ctx, user) => {
if (user.role === undefined) {
await ctx.db.patch(user._id, { role: "user" });
}
},
});
// Deploy 2: After migration completes, make it required
users: defineTable({
name: v.string(),
role: v.union(v.literal("user"), v.literal("admin")),
});
```
## Deleting a Field
Mark the field optional first, migrate data to remove it, then remove from
schema:
```typescript
// Deploy 1: Make optional
// isPro: v.boolean() --> isPro: v.optional(v.boolean())
// Migration
export const removeIsPro = migrations.define({
table: "teams",
migrateOne: async (ctx, team) => {
if (team.isPro !== undefined) {
await ctx.db.patch(team._id, { isPro: undefined });
}
},
});
// Deploy 2: Remove isPro from schema entirely
```
## Changing a Field Type
Prefer creating a new field. You can combine adding and deleting in one
migration:
```typescript
// Deploy 1: Add new field, keep old field optional
// isPro: v.boolean() --> isPro: v.optional(v.boolean()), plan: v.optional(...)
// Migration: convert old field to new field
export const convertToEnum = migrations.define({
table: "teams",
migrateOne: async (ctx, team) => {
if (team.plan === undefined) {
await ctx.db.patch(team._id, {
plan: team.isPro ? "pro" : "basic",
isPro: undefined,
});
}
},
});
// Deploy 2: Remove isPro from schema, make plan required
```
## Splitting Nested Data Into a Separate Table
```typescript
export const extractPreferences = migrations.define({
table: "users",
migrateOne: async (ctx, user) => {
if (user.preferences === undefined) return;
const existing = await ctx.db
.query("userPreferences")
.withIndex("by_user", (q) => q.eq("userId", user._id))
.first();
if (!existing) {
await ctx.db.insert("userPreferences", {
userId: user._id,
...user.preferences,
});
}
await ctx.db.patch(user._id, { preferences: undefined });
},
});
```
Make sure your code is already writing to the new `userPreferences` table for
new users before running this migration, so you don't miss documents created
during the migration window.
## Cleaning Up Orphaned Documents
```typescript
export const deleteOrphanedEmbeddings = migrations.define({
table: "embeddings",
migrateOne: async (ctx, doc) => {
const chunk = await ctx.db
.query("chunks")
.withIndex("by_embedding", (q) => q.eq("embeddingId", doc._id))
.first();
if (!chunk) {
await ctx.db.delete(doc._id);
}
},
});
```
## Zero-Downtime Strategies
During the migration window, your app must handle both old and new data formats.
There are two main strategies.
### Dual Write (Preferred)
Write to both old and new structures. Read from the old structure until
migration is complete.
1. Deploy code that writes both formats, reads old format
2. Run migration on existing data
3. Deploy code that reads new format, still writes both
4. Deploy code that only reads and writes new format
This is preferred because you can safely roll back at any point, the old format
is always up to date.
```typescript
// Bad: only writing to new structure before migration is done
export const createTeam = mutation({
args: { name: v.string(), isPro: v.boolean() },
handler: async (ctx, args) => {
await ctx.db.insert("teams", {
name: args.name,
plan: args.isPro ? "pro" : "basic",
});
},
});
// Good: writing to both structures during migration
export const createTeam = mutation({
args: { name: v.string(), isPro: v.boolean() },
handler: async (ctx, args) => {
const plan = args.isPro ? "pro" : "basic";
await ctx.db.insert("teams", {
name: args.name,
isPro: args.isPro,
plan,
});
},
});
```
### Dual Read
Read both formats. Write only the new format.
1. Deploy code that reads both formats (preferring new), writes only new format
2. Run migration on existing data
3. Deploy code that reads and writes only new format
This avoids duplicating writes, which is useful when having two copies of data
could cause inconsistencies. The downside is that rolling back to before step 1
is harder, since new documents only have the new format.
```typescript
// Good: reading both formats, preferring new
function getTeamPlan(team: Doc<"teams">): "basic" | "pro" {
if (team.plan !== undefined) return team.plan;
return team.isPro ? "pro" : "basic";
}
```
## Small Table Shortcut
For small tables (a few thousand documents at most), you can migrate in a single
`internalMutation` without the component:
```typescript
import { internalMutation } from "./_generated/server";
export const backfillSmallTable = internalMutation({
handler: async (ctx) => {
const docs = await ctx.db.query("smallConfig").collect();
for (const doc of docs) {
if (doc.newField === undefined) {
await ctx.db.patch(doc._id, { newField: "default" });
}
}
},
});
```
```bash
npx convex run migrations:backfillSmallTable
```
Only use `.collect()` when you are certain the table is small. For anything
larger, use the migrations component.
## Verifying a Migration
Query to check remaining unmigrated documents:
```typescript
import { query } from "./_generated/server";
export const verifyMigration = query({
handler: async (ctx) => {
const remaining = await ctx.db
.query("users")
.filter((q) => q.eq(q.field("role"), undefined))
.take(10);
return {
complete: remaining.length === 0,
sampleRemaining: remaining.map((u) => u._id),
};
},
});
```
Or use the component's built-in status monitoring:
```bash
npx convex run --component migrations lib:getStatus --watch
```
@@ -0,0 +1,177 @@
# Migrations Component Reference
Complete guide to the
[`@convex-dev/migrations`](https://www.convex.dev/components/migrations)
component for batched, resumable Convex data migrations.
## Installation
```bash
npm install @convex-dev/migrations
```
## Setup
```typescript
// convex/convex.config.ts
import { defineApp } from "convex/server";
import migrations from "@convex-dev/migrations/convex.config.js";
const app = defineApp();
app.use(migrations);
export default app;
```
```typescript
// convex/migrations.ts
import { Migrations } from "@convex-dev/migrations";
import { components } from "./_generated/api.js";
import { DataModel } from "./_generated/dataModel.js";
export const migrations = new Migrations<DataModel>(components.migrations);
export const run = migrations.runner();
```
The `DataModel` type parameter is optional but provides type safety for
migration definitions.
## Define a Migration
The `migrateOne` function processes a single document. The component handles
batching and pagination automatically.
```typescript
// convex/migrations.ts
export const addDefaultRole = migrations.define({
table: "users",
migrateOne: async (ctx, user) => {
if (user.role === undefined) {
await ctx.db.patch(user._id, { role: "user" });
}
},
});
```
Shorthand: if you return an object, it is applied as a patch automatically.
```typescript
export const clearDeprecatedField = migrations.define({
table: "users",
migrateOne: () => ({ legacyField: undefined }),
});
```
## Run a Migration
From the CLI:
```bash
# Define a one-off runner in convex/migrations.ts:
# export const runIt = migrations.runner(internal.migrations.addDefaultRole);
npx convex run migrations:runIt
# Or use the general-purpose runner
npx convex run migrations:run '{"fn": "migrations:addDefaultRole"}'
```
Programmatically from another Convex function:
```typescript
await migrations.runOne(ctx, internal.migrations.addDefaultRole);
```
## Run Multiple Migrations in Order
```typescript
export const runAll = migrations.runner([
internal.migrations.addDefaultRole,
internal.migrations.clearDeprecatedField,
internal.migrations.normalizeEmails,
]);
```
```bash
npx convex run migrations:runAll
```
If one fails, it stops and will not continue to the next. Call it again to retry
from where it left off. Completed migrations are skipped automatically.
## Dry Run
Test a migration before committing changes:
```bash
npx convex run migrations:runIt '{"dryRun": true}'
```
This runs one batch and then rolls back, so you can see what it would do without
changing any data.
## Check Migration Status
```bash
npx convex run --component migrations lib:getStatus --watch
```
## Cancel a Running Migration
```bash
npx convex run --component migrations lib:cancel '{"name": "migrations:addDefaultRole"}'
```
Or programmatically:
```typescript
await migrations.cancel(ctx, internal.migrations.addDefaultRole);
```
## Run Migrations on Deploy
Chain migration execution after deploying:
```bash
npx convex deploy --cmd 'npm run build' && npx convex run migrations:runAll --prod
```
## Configuration Options
### Custom Batch Size
If documents are large or the table has heavy write traffic, reduce the batch
size to avoid transaction limits or OCC conflicts:
```typescript
export const migrateHeavyTable = migrations.define({
table: "largeDocuments",
batchSize: 10,
migrateOne: async (ctx, doc) => {
// migration logic
},
});
```
### Migrate a Subset Using an Index
Process only matching documents instead of the full table:
```typescript
export const fixEmptyNames = migrations.define({
table: "users",
customRange: (query) => query.withIndex("by_name", (q) => q.eq("name", "")),
migrateOne: () => ({ name: "<unknown>" }),
});
```
### Parallelize Within a Batch
By default each document in a batch is processed serially. Enable parallel
processing if your migration logic does not depend on ordering:
```typescript
export const clearField = migrations.define({
table: "myTable",
parallelize: true,
migrateOne: () => ({ optionalField: undefined }),
});
```
@@ -0,0 +1,184 @@
---
name: convex-performance-audit
description: Audits Convex performance for reads, subscriptions, write contention, and
function limits. Use for slow features, insights findings, OCC conflicts, or
read amplification.
---
# Convex Performance Audit
Diagnose and fix performance problems in Convex applications, one problem class
at a time.
## When to Use
- A Convex page or feature feels slow or expensive
- `npx convex insights --details` reports high bytes read, documents read, or
OCC conflicts
- Low-freshness read paths are using reactivity where point-in-time reads would
do
- OCC conflict errors or excessive mutation retries
- High subscription count or slow UI updates
- Functions approaching execution or transaction limits
- The same performance pattern needs fixing across sibling functions
## When Not to Use
- Initial Convex setup, auth setup, or component extraction
- Pure schema migrations with no performance goal
- One-off micro-optimizations without a user-visible or deployment-visible
problem
## Guardrails
- Prefer simpler code when scale is small, traffic is modest, or the available
signals are weak
- Do not recommend digest tables, document splitting, fetch-strategy changes, or
migration-heavy rollouts unless there is a measured signal, a clearly
unbounded path, or a known hot read/write path
- In Convex, a simple scan on a small table is often acceptable. Do not invent
structural work just because a pattern is not ideal at large scale
## First Step: Gather Signals
Start with the strongest signal available:
1. If deployment Health insights are already available from the user or the
current context, treat them as a first-class source of performance signals.
2. If CLI insights are available, run `npx convex insights --details`. Use
`--prod`, `--preview-name`, or `--deployment-name` when needed.
- If the local repo's Convex CLI is too old to support `insights`, try
`npx -y convex@latest insights --details` before giving up.
3. If the repo already uses `convex-doctor`, you may treat its findings as
hints. Do not require it, and do not treat it as the source of truth.
4. If runtime signals are unavailable, audit from code anyway, but keep the
guardrails above in mind. Lack of insights is not proof of health, but it is
also not proof that a large refactor is warranted.
## Signal Routing
After gathering signals, identify the problem class and read the matching
reference file.
| Signal | Reference |
| -------------------------------------------------------------- | ----------------------------------------- |
| High bytes or documents read, JS filtering, unnecessary joins | `references/hot-path-rules.md` |
| OCC conflict errors, write contention, mutation retries | `references/occ-conflicts.md` |
| High subscription count, slow UI updates, excessive re-renders | `references/subscription-cost.md` |
| Function timeouts, transaction size errors, large payloads | `references/function-budget.md` |
| General "it's slow" with no specific signal | Start with `references/hot-path-rules.md` |
Multiple problem classes can overlap. Read the most relevant reference first,
then check the others if symptoms remain.
## Escalate Larger Fixes
If the likely fix is invasive, cross-cutting, or migration-heavy, stop and
present options before editing.
Examples:
- introducing digest or summary tables across multiple flows
- splitting documents to isolate frequently-updated fields
- reworking pagination or fetch strategy across several screens
- switching to a new index or denormalized field that needs migration-safe
rollout
When correctness depends on handling old and new states during a rollout,
consult `skills/convex-migration-helper/SKILL.md` for the migration workflow.
## Workflow
### 1. Scope the problem
Pick one concrete user flow from the actual project. Look at the codebase,
client pages, and API surface to find the flow that matches the symptom.
Write down:
- entrypoint functions
- client callsites using `useQuery`, `usePaginatedQuery`, or `useMutation`
- tables read
- tables written
- whether the path is high-read, high-write, or both
### 2. Trace the full read and write set
For each function in the path:
1. Trace every `ctx.db.get()` and `ctx.db.query()`
2. Trace every `ctx.db.patch()`, `ctx.db.replace()`, and `ctx.db.insert()`
3. Note foreign-key lookups, JS-side filtering, and full-document reads
4. Identify all sibling functions touching the same tables
5. Identify reactive stats, aggregates, or widgets rendered on the same page
In Convex, every extra read increases transaction work, and every write can
invalidate reactive subscribers. Treat read amplification and invalidation
amplification as first-class problems.
### 3. Apply fixes from the relevant reference
Read the reference file matching your problem class. Each reference includes
specific patterns, code examples, and a recommended fix order.
Do not stop at the single function named by an insight. Trace sibling readers
and writers touching the same tables.
### 4. Fix sibling functions together
When one function touching a table has a performance bug, audit sibling
functions for the same pattern.
After finding one problem, inspect both sibling readers and sibling writers for
the same table family, including companion digest or summary tables.
Examples:
- If one list query switches from full docs to a digest table, inspect the other
list queries for that table
- If one mutation isolates a frequently-updated field or splits a hot document,
inspect the other writers to the same table
- If one read path needs a migration-safe rollout for an unbackfilled field,
inspect sibling reads for the same rollout risk
Do not leave one path fixed and another path on the old pattern unless there is
a clear product reason.
### 5. Verify before finishing
Confirm all of these:
1. Results are the same as before, no dropped records
2. Eliminated reads or writes are no longer in the path where expected
3. Fallback behavior works when denormalized or indexed fields are missing
4. Frequently-updated fields are isolated from widely-read documents where
needed
5. Every relevant sibling reader and writer was inspected, not just the original
function
## Reference Files
- `references/hot-path-rules.md` - Read amplification, invalidation,
denormalization, indexes, digest tables
- `references/occ-conflicts.md` - Write contention, OCC resolution, hot document
splitting
- `references/subscription-cost.md` - Reactive query cost, subscription
granularity, point-in-time reads
- `references/function-budget.md` - Execution limits, transaction size, large
documents, payload size
Also check the official
[Convex Best Practices](https://docs.convex.dev/understanding/best-practices/)
page for additional patterns covering argument validation, access control, and
code organization that may surface during the audit.
## Checklist
- [ ] Gathered signals from insights, dashboard, or code audit
- [ ] Identified the problem class and read the matching reference
- [ ] Scoped one concrete user flow or function path
- [ ] Traced every read and write in that path
- [ ] Identified sibling functions touching the same tables
- [ ] Applied fixes from the reference, following the recommended fix order
- [ ] Fixed sibling functions consistently
- [ ] Verified behavior and confirmed no regressions
@@ -0,0 +1,12 @@
interface:
display_name: "Convex Performance Audit"
short_description: "Audit slow Convex reads, subscriptions, OCC conflicts, and limits."
icon_small: "./assets/icon.svg"
icon_large: "./assets/icon.svg"
brand_color: "#EF4444"
default_prompt: "Audit this Convex app for performance issues. Start with the strongest
signal available, identify the problem class, and suggest the smallest
high-impact fix before proposing bigger structural changes."
policy:
allow_implicit_invocation: true
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 3v1.5M4.5 8.25H3m18 0h-1.5M4.5 12H3m18 0h-1.5m-15 3.75H3m18 0h-1.5M8.25 19.5V21M12 3v1.5m0 15V21m3.75-18v1.5m0 15V21m-9-1.5h10.5a2.25 2.25 0 0 0 2.25-2.25V6.75a2.25 2.25 0 0 0-2.25-2.25H6.75A2.25 2.25 0 0 0 4.5 6.75v10.5a2.25 2.25 0 0 0 2.25 2.25Zm.75-12h9v9h-9v-9Z"/>
</svg>

After

Width:  |  Height:  |  Size: 490 B

@@ -0,0 +1,254 @@
# Function Budget
Use these rules when functions are hitting execution limits, transaction size
errors, or returning excessively large payloads to the client.
## Core Principle
Convex functions run inside transactions with budgets for time, reads, and
writes. Staying well within these limits is not just about avoiding errors, it
reduces latency and contention.
## Limits to Know
These are the current values from the
[Convex limits docs](https://docs.convex.dev/production/state/limits). Check
that page for the latest numbers.
| Resource | Limit |
| --------------------------------- | ----------------------------------------------------- |
| Query/mutation execution time | 1 second (user code only, excludes DB operations) |
| Action execution time | 10 minutes |
| Data read per transaction | 16 MiB |
| Data written per transaction | 16 MiB |
| Documents scanned per transaction | 32,000 (includes documents filtered out by `.filter`) |
| Index ranges read per transaction | 4,096 (each `db.get` and `db.query` call) |
| Documents written per transaction | 16,000 |
| Individual document size | 1 MiB |
| Function return value size | 16 MiB |
## Symptoms
- "Function execution took too long" errors
- "Transaction too large" or read/write set size errors
- Slow queries that read many documents
- Client receiving large payloads that slow down page load
- `npx convex insights --details` showing high bytes read
## Common Causes
### Unbounded collection
A query that calls `.collect()` on a table without a reasonable limit. As the
table grows, the query reads more and more documents.
### Large document reads on hot paths
Reading documents with large fields (rich text, embedded media references, long
arrays) when only a small subset of the data is needed for the current view.
### Mutation doing too much work
A single mutation that updates hundreds of documents, backfills data, or
rebuilds derived state in one transaction.
### Returning too much data to the client
A query returning full documents when the client only needs a few fields.
## Fix Order
### 1. Bound your reads
Never `.collect()` without a limit on a table that can grow unbounded.
```ts
// Bad: unbounded read, breaks as the table grows
const messages = await ctx.db.query("messages").collect();
```
```ts
// Good: paginate or limit
const messages = await ctx.db
.query("messages")
.withIndex("by_channel", (q) => q.eq("channelId", channelId))
.order("desc")
.take(50);
```
### 2. Read smaller shapes
If the list page only needs title, author, and date, do not read full documents
with rich content fields.
Use digest or summary tables for hot list pages. See `hot-path-rules.md` for the
digest table pattern.
### 3. Break large mutations into batches
If a mutation needs to update hundreds of documents, split it into a
self-scheduling chain.
```ts
// Bad: one mutation updating every row
export const backfillAll = internalMutation({
handler: async (ctx) => {
const docs = await ctx.db.query("items").collect();
for (const doc of docs) {
await ctx.db.patch(doc._id, { newField: computeValue(doc) });
}
},
});
```
```ts
// Good: cursor-based batch processing
export const backfillBatch = internalMutation({
args: { cursor: v.optional(v.string()), batchSize: v.optional(v.number()) },
handler: async (ctx, args) => {
const batchSize = args.batchSize ?? 100;
const result = await ctx.db
.query("items")
.paginate({ cursor: args.cursor ?? null, numItems: batchSize });
for (const doc of result.page) {
if (doc.newField === undefined) {
await ctx.db.patch(doc._id, { newField: computeValue(doc) });
}
}
if (!result.isDone) {
await ctx.scheduler.runAfter(0, internal.items.backfillBatch, {
cursor: result.continueCursor,
batchSize,
});
}
},
});
```
### 4. Move heavy work to actions
Queries and mutations run inside Convex's transactional runtime with strict
budgets. If you need to do CPU-intensive computation, call external APIs, or
process large files, use an action instead.
Actions run outside the transaction and can call mutations to write results
back.
```ts
// Bad: heavy computation inside a mutation
export const processUpload = mutation({
handler: async (ctx, args) => {
const result = expensiveComputation(args.data);
await ctx.db.insert("results", result);
},
});
```
```ts
// Good: action for heavy work, mutation for the write
export const processUpload = action({
handler: async (ctx, args) => {
const result = expensiveComputation(args.data);
await ctx.runMutation(internal.results.store, { result });
},
});
```
### 5. Trim return values
Only return what the client needs. If a query fetches full documents but the
component only renders a few fields, map the results before returning.
```ts
// Bad: returns full documents including large content fields
export const list = query({
handler: async (ctx) => {
return await ctx.db.query("articles").take(20);
},
});
```
```ts
// Good: project to only the fields the client needs
export const list = query({
handler: async (ctx) => {
const articles = await ctx.db.query("articles").take(20);
return articles.map((a) => ({
_id: a._id,
title: a.title,
author: a.author,
createdAt: a._creationTime,
}));
},
});
```
### 6. Replace `ctx.runQuery` and `ctx.runMutation` with helper functions
Inside queries and mutations, `ctx.runQuery` and `ctx.runMutation` have overhead
compared to calling a plain TypeScript helper function. They run in the same
transaction but pay extra per-call cost.
```ts
// Bad: unnecessary overhead from ctx.runQuery inside a mutation
export const createProject = mutation({
handler: async (ctx, args) => {
const user = await ctx.runQuery(api.users.getCurrentUser);
await ctx.db.insert("projects", { ...args, ownerId: user._id });
},
});
```
```ts
// Good: plain helper function, no extra overhead
export const createProject = mutation({
handler: async (ctx, args) => {
const user = await getCurrentUser(ctx);
await ctx.db.insert("projects", { ...args, ownerId: user._id });
},
});
```
Exception: components require `ctx.runQuery`/`ctx.runMutation`. Use them there,
but prefer helpers everywhere else.
### 7. Avoid unnecessary `runAction` calls
`runAction` from within an action creates a separate function invocation with
its own memory and CPU budget. The parent action just sits idle waiting. Replace
with a plain TypeScript function call unless you need a different runtime (e.g.
calling Node.js code from the Convex runtime).
```ts
// Bad: runAction overhead for no reason
export const processItems = action({
handler: async (ctx, args) => {
for (const item of args.items) {
await ctx.runAction(internal.items.processOne, { item });
}
},
});
```
```ts
// Good: plain function call
export const processItems = action({
handler: async (ctx, args) => {
for (const item of args.items) {
await processOneItem(ctx, { item });
}
},
});
```
## Verification
1. No function execution or transaction size errors
2. `npx convex insights --details` shows reduced bytes read
3. Large mutations are batched and self-scheduling
4. Client payloads are reasonably sized for the UI they serve
5. `ctx.runQuery`/`ctx.runMutation` in queries and mutations replaced with
helpers where possible
6. Sibling functions with similar patterns were checked
@@ -0,0 +1,410 @@
# Hot Path Rules
Use these rules when the top-level workflow points to read amplification,
denormalization, index rollout, reactive query cost, or invalidation-heavy
writes.
## Contents
- Core Principle
- Consistency Rule
- 1. Push Filters To Storage (indexes, migration rule, redundant indexes)
- 2. Minimize Data Sources (denormalization, fallback rule)
- 3. Minimize Row Size (digest tables)
- 4. Skip No-Op Writes
- 5. Match Consistency To Read Patterns (high-read/low-write,
high-read/high-write)
- Convex-Specific Notes (reactive queries, point-in-time reads, triggers,
aggregates, backfills)
- Verification
## Core Principle
Every byte read or written multiplies with concurrency.
Think:
`cost x calls_per_second x 86400`
In Convex, every write can also fan out into reactive invalidation, replication
work, and downstream sync.
## Consistency Rule
If you fix a hot-path pattern for one function, audit sibling functions touching
the same tables for the same pattern.
Do this especially for:
- multiple list queries over the same table
- multiple writers to the same table
- public browse and search queries over the same records
- helper functions reused by more than one endpoint
## 1. Push Filters To Storage
Both JavaScript `.filter()` and the Convex query `.filter()` method after a DB
scan mean you already paid for the read. The Convex `.filter()` method has the
same performance as filtering in JS, it does not push the predicate to the
storage layer. Only `.withIndex()` and `.withSearchIndex()` actually reduce the
documents scanned.
Prefer:
- `withIndex(...)`
- `.withSearchIndex(...)` for text search
- narrower tables
- summary tables
before accepting a scan-plus-filter pattern.
```ts
// Bad: scans then filters in JavaScript
export const listOpen = query({
args: {},
handler: async (ctx) => {
const tasks = await ctx.db.query("tasks").collect();
return tasks.filter((task) => task.status === "open");
},
});
```
```ts
// Also bad: Convex .filter() does not push to storage either
export const listOpen = query({
args: {},
handler: async (ctx) => {
return await ctx.db
.query("tasks")
.filter((q) => q.eq(q.field("status"), "open"))
.collect();
},
});
```
```ts
// Good: use an index so storage does the filtering
export const listOpen = query({
args: {},
handler: async (ctx) => {
return await ctx.db
.query("tasks")
.withIndex("by_status", (q) => q.eq("status", "open"))
.collect();
},
});
```
### Migration rule for indexes
New indexes on partially backfilled fields can create correctness bugs during
rollout.
Important Convex detail:
`undefined !== false`
If an older document is missing a field entirely, it will not match a compound
index entry that expects `false`.
Do not trust old comments saying a field is "not backfilled" or "already
backfilled". Verify.
If correctness depends on handling old and new states during rollout, do not
improvise a partial-backfill workaround in the hot path. Use a migration-safe
rollout and consult `skills/convex-migration-helper/SKILL.md`.
```ts
// Bad: optional booleans can miss older rows where the field is undefined
const projects = await ctx.db
.query("projects")
.withIndex("by_archived_and_updated", (q) => q.eq("isArchived", false))
.order("desc")
.take(20);
```
```ts
// Good: switch hot-path reads only after the rollout is migration-safe
// See the migration helper skill for dual-read / backfill / cutover patterns.
```
### Check for redundant indexes
Indexes like `by_foo` and `by_foo_and_bar` are usually redundant. You only need
`by_foo_and_bar`, since you can query it with just the `foo` condition and omit
`bar`. Extra indexes add storage cost and write overhead on every insert, patch,
and delete.
```ts
// Bad: two indexes where one would do
defineTable({ team: v.id("teams"), user: v.id("users") })
.index("by_team", ["team"])
.index("by_team_and_user", ["team", "user"]);
```
```ts
// Good: single compound index serves both query patterns
defineTable({ team: v.id("teams"), user: v.id("users") }).index("by_team_and_user", [
"team",
"user",
]);
```
Exception: `.index("by_foo", ["foo"])` is really an index on `foo` +
`_creationTime`, while `.index("by_foo_and_bar", ["foo", "bar"])` is on `foo` +
`bar` + `_creationTime`. If you need results sorted by `foo` then
`_creationTime`, you need the single-field index because the compound one would
sort by `bar` first.
## 2. Minimize Data Sources
Trace every read.
If a function resolves a foreign key for a tiny display field and a denormalized
copy already exists, prefer the denormalized field on the hot path.
### When to denormalize
Denormalize when all of these are true:
- the path is hot
- the joined document is much larger than the field you need
- many readers are paying that join cost repeatedly
Useful mental model:
`join_cost = rows_per_page x foreign_doc_size x pages_per_second`
Small-table joins are often fine. Large-document joins for tiny fields on hot
list pages are usually not.
### Fallback rule
Denormalized data is an optimization. Live data is the correctness path.
Rules:
- If the denormalized field is missing or null, fall back to the live read
- Do not show placeholders instead of falling back
- In lookup maps, only include fully populated entries
```ts
// Bad: missing denormalized data becomes a placeholder and blocks correctness
const ownerName = project.ownerName ?? "Unknown owner";
```
```ts
// Good: denormalized data is an optimization, not the only source of truth
const ownerName = project.ownerName ?? (await ctx.db.get(project.ownerId))?.name ?? null;
```
Bad lookup map pattern:
```ts
const ownersById = {
[project.ownerId]: { ownerName: null },
};
```
That blocks fallback because the map says "I have data" when it does not.
Good lookup map pattern:
```ts
const ownersById =
project.ownerName !== undefined && project.ownerName !== null
? { [project.ownerId]: { ownerName: project.ownerName } }
: {};
```
### No denormalized copy yet
Prefer adding fields to an existing summary, companion, or digest table instead
of bloating the primary hot-path table.
If introducing the new field or table requires a staged rollout, backfill, or
old/new-shape handling, use the migration helper skill for the rollout plan.
Rollout order:
1. Update schema
2. Update write path
3. Backfill
4. Switch read path
## 3. Minimize Row Size
Hot list pages should read the smallest document shape that still answers the
UI.
Prefer summary or digest tables over full source tables when:
- the list page only needs a subset of fields
- source documents are large
- the query is high volume
An 800 byte summary row is materially cheaper than a 3 KB full document on a hot
page.
Digest tables are a tradeoff, not a default:
- Worth it when the path is clearly hot, the source rows are much larger than
the UI needs, or many readers are repeatedly paying the same join and payload
cost
- Probably not worth it when an indexed read on the source table is already
cheap enough, the table is still small, or the extra write and migration
complexity would dominate the benefit
```ts
// Bad: list page reads source docs, then joins owner data per row
const projects = await ctx.db
.query("projects")
.withIndex("by_public", (q) => q.eq("isPublic", true))
.collect();
```
```ts
// Good: list page reads the smaller digest shape first
const projects = await ctx.db
.query("projectDigests")
.withIndex("by_public_and_updated", (q) => q.eq("isPublic", true))
.order("desc")
.take(20);
```
## 4. Isolate Frequently-Updated Fields
Convex already no-ops unchanged writes. The invalidation problem here is real
writes hitting documents that many queries subscribe to.
Move high-churn fields like `lastSeen`, counters, presence, or ephemeral status
off widely-read documents when most readers do not need them.
Apply this across sibling writers too. Splitting one write path does not help
much if three other mutations still update the same widely-read document.
```ts
// Bad: every presence heartbeat invalidates subscribers to the whole profile
await ctx.db.patch(user._id, {
name: args.name,
avatarUrl: args.avatarUrl,
lastSeen: Date.now(),
});
```
```ts
// Good: keep profile reads stable, move heartbeat updates to a separate document
await ctx.db.patch(user._id, {
name: args.name,
avatarUrl: args.avatarUrl,
});
await ctx.db.patch(presence._id, {
lastSeen: Date.now(),
});
```
## 5. Match Consistency To Read Patterns
Choose read strategy based on traffic shape.
### High-read, low-write
Examples:
- public browse pages
- search results
- landing pages
- directory listings
Prefer:
- point-in-time reads where appropriate
- explicit refresh
- local state for pagination
- caching where appropriate
Do not treat subscriptions as automatically wrong here. Prefer point-in-time
reads only when the product does not need live freshness and the reactive cost
is material. See `subscription-cost.md` for detailed patterns.
### High-read, high-write
Examples:
- collaborative editors
- live dashboards
- presence-heavy views
Reactive queries may be worth the ongoing cost.
## Convex-Specific Notes
### Reactive queries
Every `ctx.db.get()` and `ctx.db.query()` contributes to the invalidation set
for the query.
On the client:
- `useQuery` creates a live subscription
- `usePaginatedQuery` creates a live subscription per page
For low-freshness flows, consider a point-in-time read instead of a live
subscription only when the product does not need updates pushed automatically.
### Point-in-time reads
Framework helpers, server-rendered fetches, or one-shot client reads can avoid
ongoing subscription cost when live updates are not useful.
Use them for:
- aggregate snapshots
- reports
- low-churn listings
- pages where explicit refresh is fine
### Triggers and fan-out
Triggers fire on every write, including writes that did not materially change
the document.
When a write exists only to keep derived state in sync:
- diff before patching
- move expensive non-blocking work to `ctx.scheduler.runAfter` when appropriate
### Aggregates
Reactive global counts invalidate frequently on busy tables.
Prefer:
- one-shot aggregate fetches
- periodic recomputation
- precomputed summary rows
for global stats that do not need live updates every second.
### Backfills
For larger backfills, use cursor-based, self-scheduling `internalMutation` jobs
or the migrations component.
Deploy code that can handle both states before running the backfill.
During the gap:
- writes should populate the new shape
- reads should fall back safely
## Verification
Before closing the audit, confirm:
1. Same results as before, no dropped records
2. The removed table or lookup is no longer in the hot-path read set
3. Tests or validation cover fallback behavior
4. Migration safety is preserved while fields or indexes are unbackfilled
5. Sibling functions were fixed consistently
@@ -0,0 +1,137 @@
# OCC Conflict Resolution
Use these rules when insights, logs, or dashboard health show OCC (Optimistic
Concurrency Control) conflicts, mutation retries, or write contention on hot
tables.
## Core Principle
Convex uses optimistic concurrency control. When two transactions read or write
overlapping data, one succeeds and the other retries automatically. High
contention means wasted work and increased latency.
## Symptoms
- OCC conflict errors in deployment logs or health page
- Mutations retrying multiple times before succeeding
- User-visible latency spikes on write-heavy pages
- `npx convex insights --details` showing high conflict rates
## Common Causes
### Hot documents
Multiple mutations writing to the same document concurrently. Classic examples:
a global counter, a shared settings row, or a "last updated" timestamp on a
parent record.
### Broad read sets causing false conflicts
A query that scans a large table range creates a broad read set. If any write
touches that range, the query's transaction conflicts even if the specific
document the query cared about was not modified.
### Fan-out from triggers or cascading writes
A single user action triggers multiple mutations that all touch related
documents. Each mutation competes with the others.
Database triggers (e.g. from `convex-helpers`) run inside the same transaction
as the mutation that caused them. If a trigger does heavy work, reads extra
tables, or writes to many documents, it extends the transaction's read/write set
and increases the window for conflicts. Keep trigger logic minimal, or move
expensive derived work to a scheduled function.
### Write-then-read chains
A mutation writes a document, then a reactive query re-reads it, then another
mutation writes it again. Under load, these chains stack up.
## Fix Order
### 1. Reduce read set size
Narrower reads mean fewer false conflicts.
```ts
// Bad: broad scan creates a wide conflict surface
const allTasks = await ctx.db.query("tasks").collect();
const mine = allTasks.filter((t) => t.ownerId === userId);
```
```ts
// Good: indexed query touches only relevant documents
const mine = await ctx.db
.query("tasks")
.withIndex("by_owner", (q) => q.eq("ownerId", userId))
.collect();
```
### 2. Split hot documents
When many writers target the same document, split the contention point.
```ts
// Bad: every vote increments the same counter document
const counter = await ctx.db.get(pollCounterId);
await ctx.db.patch(pollCounterId, { count: counter!.count + 1 });
```
```ts
// Good: shard the counter across multiple documents, aggregate on read
const shardIndex = Math.floor(Math.random() * SHARD_COUNT);
const shardId = shardIds[shardIndex];
const shard = await ctx.db.get(shardId);
await ctx.db.patch(shardId, { count: shard!.count + 1 });
```
Aggregate the shards in a query or scheduled job when you need the total.
### 3. Move non-critical work to scheduled functions
If a mutation does primary work plus secondary bookkeeping (analytics,
non-critical notifications, cache warming), the bookkeeping extends the
transaction's lifetime and read/write set.
```ts
// Bad: canonical write and derived work happen in the same transaction
await ctx.db.patch(userId, { name: args.name });
await ctx.db.insert("userUpdateAnalytics", {
userId,
kind: "name_changed",
name: args.name,
});
```
```ts
// Good: keep the primary write small, defer the analytics work
await ctx.db.patch(userId, { name: args.name });
await ctx.scheduler.runAfter(0, internal.users.recordNameChangeAnalytics, {
userId,
name: args.name,
});
```
### 4. Combine competing writes
If two mutations must update the same document atomically, consider whether they
can be combined into a single mutation call from the client, reducing round
trips and conflict windows.
Do not introduce artificial locks or queues unless the above steps have been
tried first.
## Related: Invalidation Scope
Splitting hot documents also reduces subscription invalidation, not just OCC
contention. If a document is written frequently and read by many queries, those
queries re-run on every write even when the fields they care about have not
changed. See `subscription-cost.md` section 4 ("Isolate frequently-updated
fields") for that pattern.
## Verification
1. OCC conflict rate has dropped in insights or dashboard
2. Mutation latency is lower and more consistent
3. No data correctness regressions from splitting or scheduling changes
4. Sibling writers to the same hot documents were fixed consistently
@@ -0,0 +1,297 @@
# Subscription Cost
Use these rules when the problem is too many reactive subscriptions, queries
invalidating too frequently, or React components re-rendering excessively due to
Convex state changes.
## Core Principle
Every `useQuery` and `usePaginatedQuery` call creates a live subscription. The
server tracks the query's read set and re-executes the query whenever any
document in that read set changes. Subscription cost scales with:
`subscriptions x invalidation_frequency x query_cost`
Subscriptions are not inherently bad. Convex reactivity is often the right
default. The goal is to reduce unnecessary invalidation work, not to eliminate
subscriptions on principle.
## Symptoms
- Dashboard shows high active subscription count
- UI feels sluggish or laggy despite fast individual queries
- React profiling shows frequent re-renders from Convex state
- Pages with many components each running their own `useQuery`
- Paginated lists where every loaded page stays subscribed
## Common Causes
### Reactive queries on low-freshness flows
Some user flows are read-heavy and do not need live updates every time the
underlying data changes. In those cases, ongoing subscriptions may cost more
than they are worth.
### Overly broad queries
A query that returns a large result set invalidates whenever any document in
that set changes. The broader the query, the more frequent the invalidation.
### Too many subscriptions per page
A page with 20 list items, each running its own `useQuery` to fetch related
data, creates 20+ subscriptions per visitor.
### Paginated queries keeping all pages live
`usePaginatedQuery` with `loadMore` keeps every loaded page subscribed. On a
page where a user has scrolled through 10 pages, all 10 stay reactive.
### Frequently-updated fields on widely-read documents
A document that many queries touch gets a frequently-updated field (like
`lastSeen`, `lastActiveAt`, or a counter). Every write to that field invalidates
every subscription that reads the document, even if those subscriptions never
use the field. This is different from OCC conflicts (see `occ-conflicts.md`),
which are write-vs-write contention. This is write-vs-subscription: the write
succeeds fine, but it forces hundreds of queries to re-run for no reason.
## Fix Order
### 1. Use point-in-time reads when live updates are not valuable
Keep `useQuery` and `usePaginatedQuery` by default when the product benefits
from fresh live data.
Consider a point-in-time read instead when all of these are true:
- the flow is high-read
- the underlying data changes less often than users need to see
- explicit refresh, periodic refresh, or a fresh read on navigation is
acceptable
Possible implementations depend on environment:
- a server-rendered fetch
- a framework helper like `fetchQuery`
- a point-in-time client read such as `ConvexHttpClient.query()`
```ts
// Reactive by default when fresh live data matters
function TeamPresence() {
const presence = useQuery(api.teams.livePresence, { teamId });
return <PresenceList users={presence} />;
}
```
```ts
// Point-in-time read when explicit refresh is acceptable
import { ConvexHttpClient } from "convex/browser";
const client = new ConvexHttpClient(import.meta.env.VITE_CONVEX_URL);
function SnapshotView() {
const [items, setItems] = useState<Item[]>([]);
useEffect(() => {
client.query(api.items.snapshot).then(setItems);
}, []);
return <ItemGrid items={items} />;
}
```
Good candidates for point-in-time reads:
- aggregate snapshots
- reports
- low-churn listings
- flows where explicit refresh is already acceptable
Keep reactive for:
- collaborative editing
- live dashboards
- presence-heavy views
- any surface where users expect fresh changes to appear automatically
### 2. Batch related data into fewer queries
Instead of N components each fetching their own related data, fetch it in a
single query.
```ts
// Bad: each card fetches its own author
function ProjectCard({ project }: { project: Project }) {
const author = useQuery(api.users.get, { id: project.authorId });
return <Card title={project.name} author={author?.name} />;
}
```
```ts
// Good: parent query returns projects with author names included
function ProjectList() {
const projects = useQuery(api.projects.listWithAuthors);
return projects?.map((p) => (
<Card key={p._id} title={p.name} author={p.authorName} />
));
}
```
This can use denormalized fields or server-side joins in the query handler.
Either way, it is one subscription instead of N.
This is not automatically better. If the combined query becomes much broader and
invalidates much more often, several narrower subscriptions may be the better
tradeoff. Optimize for total invalidation cost, not raw subscription count.
### 3. Use skip to avoid unnecessary subscriptions
The `"skip"` value prevents a subscription from being created when the arguments
are not ready.
```ts
// Bad: subscribes with undefined args, wastes a subscription slot
const profile = useQuery(api.users.getProfile, { userId: selectedId! });
```
```ts
// Good: skip when there is nothing to fetch
const profile = useQuery(api.users.getProfile, selectedId ? { userId: selectedId } : "skip");
```
### 4. Isolate frequently-updated fields into separate documents
If a document is widely read but has a field that changes often, move that field
to a separate document. Queries that do not need the field will no longer be
invalidated by its writes.
```ts
// Bad: lastSeen lives on the user doc, every heartbeat invalidates
// every query that reads this user
const users = defineTable({
name: v.string(),
email: v.string(),
lastSeen: v.number(),
});
```
```ts
// Good: lastSeen lives in a separate heartbeat doc
const users = defineTable({
name: v.string(),
email: v.string(),
heartbeatId: v.id("heartbeats"),
});
const heartbeats = defineTable({
lastSeen: v.number(),
});
```
Queries that only need `name` and `email` no longer re-run on every heartbeat.
Queries that actually need online status fetch the heartbeat document
explicitly.
For an even further optimization, if you only need a coarse online/offline
boolean rather than the exact `lastSeen` timestamp, add a separate presence
document with an `isOnline` flag. Update it immediately when a user comes
online, and use a cron to batch-mark users offline when their heartbeat goes
stale. This way the presence query only invalidates when online status actually
changes, not on every heartbeat.
### 5. Use the aggregate component for counts and sums
Reactive global counts (`SELECT COUNT(*)` equivalent) invalidate on every insert
or delete to the table. The
[`@convex-dev/aggregate`](https://www.npmjs.com/package/@convex-dev/aggregate)
component maintains denormalized COUNT, SUM, and MAX values efficiently so you
do not need a reactive query scanning the full table.
Use it for leaderboards, totals, "X items" badges, or any stat that would
otherwise require scanning many rows reactively.
If the aggregate component is not appropriate, prefer point-in-time reads for
global stats, or precomputed summary rows updated by a cron or trigger, over
reactive queries that scan large tables.
### 6. Narrow query read sets
Queries that return less data and touch fewer documents invalidate less often.
```ts
// Bad: returns all fields, invalidates on any field change
export const list = query({
handler: async (ctx) => {
return await ctx.db.query("projects").collect();
},
});
```
```ts
// Good: use a digest table with only the fields the list needs
export const listDigests = query({
handler: async (ctx) => {
return await ctx.db.query("projectDigests").collect();
},
});
```
Writes to fields not in the digest table do not invalidate the digest query.
### 7. Remove `Date.now()` from queries
Using `Date.now()` inside a query defeats Convex's query cache. The cache is
invalidated frequently to avoid showing stale time-dependent results, which
increases database work even when the underlying data has not changed.
```ts
// Bad: Date.now() defeats query caching and causes frequent re-evaluation
const releasedPosts = await ctx.db
.query("posts")
.withIndex("by_released_at", (q) => q.lte("releasedAt", Date.now()))
.take(100);
```
```ts
// Good: use a boolean field updated by a scheduled function
const releasedPosts = await ctx.db
.query("posts")
.withIndex("by_is_released", (q) => q.eq("isReleased", true))
.take(100);
```
If the query must compare against a time value, pass it as an explicit argument
from the client and round it to a coarse interval (e.g. the most recent minute)
so requests within that window share the same cache entry.
### 8. Consider pagination strategy
For long lists where users scroll through many pages:
- If the data does not need live updates, use point-in-time fetching with manual
"load more"
- If it does need live updates, accept the subscription cost but limit the
number of loaded pages
- Consider whether older pages can be unloaded as the user scrolls forward
### 9. Separate backend cost from UI churn
If the main problem is loading flash or UI churn when query arguments change,
stabilizing the reactive UI behavior may be better than replacing reactivity
altogether.
Treat this as a UX problem first when:
- the underlying query is already reasonably cheap
- the complaint is flicker, loading flashes, or re-render churn
- live updates are still desirable once fresh data arrives
## Verification
1. Subscription count in dashboard is lower for the affected pages
2. UI responsiveness has improved
3. React profiling shows fewer unnecessary re-renders
4. Surfaces that do not need live updates are not paying for persistent
subscriptions unnecessarily
5. Sibling pages with similar patterns were updated consistently
+370
View File
@@ -0,0 +1,370 @@
---
name: convex-quickstart
description: Creates or adds Convex to an app. Use for new Convex projects, npm create
convex@latest, frontend setup, env vars, or the first npx convex dev run.
---
# Convex Quickstart
Set up a working Convex project as fast as possible.
## When to Use
- Starting a brand new project with Convex
- Adding Convex to an existing React, Next.js, Vue, Svelte, or other app
- Scaffolding a Convex app for prototyping
## When Not to Use
- The project already has Convex installed and `convex/` exists - just start
building
- You only need to add auth to an existing Convex app - use the
`convex-setup-auth` skill
## Workflow
1. Determine the starting point: new project or existing app
2. If new project, pick a template and scaffold with `npm create convex@latest`
3. If existing app, install `convex` and wire up the provider
4. Run `npx convex dev` to connect a deployment and start the dev loop
5. Verify the setup works
## Path 1: New Project (Recommended)
Use the official scaffolding tool. It creates a complete project with the
frontend framework, Convex backend, and all config wired together.
### Pick a template
| Template | Stack |
| -------------------------- | ----------------------------------------- |
| `react-vite-shadcn` | React + Vite + Tailwind + shadcn/ui |
| `nextjs-shadcn` | Next.js App Router + Tailwind + shadcn/ui |
| `react-vite-clerk-shadcn` | React + Vite + Clerk auth + shadcn/ui |
| `nextjs-clerk` | Next.js + Clerk auth |
| `nextjs-convexauth-shadcn` | Next.js + Convex Auth + shadcn/ui |
| `nextjs-lucia-shadcn` | Next.js + Lucia auth + shadcn/ui |
| `bare` | Convex backend only, no frontend |
If the user has not specified a preference, default to `react-vite-shadcn` for
simple apps or `nextjs-shadcn` for apps that need SSR or API routes.
You can also use any GitHub repo as a template:
```bash
npm create convex@latest my-app -- -t owner/repo
npm create convex@latest my-app -- -t owner/repo#branch
```
### Scaffold the project
Always pass the project name and template flag to avoid interactive prompts:
```bash
npm create convex@latest my-app -- -t react-vite-shadcn
cd my-app
npm install
```
The scaffolding tool creates files but does not run `npm install`, so you must
run it yourself.
To scaffold in the current directory (if it is empty):
```bash
npm create convex@latest . -- -t react-vite-shadcn
npm install
```
### Start the dev loop
`npx convex dev` is a long-running watcher process that syncs backend code to a
Convex deployment on every save. It also requires authentication on first run
(browser-based OAuth). Both of these make it unsuitable for an agent to run
directly.
**Ask the user to run this themselves:**
Tell the user to run `npx convex dev` in their terminal. On first run it will
prompt them to log in or develop anonymously. Once running, it will:
- Create a Convex project and dev deployment
- Write the deployment URL to `.env.local`
- Create the `convex/` directory with generated types
- Watch for changes and sync continuously
The user should keep `npx convex dev` running in the background while you work
on code. The watcher will automatically pick up any files you create or edit in
`convex/`.
**Exception - cloud or headless agents:** Environments that cannot open a
browser for interactive login should use Agent Mode (see below) to run
anonymously without user interaction.
### Start the frontend
The user should also run the frontend dev server in a separate terminal:
```bash
npm run dev
```
Vite apps serve on `http://localhost:5173`, Next.js on `http://localhost:3000`.
### What you get
After scaffolding, the project structure looks like:
```
my-app/
convex/ # Backend functions and schema
_generated/ # Auto-generated types (check this into git)
schema.ts # Database schema (if template includes one)
src/ # Frontend code (or app/ for Next.js)
package.json
.env.local # CONVEX_URL / VITE_CONVEX_URL / NEXT_PUBLIC_CONVEX_URL
```
The template already has:
- `ConvexProvider` wired into the app root
- Correct env var names for the framework
- Tailwind and shadcn/ui ready (for shadcn templates)
- Auth provider configured (for auth templates)
Proceed to adding schema, functions, and UI.
## Path 2: Add Convex to an Existing App
Use this when the user already has a frontend project and wants to add Convex as
the backend.
### Install
```bash
npm install convex
```
### Initialize and start dev loop
Ask the user to run `npx convex dev` in their terminal. This handles login,
creates the `convex/` directory, writes the deployment URL to `.env.local`, and
starts the file watcher. See the notes in Path 1 about why the agent should not
run this directly.
### Wire up the provider
The Convex client must wrap the app at the root. The setup varies by framework.
Create the `ConvexReactClient` at module scope, not inside a component:
```tsx
// Bad: re-creates the client on every render
function App() {
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string);
return <ConvexProvider client={convex}>...</ConvexProvider>;
}
// Good: created once at module scope
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string);
function App() {
return <ConvexProvider client={convex}>...</ConvexProvider>;
}
```
#### React (Vite)
```tsx
// src/main.tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { ConvexProvider, ConvexReactClient } from "convex/react";
import App from "./App";
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string);
createRoot(document.getElementById("root")!).render(
<StrictMode>
<ConvexProvider client={convex}>
<App />
</ConvexProvider>
</StrictMode>,
);
```
#### Next.js (App Router)
```tsx
// app/ConvexClientProvider.tsx
"use client";
import { ConvexProvider, ConvexReactClient } from "convex/react";
import { ReactNode } from "react";
const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);
export function ConvexClientProvider({ children }: { children: ReactNode }) {
return <ConvexProvider client={convex}>{children}</ConvexProvider>;
}
```
```tsx
// app/layout.tsx
import { ConvexClientProvider } from "./ConvexClientProvider";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<ConvexClientProvider>{children}</ConvexClientProvider>
</body>
</html>
);
}
```
#### Other frameworks
For Vue, Svelte, React Native, TanStack Start, Remix, and others, follow the
matching quickstart guide:
- [Vue](https://docs.convex.dev/quickstart/vue)
- [Svelte](https://docs.convex.dev/quickstart/svelte)
- [React Native](https://docs.convex.dev/quickstart/react-native)
- [TanStack Start](https://docs.convex.dev/quickstart/tanstack-start)
- [Remix](https://docs.convex.dev/quickstart/remix)
- [Node.js (no frontend)](https://docs.convex.dev/quickstart/nodejs)
### Environment variables
The env var name depends on the framework:
| Framework | Variable |
| ------------ | ------------------------ |
| Vite | `VITE_CONVEX_URL` |
| Next.js | `NEXT_PUBLIC_CONVEX_URL` |
| Remix | `CONVEX_URL` |
| React Native | `EXPO_PUBLIC_CONVEX_URL` |
`npx convex dev` writes the correct variable to `.env.local` automatically.
## Agent Mode (Cloud and Headless Agents)
When running in a cloud or headless agent environment where interactive browser
login is not possible, set `CONVEX_AGENT_MODE=anonymous` to use a local
anonymous deployment.
Add `CONVEX_AGENT_MODE=anonymous` to `.env.local`, or set it inline:
```bash
CONVEX_AGENT_MODE=anonymous npx convex dev
```
This runs a local Convex backend on the VM without requiring authentication, and
avoids conflicting with the user's personal dev deployment.
## Verify the Setup
After setup, confirm everything is working:
1. The user confirms `npx convex dev` is running without errors
2. The `convex/_generated/` directory exists and has `api.ts` and `server.ts`
3. `.env.local` contains the deployment URL
## Writing Your First Function
Once the project is set up, create a schema and a query to verify the full loop
works.
`convex/schema.ts`:
```ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
tasks: defineTable({
text: v.string(),
completed: v.boolean(),
}),
});
```
`convex/tasks.ts`:
```ts
import { query, mutation } from "./_generated/server";
import { v } from "convex/values";
export const list = query({
args: {},
handler: async (ctx) => {
return await ctx.db.query("tasks").collect();
},
});
export const create = mutation({
args: { text: v.string() },
handler: async (ctx, args) => {
await ctx.db.insert("tasks", { text: args.text, completed: false });
},
});
```
Use in a React component (adjust the import path based on your file location
relative to `convex/`):
```tsx
import { useQuery, useMutation } from "convex/react";
import { api } from "../convex/_generated/api";
function Tasks() {
const tasks = useQuery(api.tasks.list);
const create = useMutation(api.tasks.create);
return (
<div>
<button onClick={() => create({ text: "New task" })}>Add</button>
{tasks?.map((t) => (
<div key={t._id}>{t.text}</div>
))}
</div>
);
}
```
## Development vs Production
Always use `npx convex dev` during development. It runs against your personal
dev deployment and syncs code on save.
When ready to ship, deploy to production:
```bash
npx convex deploy
```
This pushes to the production deployment, which is separate from dev. Do not use
`deploy` during development.
## Next Steps
- Add authentication: use the `convex-setup-auth` skill
- Design your schema: see
[Schema docs](https://docs.convex.dev/database/schemas)
- Build components: use the `convex-create-component` skill
- Plan a migration: use the `convex-migration-helper` skill
- Add file storage: see
[File Storage docs](https://docs.convex.dev/file-storage)
- Set up cron jobs: see [Scheduling docs](https://docs.convex.dev/scheduling)
## Checklist
- [ ] Determined starting point: new project or existing app
- [ ] If new project: scaffolded with `npm create convex@latest` using
appropriate template
- [ ] If existing app: installed `convex` and wired up the provider
- [ ] User has `npx convex dev` running and connected to a deployment
- [ ] `convex/_generated/` directory exists with types
- [ ] `.env.local` has the deployment URL
- [ ] Verified a basic query/mutation round-trip works
@@ -0,0 +1,12 @@
interface:
display_name: "Convex Quickstart"
short_description: "Start a new Convex app or add Convex to an existing frontend."
icon_small: "./assets/icon.svg"
icon_large: "./assets/icon.svg"
brand_color: "#F97316"
default_prompt: "Set up Convex for this project as fast as possible. First decide whether
this is a new app or an existing app, then scaffold or integrate Convex and
verify the setup works."
policy:
allow_implicit_invocation: true
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
<path stroke-linecap="round" stroke-linejoin="round" d="M15.91 11.672a.375.375 0 0 1 0 .656l-5.603 3.113a.375.375 0 0 1-.557-.328V8.887c0-.286.307-.466.557-.327l5.603 3.112Z"/>
</svg>

After

Width:  |  Height:  |  Size: 435 B

+35
View File
@@ -0,0 +1,35 @@
---
name: convex-retention
description: Use when adding or changing ClawHub Convex tables, TTL fields, cleanup crons, retention policy, auth/session cleanup, metric dedupe cleanup, or deprecated table removal
---
# Convex Retention
## Overview
ClawHub retention is code-owned. Every current Convex table must be classified in
`convex/lib/retentionPolicy.ts`, and ephemeral tables need an indexed, bounded cleanup path unless
their lifecycle is handled by usage-time validation or a documented component.
## Checklist
- Read `convex/_generated/ai/guidelines.md` and the Convex ops rules in `AGENTS.md` first.
- Add every new schema table to `RETENTION_POLICIES`; the `Record<TableNames, RetentionPolicy>` type
is the enforcement gate.
- For ephemeral tables, prefer an explicit expiration field plus index, then prune with `.withIndex()`
and `.take(...)`.
- For new generic TTL tables, prefer `expirationTime` to match Convex Auth. Keep existing `expiresAt`,
`dayStart`, and `processedAt` fields unless that table already needs a real migration.
- Use `RETENTION_STANDARD_BATCH_SIZE` for ordinary retention jobs. Keep incident-tested special cases,
such as `skillStatEvents`, on their documented caps.
- Cron jobs should schedule bounded cleanup entrypoints only. Large one-off production migrations or
destructive backfills still start with `convex-migration-helper`.
- Do not bulk-clear active auth state. Expired `authSessions` and `authRefreshTokens` are pruned by
`convex/retention.ts`.
## Verification
- Add or update focused tests for policy classification and cleanup behavior.
- Run the focused Vitest slice for touched cleanup modules.
- Run `bunx convex codegen` after schema/API changes.
- Run a real Convex runtime check such as `bunx convex dev --once --typecheck=disable`.
+184
View File
@@ -0,0 +1,184 @@
---
name: convex-setup-auth
description: Sets up Convex auth, identity mapping, and access control. Use for login, auth
providers, users tables, protected functions, or roles in a Convex app.
---
# Convex Authentication Setup
Implement secure authentication in Convex with user management and access
control.
## When to Use
- Setting up authentication for the first time
- Implementing user management (users table, identity mapping)
- Creating authentication helper functions
- Setting up auth providers (Convex Auth, Clerk, WorkOS AuthKit, Auth0, custom
JWT)
## When Not to Use
- Auth for a non-Convex backend
- Pure OAuth/OIDC documentation without a Convex implementation
- Debugging unrelated bugs that happen to surface near auth code
- The auth provider is already fully configured and the user only needs a
one-line fix
## First Step: Choose the Auth Provider
Convex supports multiple authentication approaches. Do not assume a provider.
Before writing setup code:
1. Ask the user which auth solution they want, unless the repository already
makes it obvious
2. If the repo already uses a provider, continue with that provider unless the
user wants to switch
3. If the user has not chosen a provider and the repo does not make it obvious,
ask before proceeding
Common options:
- [Convex Auth](https://docs.convex.dev/auth/convex-auth) - good default when
the user wants auth handled directly in Convex
- [Clerk](https://docs.convex.dev/auth/clerk) - use when the app already uses
Clerk or the user wants Clerk's hosted auth features
- [WorkOS AuthKit](https://docs.convex.dev/auth/authkit/) - use when the app
already uses WorkOS or the user wants AuthKit specifically
- [Auth0](https://docs.convex.dev/auth/auth0) - use when the app already uses
Auth0
- Custom JWT provider - use when integrating an existing auth system not covered
above
Look for signals in the repo before asking:
- Dependencies such as `@clerk/*`, `@workos-inc/*`, `@auth0/*`, or Convex Auth
packages
- Existing files such as `convex/auth.config.ts`, auth middleware, provider
wrappers, or login components
- Environment variables that clearly point at a provider
## After Choosing a Provider
Read the provider's official guide and the matching local reference file:
- Convex Auth: [official docs](https://docs.convex.dev/auth/convex-auth), then
`references/convex-auth.md`
- Clerk: [official docs](https://docs.convex.dev/auth/clerk), then
`references/clerk.md`
- WorkOS AuthKit: [official docs](https://docs.convex.dev/auth/authkit/), then
`references/workos-authkit.md`
- Auth0: [official docs](https://docs.convex.dev/auth/auth0), then
`references/auth0.md`
The local reference files contain the concrete workflow, expected files and env
vars, gotchas, and validation checks.
Use those sources for:
- package installation
- client provider wiring
- environment variables
- `convex/auth.config.ts` setup
- login and logout UI patterns
- framework-specific setup for React, Vite, or Next.js
For shared auth behavior, use the official Convex docs as the source of truth:
- [Auth in Functions](https://docs.convex.dev/auth/functions-auth) for
`ctx.auth.getUserIdentity()`
- [Storing Users in the Convex Database](https://docs.convex.dev/auth/database-auth)
for optional app-level user storage
- [Authentication](https://docs.convex.dev/auth) for general auth and
authorization guidance
- [Convex Auth Authorization](https://labs.convex.dev/auth/authz) when the
provider is Convex Auth
Prefer official docs over recalled steps, because provider CLIs and Convex Auth
internals change between versions. Inventing setup from memory risks outdated
patterns. For third-party providers, only add app-level user storage if the app
actually needs user documents in Convex. Not every app needs a `users` table.
For Convex Auth, follow the Convex Auth docs and built-in auth tables rather
than adding a parallel `users` table plus `storeUser` flow, because Convex Auth
already manages user records internally. After running provider initialization
commands, verify generated files and complete the post-init wiring steps the
provider reference calls out. Initialization commands rarely finish the entire
integration.
## Core Pattern: Protecting Backend Functions
The most common auth task is checking identity in Convex functions.
```ts
// Bad: trusting a client-provided userId
export const getMyProfile = query({
args: { userId: v.id("users") },
handler: async (ctx, args) => {
return await ctx.db.get(args.userId);
},
});
```
```ts
// Good: verifying identity server-side
export const getMyProfile = query({
args: {},
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) throw new Error("Not authenticated");
return await ctx.db
.query("users")
.withIndex("by_tokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier))
.unique();
},
});
```
## Workflow
1. Determine the provider, either by asking the user or inferring from the repo
2. Ask whether the user wants local-only setup or production-ready setup now
3. Read the matching provider reference file
4. Follow the official provider docs for current setup details
5. Follow the official Convex docs for shared backend auth behavior, user
storage, and authorization patterns
6. Only add app-level user storage if the docs and app requirements call for it
7. Add authorization checks for ownership, roles, or team access only where the
app needs them
8. Verify login state, protected queries, environment variables, and production
configuration if requested
If the flow blocks on interactive provider or deployment setup, ask the user
explicitly for the exact human step needed, then continue after they complete
it. For UI-facing auth flows, offer to validate the real sign-up or sign-in flow
after setup is done. If the environment has browser automation tools, you can
use them. If it does not, give the user a short manual validation checklist
instead.
## Reference Files
### Provider References
- `references/convex-auth.md`
- `references/clerk.md`
- `references/workos-authkit.md`
- `references/auth0.md`
## Checklist
- [ ] Chosen the correct auth provider before writing setup code
- [ ] Read the relevant provider reference file
- [ ] Asked whether the user wants local-only setup or production-ready setup
- [ ] Used the official provider docs for provider-specific wiring
- [ ] Used the official Convex docs for shared auth behavior and authorization
patterns
- [ ] Only added app-level user storage if the app actually needs it
- [ ] Did not invent a cross-provider `users` table or `storeUser` flow for
Convex Auth
- [ ] Added authentication checks in protected backend functions
- [ ] Added authorization checks where the app actually needs them
- [ ] Clear error messages ("Not authenticated", "Unauthorized")
- [ ] Client auth provider configured for the chosen provider
- [ ] If requested, production auth setup is covered too
@@ -0,0 +1,12 @@
interface:
display_name: "Convex Setup Auth"
short_description: "Set up Convex auth, user identity mapping, and access control."
icon_small: "./assets/icon.svg"
icon_large: "./assets/icon.svg"
brand_color: "#2563EB"
default_prompt: "Set up authentication for this Convex app. Figure out the provider first,
then wire up the user model, identity mapping, and access control with the
smallest solid implementation."
policy:
allow_implicit_invocation: true
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 1 0-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 0 0 2.25-2.25v-6.75a2.25 2.25 0 0 0-2.25-2.25H6.75a2.25 2.25 0 0 0-2.25 2.25v6.75a2.25 2.25 0 0 0 2.25 2.25Z"/>
</svg>

After

Width:  |  Height:  |  Size: 394 B

@@ -0,0 +1,156 @@
# Auth0
Official docs:
- https://docs.convex.dev/auth/auth0
- https://auth0.github.io/auth0-cli/
- https://auth0.github.io/auth0-cli/auth0_apps_create.html
Use this when the app already uses Auth0 or the user wants Auth0 specifically.
## Workflow
1. Confirm the user wants Auth0
2. Determine the app framework and whether Auth0 is already partly set up
3. Ask whether the user wants local-only setup or production-ready setup now
4. Read the official Convex and Auth0 guides before making changes
5. Ask whether they want the fastest setup path by installing the Auth0 CLI
6. If they agree, install the Auth0 CLI and do as much of the Auth0 app setup as
possible through the CLI
7. If they do not want the CLI path, use the Auth0 dashboard path instead
8. Complete the relevant Auth0 frontend quickstart if the app does not already
have Auth0 wired up
9. Configure `convex/auth.config.ts` with the Auth0 domain and client ID
10. Set environment variables for local and production environments
11. Wrap the app with `Auth0Provider` and `ConvexProviderWithAuth0`
12. Gate Convex-backed UI with Convex auth state
13. Try to verify Convex reports the user as authenticated after Auth0 login
14. If the refresh-token path fails, stop improvising and send the user back to
the official docs
15. If the user wants production-ready setup, make sure the production Auth0
tenant and env vars are also covered
## What To Do
- Read the official Convex and Auth0 guide before writing setup code
- Prefer the Auth0 CLI path for mechanical setup if the user is willing to
install it, but do not present it as a fully validated end-to-end path yet
- Ask the user directly: "The fastest path is to install the Auth0 CLI so I can
do more of this for you. If you want, I can install it and then only ask you
to log in when needed. Would you like me to do that?"
- Make sure the app has already completed the relevant Auth0 quickstart for its
frontend
- Use the official examples for `Auth0Provider` and `ConvexProviderWithAuth0`
- If the Auth0 login or refresh flow starts failing in a way that is not clearly
explained by the docs, say that plainly and fall back to the official docs
instead of pretending the flow is validated
## Key Setup Areas
- install the Auth0 SDK for the app's framework
- configure `convex/auth.config.ts` with the Auth0 domain and client ID
- set environment variables for local and production environments
- wrap the app with `Auth0Provider` and `ConvexProviderWithAuth0`
- use Convex auth state when gating Convex-backed UI
## Files and Env Vars To Expect
- `convex/auth.config.ts`
- frontend app entry or provider wrapper
- Auth0 CLI install docs: `https://auth0.github.io/auth0-cli/`
- Auth0 environment variables commonly include:
- `AUTH0_DOMAIN`
- `AUTH0_CLIENT_ID`
- `VITE_AUTH0_DOMAIN`
- `VITE_AUTH0_CLIENT_ID`
## Concrete Steps
1. Start by reading `https://docs.convex.dev/auth/auth0` and the relevant Auth0
quickstart for the app's framework
2. Ask whether the user wants the Auth0 CLI path
3. If yes, install Auth0 CLI and have the user authenticate it with
`auth0 login`
4. Use `auth0 apps create` with SPA settings, callback URL, logout URL, and web
origins if creating a new app
5. If not using the CLI path, complete the relevant Auth0 frontend quickstart
and create the Auth0 app in the dashboard
6. Get the Auth0 domain and client ID from the CLI output or the Auth0 dashboard
7. Install the Auth0 SDK for the app's framework
8. Create or update `convex/auth.config.ts` with the Auth0 domain and client ID
9. Set frontend and backend environment variables
10. Wrap the app in `Auth0Provider`
11. Replace plain `ConvexProvider` wiring with `ConvexProviderWithAuth0`
12. Run the normal Convex dev or deploy flow after backend config changes
13. Try the official provider config shown in the Convex docs
14. If login works but Convex auth or token refresh fails in a way you cannot
clearly resolve, stop and tell the user to follow the official docs manually
for now
15. Only claim success if the user can sign in and Convex recognizes the
authenticated session
16. If the user wants production-ready setup, configure the production Auth0
tenant values and production environment variables too
## Gotchas
- The Convex docs assume the Auth0 side is already set up, so do not skip the
Auth0 quickstart if the app is starting from scratch
- The Auth0 CLI is often the fastest path for a fresh setup, but it still
requires the user to authenticate the CLI to their Auth0 tenant
- If the user agrees to install the Auth0 CLI, do the mechanical setup yourself
instead of bouncing them through the dashboard
- If login succeeds but Convex still reports unauthenticated, double-check
`convex/auth.config.ts` and whether the backend config was synced
- We were able to automate Auth0 app creation and Convex config wiring, but we
did not fully validate the refresh-token path end to end
- In validation, the documented `useRefreshTokens={true}` and
`cacheLocation="localstorage"` setup hit refresh-token failures, so do not
present that path as settled
- If you hit Auth0 errors like `Unknown or invalid refresh token`, do not keep
inventing fixes indefinitely, send the user back to the official docs and
explain that this path is still under investigation
- Keep dev and prod tenants separate if the project uses different Auth0
environments
- Do not confuse "Auth0 login works" with "Convex can validate the Auth0 token".
Both need to work.
- If the repo already uses Auth0, preserve existing redirect and tenant
configuration unless the user asked to change it.
- Do not assume the local Auth0 tenant settings match production. Verify the
production domain, client ID, and callback URLs separately.
- For local dev, make sure the Auth0 app settings match the app's real local
port for callback URLs, logout URLs, and web origins
## Production
- Ask whether the user wants dev-only setup or production-ready setup
- If the answer is production-ready, make sure the production Auth0 tenant
values, callback URLs, and Convex deployment config are all covered
- Verify production environment variables and redirect settings before calling
the task complete
- Do not silently write a notes file into the repo by default. If the user wants
rollout or handoff docs, create one explicitly.
## Validation
- Verify the user can complete the Auth0 login flow
- Verify Convex-authenticated UI renders only after Convex auth state is ready
- Verify protected Convex queries succeed after login
- Verify `ctx.auth.getUserIdentity()` is non-null in protected backend functions
- Verify the Auth0 app settings match the real local callback and logout URLs
during development
- If the Auth0 refresh-token path fails, mark the setup as not fully validated
and direct the user to the official docs instead of claiming the skill
completed successfully
- If production-ready setup was requested, verify the production Auth0
configuration is also covered
## Checklist
- [ ] Confirm the user wants Auth0
- [ ] Ask whether the user wants local-only setup or production-ready setup
- [ ] Complete the relevant Auth0 frontend setup
- [ ] Configure `convex/auth.config.ts`
- [ ] Set environment variables
- [ ] Verify Convex authenticated state after login, or explicitly tell the user
this path is still under investigation and send them to the official docs
- [ ] If requested, configure the production deployment too
@@ -0,0 +1,141 @@
# Clerk
Official docs:
- https://docs.convex.dev/auth/clerk
- https://clerk.com/docs/guides/development/integrations/databases/convex
Use this when the app already uses Clerk or the user wants Clerk's hosted auth
features.
## Workflow
1. Confirm the user wants Clerk
2. Make sure the user has a Clerk account and a Clerk application
3. Determine the app framework:
- React
- Next.js
- TanStack Start
4. Ask whether the user wants local-only setup or production-ready setup now
5. Gather the Clerk keys and the Clerk Frontend API URL
6. Follow the correct framework section in the official docs
7. Complete the backend and client wiring
8. Verify Convex reports the user as authenticated after login
9. If the user wants production-ready setup, make sure the production Clerk
config is also covered
## What To Do
- Read the official Convex and Clerk guide before writing setup code
- If the user does not already have Clerk set up, send them to
`https://dashboard.clerk.com/sign-up` to create an account and
`https://dashboard.clerk.com/apps/new` to create an application
- Send the user to `https://dashboard.clerk.com/apps/setup/convex` if the Convex
integration is not already active
- Match the guide to the app's framework, usually React, Next.js, or TanStack
Start
- Use the official examples for `ConvexProviderWithClerk`, `ClerkProvider`, and
`useAuth`
## Key Setup Areas
- install the Clerk SDK for the framework in use
- configure `convex/auth.config.ts` with the Clerk issuer domain
- set the required Clerk environment variables
- wrap the app with `ClerkProvider` and `ConvexProviderWithClerk`
- use Convex auth-aware UI patterns such as `Authenticated`, `Unauthenticated`,
and `AuthLoading`
## Files and Env Vars To Expect
- `convex/auth.config.ts`
- React or Vite client entry such as `src/main.tsx`
- Next.js client wrapper for Convex if using App Router
- Clerk account sign-up page: `https://dashboard.clerk.com/sign-up`
- Clerk app creation page: `https://dashboard.clerk.com/apps/new`
- Clerk Convex integration page: `https://dashboard.clerk.com/apps/setup/convex`
- Clerk API keys page: `https://dashboard.clerk.com/last-active?path=api-keys`
- Clerk environment variables:
- `CLERK_JWT_ISSUER_DOMAIN` for Convex backend validation in the Convex docs
- `CLERK_FRONTEND_API_URL` in the Clerk docs
- `VITE_CLERK_PUBLISHABLE_KEY` for Vite apps
- `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` for Next.js apps
- `CLERK_SECRET_KEY` for Next.js server-side Clerk setup where required
`CLERK_JWT_ISSUER_DOMAIN` and `CLERK_FRONTEND_API_URL` refer to the same Clerk
Frontend API URL value. Do not treat them as two different URLs.
## Concrete Steps
1. If needed, create a Clerk account at `https://dashboard.clerk.com/sign-up`
2. If needed, create a Clerk application at
`https://dashboard.clerk.com/apps/new`
3. Open `https://dashboard.clerk.com/last-active?path=api-keys` and copy the
publishable key, plus the secret key for Next.js where needed
4. Open `https://dashboard.clerk.com/apps/setup/convex`
5. Activate the Convex integration in Clerk if it is not already active
6. Copy the Clerk Frontend API URL shown there
7. Install the Clerk package for the app's framework
8. Create or update `convex/auth.config.ts` so Convex validates Clerk tokens
9. Set the publishable key in the frontend environment
10. Set the issuer domain or Frontend API URL so Convex can validate the JWT
11. Replace plain `ConvexProvider` wiring with `ConvexProviderWithClerk`
12. Wrap the app in `ClerkProvider`
13. Use Convex auth helpers for authenticated rendering
14. Run the normal Convex dev or deploy flow after updating backend auth config
15. If the user wants production-ready setup, configure the production Clerk
values and production issuer domain too
## Gotchas
- Prefer `useConvexAuth()` over raw Clerk auth state when deciding whether
Convex-authenticated UI can render
- For Next.js, keep server and client boundaries in mind when creating the
Convex provider wrapper
- After changing `convex/auth.config.ts`, run the normal Convex dev or deploy
flow so the backend picks up the new config
- Do not stop at "Clerk login works". The important check is that Convex also
sees the session and can authenticate requests.
- If the repo already uses Clerk, preserve its existing auth flow unless the
user asked to change it.
- Do not assume the same Clerk values work for both dev and production. Check
the production issuer domain and publishable key separately.
- The Convex setup page is where you get the Clerk Frontend API URL for Convex.
Keep using the Clerk API keys page for the publishable key and the secret key.
- If Convex says no auth provider matched the token, first confirm the Clerk
Convex integration was activated at
`https://dashboard.clerk.com/apps/setup/convex`
- After activating the Clerk Convex integration, sign out completely and sign
back in before retesting. An old Clerk session can keep using a token that
Convex rejects.
## Production
- Ask whether the user wants dev-only setup or production-ready setup
- If the answer is production-ready, make sure production Clerk keys and issuer
configuration are included
- Verify production redirect URLs and any production Clerk domain values before
calling the task complete
- Do not silently write a notes file into the repo by default. If the user wants
rollout or handoff docs, create one explicitly.
## Validation
- Verify the user can sign in with Clerk
- If the Clerk integration was just activated, verify after a full Clerk
sign-out and fresh sign-in
- Verify `useConvexAuth()` reaches the authenticated state after Clerk login
- Verify protected Convex queries run successfully inside authenticated UI
- Verify `ctx.auth.getUserIdentity()` is non-null in protected backend functions
- If production-ready setup was requested, verify the production Clerk
configuration is also covered
## Checklist
- [ ] Confirm the user wants Clerk
- [ ] Ask whether the user wants local-only setup or production-ready setup
- [ ] Follow the correct framework section in the official guide
- [ ] Set Clerk environment variables
- [ ] Configure `convex/auth.config.ts`
- [ ] Verify Convex authenticated state after login
- [ ] If requested, configure the production deployment too
@@ -0,0 +1,188 @@
# Convex Auth
Official docs: https://docs.convex.dev/auth/convex-auth Setup guide:
https://labs.convex.dev/auth/setup
Use this when the user wants auth handled directly in Convex rather than through
a third-party provider.
## Workflow
1. Confirm the user wants Convex Auth specifically
2. Determine which sign-in methods the app needs:
- magic links or OTPs
- OAuth providers
- passwords and password reset
3. Ask whether the user wants local-only setup or production-ready setup now
4. Read the Convex Auth setup guide before writing code
5. Make sure the project has a configured Convex deployment:
- run `npx convex dev` first if `CONVEX_DEPLOYMENT` is not set
- if CLI configuration requires interactive human input, stop and ask the
user to complete that step before continuing
6. Install the auth packages:
- `npm install @convex-dev/auth @auth/core@0.37.0`
7. Run the initialization command:
- `npx @convex-dev/auth`
8. Confirm the initializer created:
- `convex/auth.config.ts`
- `convex/auth.ts`
- `convex/http.ts`
9. Add the required `authTables` to `convex/schema.ts`
10. Replace plain `ConvexProvider` wiring with `ConvexAuthProvider`
11. Configure at least one auth method in `convex/auth.ts`
12. Run `npx convex dev --once` or the normal dev flow to push the updated
schema and generated code
13. Verify the client can sign in successfully
14. Verify Convex receives authenticated identity in backend functions
15. If the user wants production-ready setup, make sure the same auth setup is
configured for the production deployment as well
16. Only add a `users` table and `storeUser` flow if the app needs app-level
user records inside Convex
## What This Reference Is For
- choosing Convex Auth as the default provider for a new Convex app
- understanding whether the app wants magic links, OTPs, OAuth, or passwords
- keeping the setup provider-specific while using the official Convex Auth docs
for identity and authorization behavior
## What To Do
- Read the Convex Auth setup guide before writing setup code
- Follow the setup flow from the docs rather than recreating it from memory
- If the app is new, consider starting from the official starter flow instead of
hand-wiring everything
- Treat `npx @convex-dev/auth` as a required initialization step for existing
apps, not an optional extra
## Concrete Steps
1. Install `@convex-dev/auth` and `@auth/core@0.37.0`
2. Run `npx convex dev` if the project does not already have a configured
deployment
3. If `npx convex dev` blocks on interactive setup, ask the user explicitly to
finish configuring the Convex deployment
4. Run `npx @convex-dev/auth`
5. Confirm the generated auth setup is present before continuing:
- `convex/auth.config.ts`
- `convex/auth.ts`
- `convex/http.ts`
6. Add `authTables` to `convex/schema.ts`
7. Replace `ConvexProvider` with `ConvexAuthProvider` in the app entry
8. Configure the selected auth methods in `convex/auth.ts`
9. Run `npx convex dev --once` or the normal dev flow so the updated schema and
auth files are pushed
10. Verify login locally
11. If the user wants production-ready setup, repeat the required auth
configuration against the production deployment
## Expected Files and Decisions
- `convex/schema.ts`
- frontend app entry such as `src/main.tsx` or the framework-equivalent provider
file
- generated Convex Auth setup produced by `npx @convex-dev/auth`
- an existing configured Convex deployment, or the ability to create one with
`npx convex dev`
- `convex/auth.ts` starts with `providers: []` until the app configures actual
sign-in methods
- Decide whether the user is creating a new app or adding auth to an existing
app
- For a new app, prefer the official starter flow instead of rebuilding setup by
hand
- Decide which auth methods the app needs:
- magic links or OTPs
- OAuth providers
- passwords
- Decide whether the user wants local-only setup or production-ready setup now
- Decide whether the app actually needs a `users` table inside Convex, or
whether provider identity alone is enough
## Gotchas
- Do not assume a specific sign-in method. Ask which methods the app needs
before wiring UI and backend behavior.
- `npx @convex-dev/auth` is important because it initializes the auth setup,
including the key material. Do not skip it when adding Convex Auth to an
existing project.
- `npx @convex-dev/auth` will fail if the project does not already have a
configured `CONVEX_DEPLOYMENT`.
- `npx convex dev` may require interactive setup for deployment creation or
project selection. If that happens, ask the user explicitly for that human
step instead of guessing.
- `npx @convex-dev/auth` does not finish the whole integration by itself. You
still need to add `authTables`, swap in `ConvexAuthProvider`, and configure at
least one auth method.
- A project can still build even if `convex/auth.ts` still has `providers: []`,
so do not treat a successful build as proof that sign-in is fully configured.
- Convex Auth does not mean every app needs a `users` table. If the app only
needs authentication gates, `ctx.auth.getUserIdentity()` may be enough.
- If the app is greenfield, starting from the official starter flow is usually
better than partially recreating it by hand.
- Do not stop at local dev setup if the user expects production-ready auth. The
production deployment needs the auth setup too.
- Keep provider-specific setup and Convex Auth authorization behavior in the
official docs instead of inventing shared patterns from memory.
## Production
- Ask whether the user wants dev-only setup or production-ready setup
- If the answer is production-ready, make sure the auth configuration is applied
to the production deployment, not just the dev deployment
- Verify production-specific redirect URLs, auth method configuration, and
deployment settings before calling the task complete
- Do not silently write a notes file into the repo by default. If the user wants
rollout or handoff docs, create one explicitly.
## Human Handoff
If `npx convex dev` or deployment setup requires human input:
- stop and explain exactly what the user needs to do
- say why that step is required
- resume the auth setup immediately after the user confirms it is done
## Validation
- Verify the user can complete a sign-in flow
- Offer to validate sign up, sign out, and sign back in with the configured auth
method
- If browser automation is available in the environment, you can do this
directly
- If browser automation is not available, give the user a short manual
validation checklist instead
- Verify `ctx.auth.getUserIdentity()` returns an identity in protected backend
functions
- Verify protected UI only renders after Convex-authenticated state is ready
- Verify environment variables and redirect settings match the current app
environment
- Verify `convex/auth.ts` no longer has an empty `providers: []` configuration
once the app is meant to support real sign-in
- Run `npx convex dev --once` or the normal dev flow after setup changes and
confirm Convex codegen and push succeed
- If production-ready setup was requested, verify the production deployment is
also configured correctly
## Checklist
- [ ] Confirm the user wants Convex Auth specifically
- [ ] Ask whether the user wants local-only setup or production-ready setup
- [ ] Ensure a Convex deployment is configured before running auth
initialization
- [ ] Install `@convex-dev/auth` and `@auth/core@0.37.0`
- [ ] Run `npx convex dev` first if needed
- [ ] Run `npx @convex-dev/auth`
- [ ] Confirm `convex/auth.config.ts`, `convex/auth.ts`, and `convex/http.ts`
were created
- [ ] Follow the setup guide for package install and wiring
- [ ] Add `authTables` to `convex/schema.ts`
- [ ] Replace `ConvexProvider` with `ConvexAuthProvider`
- [ ] Configure at least one auth method in `convex/auth.ts`
- [ ] Run `npx convex dev --once` or the normal dev flow after setup changes
- [ ] Confirm which sign-in methods the app needs
- [ ] Verify the client can sign in and the backend receives authenticated
identity
- [ ] Offer end-to-end validation of sign up, sign out, and sign back in
- [ ] If requested, configure the production deployment too
- [ ] Only add extra `users` table sync if the app needs app-level user records
@@ -0,0 +1,147 @@
# WorkOS AuthKit
Official docs:
- https://docs.convex.dev/auth/authkit/
- https://docs.convex.dev/auth/authkit/add-to-app
- https://docs.convex.dev/auth/authkit/auto-provision
Use this when the app already uses WorkOS or the user wants AuthKit
specifically.
## Workflow
1. Confirm the user wants WorkOS AuthKit
2. Determine whether they want:
- a Convex-managed WorkOS team
- an existing WorkOS team
3. Ask whether the user wants local-only setup or production-ready setup now
4. Read the official Convex and WorkOS AuthKit guide
5. Create or update `convex.json` for the app's framework and real local port
6. Follow the correct branch of the setup flow based on that choice
7. Configure the required WorkOS environment variables
8. Configure `convex/auth.config.ts` for WorkOS-issued JWTs
9. Wire the client provider and callback flow
10. Verify authenticated requests reach Convex
11. If the user wants production-ready setup, make sure the production WorkOS
configuration is covered too
12. Only add `storeUser` or a `users` table if the app needs first-class user
rows inside Convex
## What To Do
- Read the official Convex and WorkOS AuthKit guide before writing setup code
- Determine whether the user wants a Convex-managed WorkOS team or an existing
WorkOS team
- Treat `convex.json` as a first-class part of the AuthKit setup, not an
optional extra
- Follow the current setup flow from the docs instead of relying on older
examples
## Key Setup Areas
- package installation for the app's framework
- `convex.json` with the `authKit` section for dev, and preview or prod if
needed
- environment variables such as `WORKOS_CLIENT_ID`, `WORKOS_API_KEY`, and
redirect configuration
- `convex/auth.config.ts` wiring for WorkOS-issued JWTs
- client provider setup and token flow into Convex
- login callback and redirect configuration
## Files and Env Vars To Expect
- `convex.json`
- `convex/auth.config.ts`
- frontend auth provider wiring
- callback or redirect route setup where the framework requires it
- WorkOS environment variables commonly include:
- `WORKOS_CLIENT_ID`
- `WORKOS_API_KEY`
- `WORKOS_COOKIE_PASSWORD`
- `VITE_WORKOS_CLIENT_ID`
- `VITE_WORKOS_REDIRECT_URI`
- `NEXT_PUBLIC_WORKOS_REDIRECT_URI`
For a managed WorkOS team, `convex dev` can provision the AuthKit environment
and write local env vars such as `VITE_WORKOS_CLIENT_ID` and
`VITE_WORKOS_REDIRECT_URI` into `.env.local` for Vite apps.
## Concrete Steps
1. Choose Convex-managed or existing WorkOS team
2. Create or update `convex.json` with the `authKit` section for the framework
in use
3. Make sure the dev `redirectUris`, `appHomepageUrl`, `corsOrigins`, and local
redirect env vars match the app's actual local port
4. For a managed WorkOS team, run `npx convex dev` and follow the interactive
onboarding flow
5. For an existing WorkOS team, get `WORKOS_CLIENT_ID` and `WORKOS_API_KEY` from
the WorkOS dashboard and set them with `npx convex env set`
6. Create or update `convex/auth.config.ts` for WorkOS JWT validation
7. Run the normal Convex dev or deploy flow so backend config is synced
8. Wire the WorkOS client provider in the app
9. Configure callback and redirect handling
10. Verify the user can sign in and return to the app
11. Verify Convex sees the authenticated user after login
12. If the user wants production-ready setup, configure the production client
ID, API key, redirect URI, and deployment settings too
## Gotchas
- The docs split setup between Convex-managed and existing WorkOS teams, so ask
which path the user wants if it is not obvious
- Keep dev and prod WorkOS configuration separate where the docs call for
different client IDs or API keys
- Only add `storeUser` or a `users` table if the app needs first-class user rows
inside Convex
- Do not mix dev and prod WorkOS credentials or redirect URIs
- If the repo already contains WorkOS setup, preserve the current tenant model
unless the user wants to change it
- For managed WorkOS setup, `convex dev` is interactive the first time. In
non-interactive terminals, stop and ask the user to complete the onboarding
prompts.
- `convex.json` is not optional for the managed AuthKit flow. It drives redirect
URI, homepage URL, CORS configuration, and local env var generation.
- If the frontend starts on a different port than the one in `convex.json`, the
hosted WorkOS sign-in flow will point to the wrong callback URL. Update
`convex.json`, update the local redirect env var, and run `npx convex dev`
again.
- Vite can fall off `5173` if other apps are already running. Do not assume the
default port still matches the generated AuthKit config.
- A successful WorkOS sign-in should redirect back to the local callback route
and then reach a Convex-authenticated state. Do not stop at "the hosted WorkOS
page loaded."
## Production
- Ask whether the user wants dev-only setup or production-ready setup
- If the answer is production-ready, make sure the production WorkOS client ID,
API key, redirect URI, and Convex deployment config are all covered
- Verify the production redirect and callback settings before calling the task
complete
- Do not silently write a notes file into the repo by default. If the user wants
rollout or handoff docs, create one explicitly.
## Validation
- Verify the user can complete the login flow and return to the app
- Verify the callback URL matches the real frontend port in local dev
- Verify Convex receives authenticated requests after login
- Verify `convex.json` matches the framework and chosen WorkOS setup path
- Verify `convex/auth.config.ts` matches the chosen WorkOS setup path
- Verify environment variables differ correctly between local and production
where needed
- If production-ready setup was requested, verify the production WorkOS
configuration is also covered
## Checklist
- [ ] Confirm the user wants WorkOS AuthKit
- [ ] Ask whether the user wants local-only setup or production-ready setup
- [ ] Choose Convex-managed or existing WorkOS team
- [ ] Create or update `convex.json`
- [ ] Configure WorkOS environment variables
- [ ] Configure `convex/auth.config.ts`
- [ ] Verify authenticated requests reach Convex after login
- [ ] If requested, configure the production deployment too
+75
View File
@@ -0,0 +1,75 @@
---
name: convex
description: Routes general Convex requests to the right project skill. Use when the user
asks which Convex skill to use or gives an underspecified Convex app task.
---
# Convex
Use this as the routing skill for Convex work in this repo.
If a more specific Convex skill clearly matches the request, use that instead.
## Start Here
If the project does not already have Convex AI guidance installed, or the
existing guidance looks stale, strongly recommend installing it first.
Preferred:
```bash
npx convex ai-files install
```
This installs or refreshes the managed Convex AI files. It is the recommended
starting point for getting the official Convex guidelines in place and following
the current Convex AI setup described in the docs:
- [Convex AI docs](https://docs.convex.dev/ai)
Simple fallback:
- [convex_rules.txt](https://convex.link/convex_rules.txt)
Prefer `npx convex ai-files install` over copying rules by hand when possible.
## Command Preflight
Before running any `bunx convex ...` command in ClawHub, explicitly identify:
- target runtime: `local`, `dev`, or `prod`
- deployment: exact name or URL when known, such as `wry-manatee-359` for prod
- code state: whether the function/schema changes have already been pushed with
`bunx convex dev --once`, `bunx convex deploy`, or the production deploy
workflow
Use the current Convex CLI flag shape:
- read data: `bunx convex data --deployment <deployment> <table>`
- run a function: `bunx convex run --deployment <deployment> <function> '<json>'`
- readonly inline query:
`bunx convex run --deployment <deployment> --inline-query '<query>'`
- single-table import:
`bunx convex import --deployment <deployment> --table <table> --replace -y <file>`
If `--env-file .env.local` produces `401 MissingAccessToken`, omit the env file
and target the deployment directly with `--deployment <deployment>` or `--prod`.
Do not use stale `--deployment-name` guidance.
## Route to the Right Skill
After that, use the most specific Convex skill for the task:
- New project or adding Convex to an app: `convex-quickstart`
- Authentication setup: `convex-setup-auth`
- Building a reusable Convex component: `convex-create-component`
- Planning or running a migration: `convex-migration-helper`
- Investigating performance issues: `convex-performance-audit`
If one of those clearly matches the user's goal, switch to it instead of staying
in this skill.
## When Not to Use
- The user has already named a more specific Convex workflow
- Another Convex skill obviously fits the request better
@@ -0,0 +1,194 @@
---
name: create-and-cleanup-migration
description: Use for end-to-end ClawHub Convex production migrations, backfills, destructive cleanups, and one-off maintenance functions that must be created, validated, shipped, run, verified, then removed after completion.
---
# Create And Cleanup Migration
Drive a ClawHub Convex migration from implementation through production cleanup,
with explicit operator gates before destructive execution and before removing the
temporary migration code.
## When To Use
- A Convex production data migration, backfill, destructive cleanup, schema
narrowing, table reshaping, or one-off maintenance function is needed.
- Temporary Convex code must be created, deployed, run, verified, and then
removed after it is no longer useful.
- The user asks for the full lifecycle: implement migration, PR, deploy, dry run,
apply, verify, cleanup PR, deploy cleanup.
## Required Companion Guidance
1. Start with `convex-migration-helper`.
2. Read `convex/_generated/ai/guidelines.md` before editing Convex code.
3. Default to `@convex-dev/migrations` for production data changes.
4. If not using `@convex-dev/migrations`, write down why the component is
unnecessary and provide equivalent:
- dry-run support
- cursor batching
- resumable/progress behavior
- destructive confirmation token
- real Convex runtime validation
## Safety Rules
- Never run a destructive production apply step until after presenting dry-run
results and receiving explicit user confirmation in the current thread.
- Before implementing anything, classify the requested "migration" as one of:
code deploy, existing Convex function run, operator import/export command,
schema narrowing, data cleanup, or cleanup-code removal. Do not invent a new
Convex migration function when the issue or PR specifies an operator command
such as `convex import --replace`.
- Before any production migration apply, force the operator to visit
`https://dashboard.convex.dev/`, manually click **Backup Now** on the target
deployment, wait for completion, and explicitly confirm in the thread. Do not
automate dashboard backup creation.
- Never remove migration code until after presenting apply/verification results
and receiving explicit user confirmation in the current thread.
- Keep production commands pointed at the explicit deployment name when known;
do not rely on generic `--prod` if this repo's guidance says to verify the
actual deployment.
- If the migration can affect visibility, moderation, ownership, billing,
installability, or public API output, call that out before the apply gate.
- Preserve resume cursors, run IDs, PR URLs, deploy URLs, and final stats in the
handoff.
## Phase 1: Design The Migration
1. Identify the intended data change and whether it is:
- schema widen/migrate/narrow
- field cleanup
- table cleanup
- ownership/relationship repair
- recurring maintenance
2. Choose the implementation:
- Prefer `@convex-dev/migrations` for non-trivial production data.
- Use a hand-rolled internal function only for a clearly small or special
case, and document the exception.
3. Define done criteria:
- dry-run expected counts
- apply expected counts
- verification query/result proving no remaining targets
- cleanup PR scope
## Phase 2: Implement
1. Add or update the Convex migration/maintenance code.
2. Include argument validators for every Convex function.
3. Include dry-run support.
4. Include batching and resume/progress state.
5. Include a confirmation token for destructive writes.
6. Keep apply logic idempotent where practical.
7. Add targeted tests for business logic and safety gates.
8. Add real Convex runtime validation for Convex semantics such as pagination,
validators, internal/public function boundaries, scheduler behavior, and
action/query/mutation interactions.
## Phase 3: Local Validation
Run the smallest meaningful set first, then broaden before PR handoff:
- targeted unit tests for the migration logic
- `bunx convex codegen` when Convex API/schema changed
- `bunx tsc --noEmit` or the repo's Convex deploy typecheck path
- `bun run ci:static`
- `bun run ci:unit` for source/test changes unless explicitly waived
- a real local Convex validation path, such as `bunx convex dev --once`,
`convex run`, HTTP smoke, or local-auth Playwright, covering the changed
Convex behavior
If local real Convex validation is blocked, record the blocker and make the PR
or deployment plan explicitly compensate with an equivalent runtime proof.
## Phase 4: PR, Review, Merge, Deploy
1. Open a focused PR containing the migration implementation.
2. Include:
- summary
- migration strategy
- dry-run/apply safety gates
- tests and runtime validation
- cleanup plan
3. Run the repo's review/CI workflow required by `AGENTS.md`.
4. Address actionable review findings.
5. Merge only after required checks are green or the user explicitly accepts a
documented risk.
6. Deploy the relevant production target from `main`.
7. Wait for deployment success before starting the manual backup gate.
## Phase 5: Manual Backup Gate
Before any production dry run or production migration apply:
1. Tell the operator to open `https://dashboard.convex.dev/`.
2. Tell the operator to select the target deployment and manually click
**Backup Now**.
3. Wait until the Convex dashboard shows that the backup completed.
4. Require the operator to explicitly confirm in the thread that the dashboard
backup completed for the target deployment.
5. Do not automate this step and do not proceed on implied confirmation,
partial screenshots, or "go ahead" messages that do not say the backup
completed.
## Phase 6: Production Dry Run
1. Run only after explicit user confirmation from the manual backup gate.
2. Run the production dry run with bounded batch settings.
3. Resume until either:
- `isDone: true`, or
- a clearly documented safety cap is reached.
4. Present results to the user before apply:
- deployment name
- command shape
- `dryRun`
- `isDone`
- done/progress fields
- scanned/matched/patched/deleted stats
- sample IDs
- resume cursors if incomplete
- known user-visible or operational implications
5. Stop and wait for explicit user confirmation before applying.
## Phase 7: Production Apply
1. Run only after explicit user confirmation of the dry-run results and the
completed manual backup gate.
2. Use the destructive confirmation token.
3. Resume in bounded batches until complete or until a documented safety cap.
4. Present apply results:
- patched/deleted counts
- skipped/missing counts if tracked
- final cursors/progress
- any errors or partial completion
5. Run verification:
- dry run or status command should show zero remaining targets, or
- explain why remaining targets are expected.
6. Stop and wait for explicit user confirmation before cleanup-code removal.
## Phase 8: Cleanup PR
1. Remove temporary migration functions, tests, docs, scripts, and generated API
entries that are no longer needed.
2. Keep durable specs/docs only if they explain lasting behavior or invariants.
3. Run targeted validation plus the repo-required gates for the touched surface.
4. Open a cleanup PR with:
- apply results
- verification proof
- explanation of removed temporary code
5. Merge after checks/review.
6. Deploy the cleanup PR if removing Convex functions or schema/code that affects
production.
## Final Handoff
Report:
- implementation PR URL and merge SHA
- production deploy run URL and deployed SHA
- dry-run result
- manual Convex dashboard backup confirmation
- apply result
- verification result
- cleanup PR URL, merge SHA, and deploy run URL
- any remaining follow-up tasks or intentional retained migration code
@@ -0,0 +1,79 @@
---
name: technical-documentation
description: Build and review high-quality technical docs as well as agent instruction files in your repository.
license: MIT
metadata:
source: "https://github.com/vincentkoc/dotskills"
---
# Technical Documentation
## Purpose
Produce and review technical documentation that is clear, actionable, and maintainable for both humans and agents, including contributor-governance files and agent instruction files.
## When to use
- Creating or overhauling docs in an existing product/codebase (brownfield).
- Building evergreen docs meant to stay accurate and reusable over time.
- Reviewing doc diffs for structure, clarity, and operational correctness.
- Running full-repo documentation audits that must include both governance files and product docs surfaces (`docs/`, `README*`, `.md/.mdx/.mdc`, Fern/Sphinx/Mintlify-style sources).
- Updating or reviewing AGENTS.md and/or CONTRIBUTING.md to keep agent and contributor workflows aligned with current repo practices.
- Improving repository onboarding/docs that include contribution instructions, issue templates, PR flow, and review gates.
- Designing governance documentation strategy for repos with alias instruction files (for example `CLAUDE.md`, `AGENT.md`, `.cursorrules`, `.cursor/rules/*`, `.agent/`, `.agents/`, `.pi/`) where `AGENTS.md` is treated as canonical when present and aliases should be kept as compatibility surfaces.
- Diagnosing agent-file drift where teams had to prompt iteratively to surface missing files, broken commands, or policy conflicts.
- Applying repository-specific documentation overlays, including OpenClaw page-type, docs IA, preservation, and validation rules when present.
## Workflow
1. Classify task: `build` or `review`; context: `brownfield` or `evergreen`.
2. Inventory full documentation scope early (governance + product docs): AGENTS/CONTRIBUTING/aliases plus docs directories, framework sources, and root/module READMEs.
3. Detect multilingual scope (README/docs in multiple languages) and define required parity level.
4. Read `references/agent-and-contributing.md` for agent instruction and `CONTRIBUTING.md` workflow rules (inventory, canonical/alias mapping, dual-mode balance, deliverable standards, and precedence/conflict handling).
5. Read `references/principles.md` for the governing ruleset (Matt Palmer & OpenAI).
6. For OpenClaw docs work, read `references/openclaw.md` before the build/review playbook.
7. For build tasks, follow `references/build.md`.
8. For review tasks, follow `references/review.md` and proactively detect issues without waiting for repeated prompts.
9. For complex or high-risk tasks (build or review), it is acceptable to run longer, deeper, and more exhaustive investigations when needed for confidence.
10. When available, use sub-agents for bounded parallel discovery/review work, then merge outputs into one coherent final deliverable.
11. Use `references/tooling.md` when platform/tooling choices affect recommendations.
12. Run a proactive issue sweep for both governance and docs-content surfaces, and fix high-confidence defects in the same pass unless explicitly asked for report-only mode.
13. In brownfield mode, prioritize compatibility with current docs IA, tooling, and release state.
14. In evergreen mode, prioritize timeless wording, update strategy, and durable structure.
15. Return deliverables plus validation notes, parity status, and remaining gaps.
## Sub-agent orchestration guidance
Prefer sub-agents when the repo is large or the requested change set is broad; use them by default for repo-wide, multi-framework, or high-conflict work.
- `inventory-agent` -> `agents/inventory-agent.md` (`fast` / Claude `haiku`): file/config discovery, coverage map, and missing-path checks.
- `governance-agent` -> `agents/governance-agent.md` (`thinking` / Claude `sonnet`): AGENTS/CONTRIBUTING/alias precedence, conflicts, and policy drift.
- `docs-framework-agent` -> `agents/docs-framework-agent.md` (`thinking` / Claude `sonnet`): framework config, relative path base, and file-path vs URL-path mapping checks.
- `synthesis-agent` -> `agents/synthesis-agent.md` (`long` / Claude `opus`): merge sub-agent outputs into one prioritized fix plan and unified precedence model.
## Inputs
- Doc type (tutorial, how-to, reference, explanation) and audience.
- File scope or diff scope.
- Docs framework/tooling constraints (Fern, Mintlify, Sphinx, etc.).
- Build/review mode and brownfield/evergreen intent.
- Target agent and human compatibility intent.
- Docs framework surfaces in scope (for example Fern, Sphinx, Mintlify, Markdown/MDX/MDC/RST/RSC files).
- Desired investigation depth/time budget (quick pass vs exhaustive review).
- Execution mode (`single-agent` or `sub-agent-assisted` when available).
- Remediation mode (`apply-fixes` by default, or `report-only` when requested).
- Multilingual scope: source-of-truth language, target locales, and parity expectations.
- Repository-specific overlay constraints, if any.
## Outputs
- Updated draft or review findings with clear next actions.
- Validation notes (what was checked, what remains).
- Navigation/maintenance recommendations for long-term quality.
- Governance-doc alignment summary when AGENTS/CONTRIBUTING were touched.
- Agent instruction-surface map (primary file, alias files, Codex/Claude/Cursor handling plan).
- Documentation-surface coverage map (what was reviewed under `/docs`, README hierarchy, and framework-specific source trees).
- Autodetected issue list with applied fixes (or explicit report-only findings).
- Delegation notes when sub-agents were used (scope delegated and how findings were merged).
- Multilingual parity note (in-sync, partial with rationale, or intentionally divergent).
- Repository-specific overlay notes when one was used.
@@ -0,0 +1,32 @@
---
name: docs-framework-agent
description: Thinking-focused docs framework checker for config-relative paths and route/file mapping consistency.
model: sonnet
tools:
- Read
- Glob
- Grep
permissionMode: default
maxTurns: 10
---
You are the docs-framework sub-agent for technical documentation.
Goals:
- validate framework config-driven docs behavior
- prevent path-mapping drift between source files and published routes
Tasks:
- detect and read framework config first (Fern/Sphinx/Mintlify/custom)
- resolve paths relative to the declaring file/config
- validate both maps:
- config -> file exists
- config/nav/routing -> URL path is valid and consistent
Return:
- config files reviewed
- path assumptions made
- mismatches (`missing file`, `stale route`, `wrong base path`)
@@ -0,0 +1,30 @@
---
name: governance-agent
description: Thinking-focused governance reviewer for AGENTS/CONTRIBUTING/alias precedence, conflict detection, and policy drift analysis.
model: sonnet
tools:
- Read
- Glob
- Grep
permissionMode: default
maxTurns: 10
---
You are the governance sub-agent for technical documentation.
Goals:
- validate AGENTS/CONTRIBUTING/alias alignment and precedence
- identify policy drift and conflicting instructions
Tasks:
- determine canonical instruction source and alias compatibility mapping
- detect conflicts across nested scope files and tool-specific rule consumers
- validate command examples against stated governance expectations
Return:
- precedence model
- conflict list with severity
- recommended low-risk remediations
@@ -0,0 +1,31 @@
---
name: inventory-agent
description: Fast repo-surface discovery for technical documentation audits. Use for coverage mapping and missing-path detection before deeper review.
model: haiku
tools:
- Read
- Glob
- Grep
- LS
permissionMode: default
maxTurns: 6
---
You are the inventory sub-agent for technical documentation.
Goals:
- enumerate governance and docs-content surfaces in scope
- detect missing files, broken references, and obvious command/path failures
Tasks:
- map `AGENTS.md`/`CONTRIBUTING.md`/aliases and docs surfaces (`docs/**`, README hierarchy, `.md/.mdx/.mdc/.rst/.rsc`)
- list framework config files discovered (Fern/Sphinx/Mintlify or equivalent)
- report hard failures only, with exact file paths
Return:
- coverage map
- missing/broken path list
- unresolved blockers
@@ -0,0 +1,10 @@
interface:
display_name: "Technical Documentation"
short_description: "Build and review technical documentation for brownfield and evergreen systems."
icon_small: "./assets/icon.jpg"
icon_large: "./assets/icon.jpg"
brand_color: "#111827"
default_prompt: "Build or review technical documentation with a clear, maintainable, and production-ready workflow."
policy:
allow_implicit_invocation: true
@@ -0,0 +1,28 @@
---
name: synthesis-agent
description: Long-context synthesis agent that merges sub-agent outputs into one prioritized and deduplicated documentation action plan.
model: opus
tools:
- Read
permissionMode: default
maxTurns: 12
---
You are the synthesis sub-agent for technical documentation.
Goal:
- merge sub-agent outputs into one coherent, non-duplicated action plan
Tasks:
- prioritize blockers first, then non-blocking improvements
- normalize to one precedence model for governance decisions
- remove duplicated recommendations and contradictory fixes
- keep final output concise and execution-ready
Return:
- prioritized fix plan
- validation summary (done vs pending)
- explicit remaining gaps/blockers
Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

@@ -0,0 +1,145 @@
# AGENT and CONTRIBUTING Principles
This reference consolidates the core rules for agent-policy and contributor-governance docs.
You must:
1. Discover repo-level and nested instruction files with:
`rg --files -g 'AGENTS.md' -g 'CONTRIBUTING.md' -g 'CLAUDE.md' -g 'AGENT.md' -g '.cursor/rules/*' -g '.cursorrules' -g '.agent/**' -g '.agents/**' -g '.pi/**' -g 'AGENTS.*.md'`
2. Read the root and nearest-scope `AGENTS.md`/`CONTRIBUTING.md` pair before editing.
3. If alias files exist, normalize to one canonical source (`AGENTS.md` preferred when present; otherwise nearest alias), plus compatibility pointers or explicit symlink notes.
4. Document conflicting instructions and precedence decisions.
## GitHub + AGENTS baseline
Source: https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/setting-guidelines-for-repository-contributors
Source: https://agents.md/
Source: https://github.blog/ai-and-ml/github-copilot/how-to-write-a-great-agents-md-lessons-from-over-2500-repositories/
Source: https://cobusgreyling.substack.com/p/what-is-agentsmd
Source: https://www.infoq.com/news/2025/08/agents-md/
Use these as default operating principles:
1. Keep `CONTRIBUTING.md` discoverable and actionable (`.github`, root, or `docs`).
2. Keep agent instructions concrete: real commands, real paths, clear boundaries.
3. Use explicit behavior boundaries for agents: `Always`, `Ask first`, `Never`.
4. Keep contributor and agent rules aligned with actual repository workflows.
5. Ensure clear guidance is provided to agents on if, when and how to raise issues and pull requests.
## Canonical and alias policy
Source: https://agents.md/
Source: https://github.blog/ai-and-ml/github-copilot/how-to-write-a-great-agents-md-lessons-from-over-2500-repositories/
1. Treat `AGENTS.md` as canonical when present.
2. If `AGENTS.md` is absent, treat the nearest alias file as canonical.
3. Keep compatibility surfaces explicit: `AGENTS.md`, `AGENT.md`, `.cursorrules`, `.cursor/rules/*`, `.agent/`, `.agents/`, `.pi/`.
4. If aliases are used, document how they map back to canonical policy (or symlink when supported).
5. When repos use `.agents/` as canonical rule storage, keep `.cursor` as a compatibility symlink to `.agents` for Cursor rule auto-loading.
6. Keep policy DRY: store one shared policy core and expose it via aliases/symlinks instead of duplicating rule text.
## Context-awareness by agent platform
Source: https://github.com/vercel-labs/agent-skills/blob/main/AGENTS.md
Source: https://github.com/openai/codex/blob/main/AGENTS.md
1. For Cursor and Claude-style glob consumers, keep rule files narrow and bounded.
2. Avoid over-referencing large path sets that inflate context for glob-based agents.
3. For Codex-style workflows, prefer explicit file references and deterministic commands.
4. Keep long runbooks outside top-level policy files; link to scoped docs.
5. Ensure all agents have a happy path regardless so ensuring everything works across Codex, Claude and other coding agents.
## Symlink and compatibility operations
1. Preferred layout for multi-agent compatibility:
- canonical rule directory: `.agents/`
- Cursor compatibility path: `.cursor -> .agents` symlink
- canonical policy doc: `AGENTS.md` pointing to `.agents` paths where relevant
2. Validate symlink state before finalizing changes:
- if `.agents/` exists and `.cursor` is missing, create `.cursor` symlink to `.agents`
- if `.cursor` is a symlink to another target, fix target or document why it must differ
- if `.cursor` is a real directory/file, treat as migration conflict and ask before replacement
3. Validate rule payload through the canonical directory:
- rules: `.agents/rules/*.mdc` with valid frontmatter (`description`, `globs`, `alwaysApply` as needed)
- commands: `.agents/commands/*.md` when command routing is used
- MCP config: `.agents/mcp.json` when MCP is in scope
4. Keep Codex behavior explicit:
- `AGENTS.md` is primary for Codex repository instructions
- `.cursor` compatibility is for Cursor auto-loading and does not replace canonical AGENTS policy
5. Record applied symlink fixes and unresolved compatibility gaps in validation notes.
## Dual-mode and deliverable standards
Source: https://github.blog/ai-and-ml/github-copilot/how-to-write-a-great-agents-md-lessons-from-over-2500-repositories/
Source: https://agents.md/
Source: https://github.com/openai/codex/blob/main/AGENTS.md
Source: https://github.com/vercel-labs/agent-skills/blob/main/AGENTS.md
1. Author one shared policy core (same commands, boundaries, and precedence) for all agents.
2. For Cursor/Claude-style agents, expose that core through glob-driven and bounded files (small `AGENTS.md`/rule surface).
3. For Codex, expose that same core through explicit file references with precise scope.
4. Where styles diverge, prefer the smallest common structure that satisfies both and avoid duplicating policy text.
5. Treat AGENTS/CONTRIBUTING as first-class deliverables when in scope.
6. Preserve required structure, constraints, and examples from existing files.
7. Align wording and commands with active repository instructions.
## Proactive issue discovery and remediation
Source: https://github.blog/ai-and-ml/github-copilot/how-to-write-a-great-agents-md-lessons-from-over-2500-repositories/
Source: https://github.com/openai/codex/blob/main/AGENTS.md
Source: https://github.com/vercel-labs/agent-skills/blob/main/AGENTS.md
1. Run a conflict matrix review across AGENTS/aliases/CONTRIBUTING and related command/rule docs before finalizing.
2. Treat the following as high-priority defects: missing referenced files, non-existent setup commands, command scope mismatches, and branch/commit policy conflicts.
3. Do not stop at caveat-only notes when a low-risk fix is clear; apply the fix in the same pass.
4. If a canonical entry file is missing (for example a directory `README.md` that docs depend on), create a minimal actionable file and update references.
5. Long-running investigations are acceptable when needed to uncover cross-file drift, especially in agent-instruction ecosystems.
## Discovery
1. Agents prefer simple terminal commands so having a well defined `make *` or `npm run *` is ideal
2. Agents can discover terminal commands through shell completion so providing shell completion helps
## CONTRIBUTING size and scope control
Source: https://contributing.md/how-to-build-contributing-md/
Source: https://blog.codacy.com/best-practices-to-manage-an-open-source-project
Source: https://mozillascience.github.io/working-open-workshop/contributing/
Source: https://github.com/openclaw/openclaw/blob/main/CONTRIBUTING.md
1. Keep root `CONTRIBUTING.md` focused on setup, issue flow, PR flow, testing, and review gates.
2. Use issue/PR template links instead of embedding every process detail inline.
3. When the file grows too large, split by domain and link from root.
4. Move any large content into docs if avalible (for example Mintlify/Fern/Sphinx workflows) to avoid large contributor guide.
5. Optimize for agent/machine readability as well as humans.
## Example repos to emulate
Source: https://github.com/openclaw/openclaw/blob/main/AGENTS.md
Source: https://github.com/openclaw/openclaw/blob/main/CONTRIBUTING.md
Source: https://github.com/openclaw/openclaw/blob/main/VISION.md
Source: https://github.com/openai/codex/blob/main/AGENTS.md
Source: https://github.com/processing/p5.js/blob/main/AGENTS.md
Source: https://github.com/vercel-labs/agent-skills/blob/main/AGENTS.md
Source: https://github.com/agentsmd/agents.md/blob/main/AGENTS.md
Source: https://github.com/rails/rails/blob/main/CONTRIBUTING.md
Source: https://github.com/kubernetes/kubernetes/blob/master/CONTRIBUTING.md
Source: https://github.com/atom/atom/blob/master/CONTRIBUTING.md
Source: https://github.com/github/docs/blob/main/CONTRIBUTING.md
Source: https://github.com/facebook/react/blob/main/CONTRIBUTING.md
1. OpenClaw: strong real-world alias policy and AGENTS/CONTRIBUTING/VISION cohesion.
2. OpenAI Codex: strict command discipline and explicit scope control.
3. p5.js: explicit AI-policy guardrails in agent instructions.
4. Vercel + agentsmd spec: compact, context-efficient AGENTS patterns.
5. Rails/Kubernetes/Atom/GitHub Docs/React: contributor guidance patterns at different project scales.
## Practical merge policy
When these rules conflict:
1. Preserve contributor and reader task success first.
2. Preserve instruction clarity and unambiguous boundaries second.
3. Preserve long-term maintainability and context-efficiency third.
4. Add extra agent optimization only if it does not reduce human clarity or there is explict need.
5. Use your judgement as the expert.
@@ -0,0 +1,116 @@
# Build Docs Playbook
Read `principles.md` first, then follow this execution flow.
## 1. Detect and align agent instruction and governance instructions
- Use `references/agent-and-contributing.md` as the source of truth for inventory, canonical/alias mapping, and precedence/conflict handling.
- Apply the symlink compatibility policy when in scope (`.agents` canonical directory with `.cursor` compatibility symlink when required by tooling).
- Long-running and extensive build investigations are acceptable when needed to resolve ambiguous or conflicting documentation sources.
- When available, use sub-agents for bounded parallel inventory/cross-check tasks and merge results into one canonical decision set.
- Capture required constraints before writing:
- nested-agent rules, command/test requirements, PR workflow, and style checks.
- Use the same command and validation expectations in proposed snippets and examples.
## 2. Inventory product documentation surfaces (not governance only)
- For repo-wide builds, include docs content surfaces in addition to AGENTS/CONTRIBUTING.
- Inventory docs files and frameworks in scope (examples): `README*.md`, `docs/**`, `**/*.md`, `**/*.mdx`, `**/*.mdc`, `**/*.rst`, `**/*.rsc`, Fern/Mintlify config, Sphinx `conf.py`.
- Build a coverage map before drafting so governance and product docs are both represented.
- If scope is ambiguous, default to broader docs discovery first, then narrow intentionally.
## 3. Framework config and path mapping rules
- Detect framework/config first (for example Fern config, Sphinx `conf.py`, Mintlify config, or equivalent).
- Resolve every referenced path relative to the file/config that declares it, not assumed repo root.
- Treat filesystem paths and published URL routes as separate mappings; do not infer one from the other without config evidence.
- Validate both layers:
- config -> file exists on disk
- config/nav/routing -> URL path is consistent and reachable
- Record path-mapping assumptions and mismatches in handoff (`missing file`, `stale route`, `wrong base path`).
## 4. Define intent and success
- Audience, prerequisites, and job-to-be-done.
- Expected reader outcome immediately after completion.
- Doc type: tutorial, how-to, reference, explanation.
- Success criteria: what must be true after publish.
## 5. Build structure before prose
- Follow the funnel: what/why, quickstart, next steps.
- Keep headings informative and scannable.
- Open each section with the takeaway sentence.
- Add decision points with concrete branch guidance.
- For OpenClaw docs work, choose a page type from `references/openclaw.md` before drafting.
- Keep task-critical OpenClaw configuration inline; link exhaustive defaults, enums, schemas, generated references, and rare debugging workflows.
## 6. Build AGENTS.md and CONTRIBUTING.md intentionally
- Keep AGENTS.md structure consistent with `agents.md` ecosystem patterns:
- include YAML frontmatter when present in repo style (`name`, `description`).
- state persona scope and explicit instruction boundaries: `Always`, `Ask first`, `Never`.
- include concrete commands and representative code examples.
- For CONTRIBUTING.md, prioritize issue triage flow, PR expectations, setup/test commands, and review gates.
- Add `Code of Conduct`, `Testing`, `Local checks`, and `PR expectations` sections when missing but required by the repo.
- If CONTRIBUTING.md is becoming too large, split by scope into linked docs (for example, framework/tool-specific setup and release workflows) and keep the root file as a concise entry point.
- Keep cross-file consistency: links from CONTRIBUTING.md to AGENTS.md (and vice versa) should be accurate and non-circular.
- If multiple AGENTS.md files exist, document the directory-level scope and avoid conflicting advice.
- If a required canonical entry file is missing (for example referenced `README.md` under a major directory), create the file in the same pass instead of adding a caveat-only note.
- For new entry files, keep them minimal and actionable: purpose, prerequisites, concrete run commands, and pointers to deeper docs.
## 7. Keep agent context tight
- Author once, expose twice:
- keep one shared policy core and avoid duplicating guidance in separate agent-specific files.
- publish that core through bounded glob-friendly files for Cursor/Claude plus explicit path references for Codex.
- For Cursor and Claude-style agents, avoid broad references. Use minimal globbing and narrow rule files that each serve one concern (for example, repo-wide setup, test rules, security checks).
- Keep AGENTS and alias files short-to-medium; move detailed runbooks to linked docs.
- For Codex, prefer explicit file references and concrete paths for exact reuse.
- Avoid adding unrelated historical or process details to avoid token/context drift during future tool reads.
## 8. Brownfield build mode
- Match existing terminology, navigation, and component patterns.
- Preserve existing IA unless there is a documented migration plan.
- For rewrites, include a migration note from old to new paths.
- Prefer smallest safe change set that improves utility.
## 9. Evergreen build mode
- Prefer stable concepts over release-tied narrative.
- Isolate volatile details under clearly marked version sections.
- Include maintenance signals: owners, refresh triggers, stale criteria.
- Include lifecycle notes: deprecation and replacement paths.
## 10. Writing constraints
- Use precise language and short, imperative instructions.
- Keep code examples copy-ready and self-contained.
- Include common failure modes and safe defaults.
- Avoid placeholder guidance that cannot be executed.
## 11. Agent and automation readiness
- Keep key facts in text (not image-only).
- Prefer structured lists/tables when choices matter.
- Add links and anchors that allow deterministic navigation.
- Document what can be checked automatically in CI.
## 12. Build validation
- Validate commands and snippets where possible.
- Verify links and references in changed sections.
- Run a reference existence sweep for every path/command you introduced.
- Verify docs-framework consistency when in scope (for example Sphinx/Fern config and referenced doc paths).
- For OpenClaw docs work, apply the validation checklist in `references/openclaw.md`.
## 13. Multilingual parity mode (when applicable)
- Pick one source-of-truth language for technical accuracy and release timing.
- Define parity target: full parity, staged parity, or intentional divergence per section.
- Keep structure aligned across locales (headings, anchors, section order) when possible.
- Preserve command/code correctness first; localize explanatory text second.
- If parity is not feasible, add a visible note with missing scope and expected sync window.
- Run a locale parity check for changed sections (added/removed steps, warnings, prerequisites).
- Record unresolved checks explicitly in handoff.
@@ -0,0 +1,128 @@
# OpenClaw Documentation Overlay
Use this reference only for OpenClaw docs work. It layers OpenClaw-specific page
types, navigation, preservation, and validation rules on top of the general
technical-documentation skill.
## Reader Model
- Lead with the task the reader is trying to complete.
- Give one recommended path before alternatives.
- Keep main docs focused on the common path; move dense contracts and rare
debugging detail to linked reference or troubleshooting pages.
- Explain production risks exactly where the reader can make the mistake.
- Link concepts, guides, references, CLI pages, SDK docs, testing, and
troubleshooting so readers can continue without rereading.
## Page Types
Choose the page type before writing or reviewing:
- Overview: route readers to the right product area, integration path, or guide.
- Quickstart: get a new user to a working result with the fewest safe steps.
- Topic page: explain a major OpenClaw entity or surface end to end.
- Guide: walk through one workflow from prerequisites to production readiness.
- API/SDK/CLI reference: define every object, method, command, option, response,
error, enum, default, and version rule in scope.
- Testing guide: show sandbox setup, fixtures, simulated failures, and live-mode
differences.
- Troubleshooting guide: map observable symptoms to checks, causes, and fixes.
- Governance file: keep agent/contributor policy concrete, scoped, and aligned
with current OpenClaw repo behavior.
## Topic Pages
Use this shape for major-entity pages:
1. Title naming the entity or surface.
2. Unheaded opening that says what it is, what it owns, and what it does not own.
3. Requirements, only when setup needs accounts, versions, permissions, plugins,
operating systems, or credentials.
4. Quickstart with the recommended path and smallest reliable verification.
5. Configuration with task-critical options inline and exhaustive details linked
to reference docs.
6. Major subtopics organized by reader intent, not under a generic "Subtopics"
heading.
7. Troubleshooting with observable failures and concrete checks.
8. Related links to guides, references, commands, concepts, and adjacent topics.
## Guides
Use this shape for workflow pages:
1. Title naming the outcome, not the implementation detail.
2. Opening that states what the reader can accomplish.
3. Before you begin: accounts, keys, permissions, versions, tools, and
assumptions.
4. Choose a path, only when the reader must decide.
5. Steps with verb-led headings, commands, expected output, and checks.
6. Test with the smallest reliable proof that the workflow works.
7. Production readiness: security, retries, limits, observability, migrations,
and cleanup.
8. Troubleshooting near the workflow that causes the failures.
9. See also links to concepts, references, SDK docs, and adjacent guides.
## Docs IA And Navigation
- Read `docs/docs.json` before navigation changes.
- Keep topic pages and common workflows on the main reader path.
- Put exhaustive contracts, generated references, maintainer-only detail, and
support material under `Reference` or another clearly scoped support page.
- Keep generated `plugins/reference/*` children and redirect-only pages out of
visible navigation unless explicitly required.
- For moved pages, include a keep/drop/move/destination matrix in the handoff.
- Add "Read when" hints for docs-list routing when creating or changing pages
that participate in the docs index.
## Source-Backed Content
- CLI docs must match current flags, output, errors, and examples.
- API/SDK docs must include fields, defaults, enum values, constraints, nullable
behavior, lifecycle states, errors, and recovery guidance.
- Config docs must align exported types, schema/help output, metadata, baselines,
and current docs.
- Dependency-backed behavior must be verified from upstream docs, source, or
types before documenting defaults, timing, errors, or API behavior.
- Separate current behavior, shipped behavior, planned behavior, and maintainer
intent.
## Examples
- Prefer complete copy-pasteable commands and snippets.
- Use realistic variable names and values.
- Mark placeholders with angle-bracket names such as `<API_KEY>`.
- Show expected success output when it helps verification.
- Keep one conceptual unit per code block and use language-specific fences.
- Avoid examples that hide setup, auth, error handling, or cleanup.
- Never expose real secrets, live config, phone numbers, private videos, or
credentials.
## Preservation Reviews
For rewrites or splits:
- Identify source units before rewriting: headings, paragraphs, tables, examples,
CLI/API contracts, warnings, and troubleshooting facts.
- Map each retained unit to a destination page or section.
- Do not treat a broad "covered" row as proof for dense source material; use
line- or claim-level evidence when the source unit is dense.
- For dropped content, state whether it is obsolete, duplicated elsewhere,
unsupported, or moved to a reference/support page.
- When a docs-audit artifact is used, verify it is mapped audit data with
non-empty `mappings[]`, not only inventory or reindexed JSON.
## Validation
Choose the narrowest proof that covers the touched surface:
- `pnpm docs:list`
- `pnpm docs:check-mdx`
- `pnpm docs:check-links`
- `pnpm docs:check-i18n-glossary`
- `pnpm format:docs:check` or `pnpm lint:docs`
- `git diff --check`
- generated-doc or inventory checks when generated references, plugin catalogs,
labeler, or docs scripts changed
- behavior tests or command probes when docs claim runtime behavior
If proof is blocked, say exactly which command was not run and why.
@@ -0,0 +1,54 @@
# Documentation Principles
This reference consolidates the core rules used by this skill.
## Matt Palmer: 8 rules for better docs
Source: https://mattpalmer.io/posts/2025/10/8-rules-for-better-docs/
Use these as default operating principles:
1. Write for humans, optimize for agents.
2. Start with a funnel: what/why, quickstart, next steps.
3. Use Diataxis to scaffold content.
4. Write with AI, but structure for agents.
5. Offload routine docs operations to background agents.
6. Automate quality with CI.
7. Automate scaffolding and repetitive workflow tasks.
8. Make contribution easy and visible.
## OpenAI cookbook: what makes documentation good
Source: https://cookbook.openai.com/articles/what_makes_documentation_good
Key quality constraints:
- Prefer specific and accurate terminology over niche jargon.
- Keep examples self-contained and minimize dependencies.
- Prioritize high-value topics over edge-case depth.
- Do not teach unsafe patterns (for example, exposed secrets).
- Open with context that helps readers orient quickly.
- Apply empathy and override rigid rules when it clearly improves outcomes.
## Practical merge policy
When these rules conflict:
1. Preserve reader task success first.
2. Preserve structural clarity second.
3. Preserve long-term maintainability third.
4. Add agent optimization only if it does not reduce human clarity.
For agent-instructions and contributor-governance specifics (AGENTS/aliases/CONTRIBUTING), use `references/agent-and-contributing.md` as the detailed additional source of truth.
When the target repo or request is OpenClaw-specific, layer `references/openclaw.md` on top of these general rules. Otherwise ignore that repo-specific overlay.
## Execution policy for this skill
- Long-running and extensive investigations are allowed for both build and review work when needed to resolve ambiguity or cross-file drift.
- Use sub-agents when available for bounded parallel discovery, verification, or cross-source comparison.
- Keep one merged outcome: sub-agent outputs must be normalized into a single consistent recommendation/fix set.
## Multilingual parity rule
When docs exist in multiple languages, target cross-locale parity for task-critical content (steps, warnings, prerequisites, and limits). If full parity is not possible, publish explicit parity status and sync intent.
@@ -0,0 +1,121 @@
# Review Docs Playbook
Read `principles.md` first, then apply this checklist.
## 1. Scope and classification
- Identify doc type and target audience.
- Confirm brownfield vs evergreen intent.
- Confirm expected outcome for the reader.
- For full-repo reviews, explicitly include both governance surfaces and product-doc surfaces (`docs/`, README trees, `.md/.mdx/.mdc`, `.rst/.rsc`, framework docs configs).
- For OpenClaw docs reviews, apply `references/openclaw.md` for page type, docs IA, preservation, examples, and validation checks.
## 2. Investigation behavior
- Proactively find issues and risks without waiting for repeated prompts.
- If there are signals of deeper problems, continue investigation beyond the first pass.
- Long-running and extensive investigations are acceptable when needed for confidence and correctness.
- When available, use sub-agents for bounded parallel discovery (for example file-inventory, command validation, or cross-doc consistency checks), then merge to one final issue set.
- When no issues are found, state that explicitly and call out residual risks or validation gaps.
- Default to `apply-fixes` for high-confidence documentation defects unless the user explicitly requests `report-only`.
- Do not stop at AGENTS/CONTRIBUTING checks when the task is documentation-wide; continue into docs-content and docs-framework surfaces.
## 3. Governance surface review
- Use `references/agent-and-contributing.md` as the source of truth for inventory, canonical/alias mapping, and precedence/conflict handling.
For AGENTS.md:
- confirm persona intent, scope, and command/tool boundaries are explicit.
- check frontmatter style matches repo conventions when present.
- ensure `Always`, `Ask first`, and `Never` boundaries are present when expected.
- require concrete command examples and repo-specific paths to avoid ambiguity.
For CONTRIBUTING.md:
- verify issue/PR workflow is complete and actionable.
- ensure local setup, lint/test commands, and review criteria are accurate.
- ensure governance does not conflict with nested AGENTS instructions.
- flag oversized files that should be split into linked section docs (for example tool-specific setup and release docs).
For agent-platform awareness:
- confirm references are minimal and scoped for Cursor/Claude glob behavior.
- confirm Codex-facing guidance uses explicit file references.
- confirm both surfaces represent the same shared policy core (commands, boundaries, and precedence), not divergent guidance.
- audit `.agents`/`.cursor` compatibility behavior:
- verify canonical rule directory and symlink state match repo policy
- verify symlink target integrity and platform/tooling expectations
- verify AGENTS policy references remain canonical for Codex even when `.cursor` compatibility exists
- check for context bloat from duplicated policy statements across agent and contributor files.
- check for conflicting rules, skills and agent instructions
- check for conflicting information in agent instructions vs codebase
- check for broken or missing referenced files (for example README/index files named as canonical entry points).
- check for setup/command drift (for example non-existent install commands, root-level commands that should be module-scoped).
## 4. Product documentation surface review
- Verify docs IA coverage across root/module `README*` files and `docs/**` trees.
- Review framework-native docs sources in scope (for example Fern, Mintlify, Sphinx, MkDocs) and ensure guidance matches actual source-of-truth files.
- Check `.md/.mdx/.mdc/.rst/.rsc` for stale commands, missing prerequisites, and broken cross-links.
- Confirm referenced doc paths and anchors exist.
- Flag docs that should be split/merged to improve discoverability and maintenance.
- For OpenClaw docs, check `docs/docs.json`, docs-list routing hints, main path versus `Reference` placement, and generated-reference visibility.
- For OpenClaw rewrites or page splits, require source-backed keep/drop/move/destination coverage for important claims, warnings, examples, commands, fields, and troubleshooting facts.
## 5. Framework config and path mapping checks
- Detect and read framework config first (for example Fern config, Sphinx `conf.py`, Mintlify config, or equivalent).
- Resolve path references relative to the declaring file/config.
- Treat filesystem paths and published URL routes as separate maps; verify both.
- Flag path-map drift explicitly (`missing file`, `stale route`, `wrong base path`).
## 6. Structural review
- Funnel check: what/why, quickstart, next steps.
- Validate heading flow and navigation discoverability.
- Flag critical content trapped in images or buried sections.
- Check Diataxis alignment and split mixed-purpose sections.
- For OpenClaw docs, confirm the content matches an explicit page type from `references/openclaw.md`.
## 7. Writing quality review
- Check for concise, scannable paragraphs.
- Remove ambiguous pronouns and undefined terms.
- Verify examples are executable and scoped correctly.
- Verify tone is directive, technical, and non-hand-wavy.
## 8. Brownfield review mode
- Verify compatibility with existing docs IA and conventions.
- Verify anchors, redirects, and cross-doc links remain valid.
- Flag regressions in onboarding and task completion paths.
- Ensure changed terminology is intentionally propagated.
## 9. Evergreen review mode
- Flag date-stamped or brittle wording without version scope.
- Check ownership and refresh signals are present.
- Ensure recommendations remain valid after routine product evolution.
- Flag missing deprecation/migration guidance.
## 10. Tooling and platform review
Read `tooling.md` if platform fit is uncertain.
- Check whether content uses platform primitives effectively.
- Flag structure that fights the chosen docs platform.
- Recommend targeted platform-aware improvements.
## 11. Multilingual parity review (when applicable)
- Confirm declared source-of-truth language and expected parity policy.
- Compare changed sections across locales for step/order/warning drift.
- Flag missing updates to prerequisites, version notes, limits, and safety guidance.
- Allow intentional divergence only when rationale is explicit and user-impact is low.
- Require a reader-visible status note when locale parity is partial.
## 12. Output format
1. Blocking issues (file + required fix)
2. Non-blocking improvements
3. Validation notes (done vs pending)
@@ -0,0 +1,32 @@
# Documentation Tooling Guide
Source: https://www.mintlify.com/blog/top-7-api-documentation-tools-of-2025
Use this file when deciding build/review expectations for doc platforms.
## Tool-selection checkpoints
- Existing stack lock-in: do not force migration for minor gains.
- API workflow depth: generated references, OpenAPI support, testability.
- Collaboration model: docs-as-code, review workflow, versioning.
- Runtime quality: search, navigation, and copy-ready code snippets.
- AI readiness: structured content, stable URLs, machine-friendly layout yet human readable.
- Human readiness: reading complexity, reading UX, navigation depth, minimize jargon.
## Apply in brownfield mode
- Prioritize compatibility with the current platform.
- Use available components and style conventions before introducing new patterns.
- Propose migration only when current constraints block critical outcomes.
## Apply in evergreen mode
- Favor platforms and templates that make routine updates low-friction.
- Standardize section templates to reduce drift.
- Capture ownership, update cadence, and stale-content detection rules.
## Review implications
- Check whether content uses platform primitives correctly (tabs, callouts, endpoint blocks).
- Flag docs that are technically correct but hard to scan in the chosen platform.
- Recommend platform-specific improvements only when they reduce cognitive load.
+21
View File
@@ -0,0 +1,21 @@
# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY
version = 1
name = "ClawHub"
[setup]
script = "bun run setup:worktree -- --quiet && bun scripts/dev-worktree.ts --detach"
[[actions]]
name = "Run"
icon = "run"
command = "bun run dev:worktree"
[[actions]]
name = "Convex Dev"
icon = "tool"
command = "bun run setup:worktree -- --quiet && bunx convex dev --typecheck=disable"
[[actions]]
name = "Seed Dev DB"
icon = "tool"
command = "bun run seed:dev"
+20
View File
@@ -0,0 +1,20 @@
[list]
url = "http://127.0.0.1:{{ (repo ~ '-' ~ branch) | hash_port }}"
[[pre-start]]
env = "wt step copy-ignored || true; bun run setup:worktree -- --quiet --force --prefer-fallback"
[[pre-start]]
deps = "test -x node_modules/.bin/vite || bun install"
[post-start]
dev = "bun scripts/dev-worktree.ts --detach --seed --port {{ (repo ~ '-' ~ branch) | hash_port }}"
[pre-remove]
dev = "if test -f .codex/runtime/dev-worktree.pid; then pid=$(cat .codex/runtime/dev-worktree.pid); kill -TERM -$pid 2>/dev/null || kill $pid 2>/dev/null || true; rm -f .codex/runtime/dev-worktree.pid; fi"
[aliases]
dev = "wt --yes hook pre-start && bun scripts/dev-worktree.ts --detach --seed --port {{ (repo ~ '-' ~ branch) | hash_port }}"
setup = "wt --yes hook pre-start"
stop = "if test -f .codex/runtime/dev-worktree.pid; then pid=$(cat .codex/runtime/dev-worktree.pid); kill -TERM -$pid 2>/dev/null || kill $pid 2>/dev/null || true; rm -f .codex/runtime/dev-worktree.pid; fi"
url = "echo http://127.0.0.1:{{ (repo ~ '-' ~ branch) | hash_port }}"
+32
View File
@@ -0,0 +1,32 @@
profile: clawhub-check
provider: blacksmith-testbox
blacksmith:
org: openclaw
workflow: .github/workflows/ci-check-testbox.yml
job: check
ref: main
idleTimeout: 90m
debug: false
sync:
delete: true
checksum: false
gitSeed: true
fingerprint: true
baseRef: main
exclude:
- .artifacts
- .codex
- .DS_Store
- coverage
- dist
- dist-ssr
- node_modules
- playwright-report
- test-results
env:
allow:
- CI
- NODE_OPTIONS
- CLAWHUB_*
- VITE_CONVEX_URL
- VITE_CONVEX_SITE_URL
+15
View File
@@ -0,0 +1,15 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab
+12 -3
View File
@@ -1,9 +1,7 @@
# Frontend
VITE_CONVEX_URL=
VITE_CONVEX_SITE_URL=
VITE_SOULHUB_SITE_URL=
VITE_SOULHUB_HOST=
VITE_SITE_MODE=
VITE_ENABLE_DEV_AUTH=
SITE_URL=http://localhost:3000
CONVEX_SITE_URL=
@@ -15,5 +13,16 @@ AUTH_GITHUB_SECRET=
JWT_PRIVATE_KEY=
JWKS=
# Local dev personas
DEV_AUTH_ENABLED=
DEV_AUTH_CONVEX_DEPLOYMENT=
DEV_AUTH_SITE_URL=
DEV_AUTH_SECRET=
# Embeddings
OPENAI_API_KEY=
# Transactional email
RESEND_API_KEY=
CLAWHUB_SECURITY_EMAIL_FROM=ClawHub Security <noreply@notifications.openclaw.ai>
CLAWHUB_NOREPLY_FROM=ClawHub <noreply@notifications.openclaw.ai>
+12
View File
@@ -0,0 +1,12 @@
* text=auto eol=lf
*.avif binary
*.gif binary
*.ico binary
*.jpg binary
*.jpeg binary
*.png binary
*.webp binary
*.woff binary
*.woff2 binary
+119
View File
@@ -0,0 +1,119 @@
# Protect the ownership rules themselves.
/.github/CODEOWNERS @openclaw/openclaw-secops
# WARNING: GitHub CODEOWNERS uses last-match-wins semantics.
# If you add overlapping rules below the secops block, include @openclaw/openclaw-secops
# on those entries too or you can silently remove required secops review.
# Security-sensitive code, config, workflows, and docs require secops review.
/.github/actions/ @openclaw/openclaw-secops
/.github/actionlint.yaml @openclaw/openclaw-secops
/.github/codeql/ @openclaw/openclaw-secops
/.github/dependabot.yml @openclaw/openclaw-secops
/.github/workflows/ @openclaw/openclaw-secops
/scripts/clawhub-cli-npm-publish.sh @openclaw/openclaw-secops
/scripts/clawhub-cli-npm-release-check.mjs @openclaw/openclaw-secops
/scripts/github/clawhub-rescan-auto-response.mjs @openclaw/openclaw-secops
# Backend auth, API, publish, upload, moderation, and scan enforcement.
/convex/schema.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/auth.config.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/auth.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/http.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/httpApi.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/httpApiV1/ @openclaw/openclaw-secops @Patrick-Erichsen
/convex/packagePublishTokens.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/packages.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/maintenance.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/publishers.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/rateLimits.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/skills.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/skillTransfers.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/tokens.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/uploads.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/vt.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/webhooks.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/access.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/apiTokenAuth.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/githubActionsOidc.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/httpHeaders.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/httpRateLimit.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/manualOverrides.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/moderation.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/moderationEngine.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/moderationReasonCodes.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/packageRegistry.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/packageSearchDigest.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/packageSecurity.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/publishers.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/publishLimits.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/reporting.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/securityPrompt.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/skillCapabilityTags.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/skillPublish.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/skillSafety.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/staticPublishScan.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/tokens.ts @openclaw/openclaw-secops @Patrick-Erichsen
/convex/lib/webhooks.ts @openclaw/openclaw-secops @Patrick-Erichsen
# Frontend auth, admin, publish, upload, and security-review surfaces.
/src/lib/packageApi.ts @openclaw/openclaw-secops @BunsDev
/src/lib/packageUpload.ts @openclaw/openclaw-secops @BunsDev
/src/lib/roles.ts @openclaw/openclaw-secops @BunsDev
/src/lib/uploadFiles.ts @openclaw/openclaw-secops @BunsDev
/src/lib/uploadUtils.ts @openclaw/openclaw-secops @BunsDev
/src/routes/admin.tsx @openclaw/openclaw-secops @BunsDev
/src/routes/cli/auth.tsx @openclaw/openclaw-secops @BunsDev
/src/routes/packages/new.tsx @openclaw/openclaw-secops @BunsDev
/src/routes/plugins/publish.tsx @openclaw/openclaw-secops @BunsDev
/src/routes/publish-plugin.tsx @openclaw/openclaw-secops @BunsDev
/src/routes/publish-skill.tsx @openclaw/openclaw-secops @BunsDev
/src/routes/skills/publish.tsx @openclaw/openclaw-secops @BunsDev
/src/routes/upload.tsx @openclaw/openclaw-secops @BunsDev
/src/routes/upload/ @openclaw/openclaw-secops @BunsDev
/src/routes/$owner/$slug/security/ @openclaw/openclaw-secops @BunsDev
/src/routes/plugins/$name/security/ @openclaw/openclaw-secops @BunsDev
# CLI auth, admin, publishing, ownership, and package-contract surfaces.
/packages/clawhub/src/browserAuth.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/clawhub/src/http.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/clawhub/src/cli/adminHelp.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/clawhub/src/cli/authToken.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/clawhub/src/cli/clawdbotConfig.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/clawhub/src/cli/commands/auth.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/clawhub/src/cli/commands/delete.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/clawhub/src/cli/commands/github.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/clawhub/src/cli/commands/moderation.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/clawhub/src/cli/commands/ownership.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/clawhub/src/cli/commands/packages.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/clawhub/src/cli/commands/publish.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/clawhub/src/cli/commands/transfer.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/clawhub/src/schema/openclawContract.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/clawhub/src/schema/packages.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/clawhub/src/schema/routes.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/clawhub/src/schema/schemas.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/clawhub/src/schema/textFiles.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/schema/src/openclawContract.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/schema/src/index.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/schema/src/packages.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/schema/src/pluginCategories.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/schema/src/routes.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/schema/src/schemas.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/schema/src/textFiles.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/schema/dist/index.d.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/schema/dist/index.js @openclaw/openclaw-secops @Patrick-Erichsen
/packages/schema/dist/index.js.map @openclaw/openclaw-secops @Patrick-Erichsen
/packages/schema/dist/pluginCategories.d.ts @openclaw/openclaw-secops @Patrick-Erichsen
/packages/schema/dist/pluginCategories.js @openclaw/openclaw-secops @Patrick-Erichsen
/packages/schema/dist/pluginCategories.js.map @openclaw/openclaw-secops @Patrick-Erichsen
# Security, auth, API, webhook, and deployment documentation.
/docs/acceptable-usage.md @openclaw/openclaw-secops @Patrick-Erichsen
/docs/api.md @openclaw/openclaw-secops @Patrick-Erichsen
/docs/auth.md @openclaw/openclaw-secops @Patrick-Erichsen
/docs/http-api.md @openclaw/openclaw-secops @Patrick-Erichsen
/docs/namespace-claims.md @openclaw/openclaw-secops @Patrick-Erichsen
/docs/security.md @openclaw/openclaw-secops @Patrick-Erichsen
/docs/webhook.md @openclaw/openclaw-secops @Patrick-Erichsen
/specs/deploy.md @openclaw/openclaw-secops @Patrick-Erichsen
/specs/github-import.md @openclaw/openclaw-secops @Patrick-Erichsen
/public/api/v1/openapi.json @openclaw/openclaw-secops @Patrick-Erichsen
@@ -0,0 +1,117 @@
name: Org / Namespace Claim
description: Request review for an org, brand, package scope, or namespace ownership dispute.
title: "Org claim: "
labels:
- "area: moderation"
- "area: security"
- "status: review"
body:
- type: markdown
attributes:
value: |
Use this form when you believe a ClawHub org, owner handle, package scope, skill slug, plugin package, or related namespace should be reserved, transferred, renamed, hidden, quarantined, aliased, or reviewed because of real-world project, brand, or organizational ownership.
Public GitHub issues must not include secrets, private documents, private legal files, personal identity documents, API tokens, DNS control tokens, or other sensitive material. Share public, non-sensitive proof here and tell us below if staff needs to arrange a private channel for sensitive evidence.
This is not the ban/account appeal flow. If your ClawHub account was banned, disabled, or cannot sign in because of account standing, use the ClawHub appeal form instead: https://appeals.openclaw.ai/
Related policy discussion: https://github.com/openclaw/clawhub/issues/2320
- type: input
id: claimed_namespace
attributes:
label: Claimed owner, org, scope, or namespace
description: Which ClawHub owner handle, org handle, package scope, skill slug, or package namespace are you claiming?
placeholder: "@example-org, example-org, @example-org/example-plugin, or example-skill"
validations:
required: true
- type: textarea
id: disputed_resources
attributes:
label: Disputed ClawHub resources
description: Link every relevant ClawHub URL, package name, skill slug, owner page, or related GitHub issue.
placeholder: |
- https://clawhub.ai/example-org/example-skill
- https://clawhub.ai/plugins/@example-org/example-plugin
- Package or skill names involved:
validations:
required: true
- type: textarea
id: claimant_relationship
attributes:
label: Claimant identity and relationship
description: Explain who is making the request and how they are connected to the org, project, package, or brand. Keep it public-safe.
placeholder: |
I maintain the upstream project at...
I am an admin/owner/member of...
Public profile or docs showing that relationship:
validations:
required: true
- type: dropdown
id: requested_outcome
attributes:
label: Requested outcome
description: Pick every outcome that would resolve the claim.
multiple: true
options:
- Reserve namespace or package
- Transfer ownership
- Rename existing resource
- Hide or quarantine current resource
- Add alias or redirect
- Review only / need staff guidance
- Other
validations:
required: true
- type: textarea
id: public_proof
attributes:
label: Public proof links and explanation
description: Add public links and explain what each proves. Useful proof includes GitHub org/repo control, domain or official email-domain proof, package-registry scope control, trademark or brand evidence, source repo history, package history, and public project docs. Do not paste secrets, tokens, private documents, or private legal files.
placeholder: |
- https://github.com/example-org/example-project proves...
- https://example.org/docs/clawhub proves...
- https://www.npmjs.com/org/example-org proves...
validations:
required: true
- type: textarea
id: current_owner_context
attributes:
label: Current owner, history, or context
description: Share what you know about the current ClawHub owner, prior transfers, project rename history, or attempted contact.
placeholder: |
The current listing appears to be owned by...
We contacted...
The project was renamed from...
- type: textarea
id: urgency_context
attributes:
label: User harm or urgency
description: Explain impact, affected users, install paths, or other facts that should affect triage priority. Say "No urgent user harm known" if this is not urgent.
placeholder: |
Users are being directed from...
The package is referenced by...
We believe this is urgent because...
validations:
required: true
- type: textarea
id: sensitive_evidence
attributes:
label: Sensitive evidence or private staff channel
description: Say whether public evidence is enough or whether staff needs to arrange a private channel. Summarize the kind of private evidence without including the sensitive material itself.
placeholder: |
Public evidence is enough.
Or:
We need a private staff channel for DNS challenge proof, private legal documents, or other sensitive evidence.
validations:
required: true
- type: checkboxes
id: acknowledgements
attributes:
label: Acknowledgements
options:
- label: I have not included secrets, API tokens, private documents, private legal files, personal identity documents, or other sensitive material in this public issue.
required: true
- label: I understand this public issue may be linked from ClawHub moderation or namespace policy discussions.
required: true
+104
View File
@@ -0,0 +1,104 @@
name: RFC
description: Propose a ClawHub policy, product, trust, or interface decision for feedback.
title: "RFC: "
labels:
- "type: rfc"
- "status: review"
body:
- type: markdown
attributes:
value: |
Use RFCs for decisions that need visible feedback before they become policy, product behavior, or public API contract. Accepted repo RFC files live under `rfcs/`, not `docs/`, so draft/decision records do not publish to the docs site. Keep sensitive enforcement details, private reports, exploit specifics, and scanner thresholds out of the public issue.
- type: dropdown
id: area
attributes:
label: Area
description: Pick the primary area this RFC affects.
options:
- Moderation / policy
- Security / trust
- Product / UX
- API / CLI
- Documentation
- Other
validations:
required: true
- type: textarea
id: context
attributes:
label: Context
description: What problem, decision, or ambiguity does this RFC address?
placeholder: |
ClawHub needs a clearer policy for...
validations:
required: true
- type: textarea
id: goals
attributes:
label: Goals
description: What should this RFC achieve?
placeholder: |
- Make enforcement expectations understandable to users.
- Give moderators a consistent decision boundary.
validations:
required: true
- type: textarea
id: non_goals
attributes:
label: Non-goals
description: What is intentionally out of scope?
placeholder: |
- This RFC does not expose internal scanner thresholds.
- This RFC does not decide implementation details for every moderation tool.
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposal
description: Describe the proposed policy, behavior, or decision.
placeholder: |
ClawHub should...
validations:
required: true
- type: textarea
id: examples
attributes:
label: Examples
description: Give concrete allowed, not allowed, or edge-case examples.
placeholder: |
Allowed:
- Defensive security review with explicit scope and evidence.
Not allowed:
- Account takeover, evasion, or non-consensual surveillance workflows.
Edge cases:
- ...
- type: textarea
id: user_impact
attributes:
label: User impact
description: How does this affect authors, users, moderators, API consumers, or external contributors?
placeholder: |
Authors will...
Users will...
Moderators will...
- type: textarea
id: open_questions
attributes:
label: Open questions
description: What feedback would be most useful before a decision?
placeholder: |
- Should appeals be handled in-product, through GitHub, or both?
- What examples would make this clearer?
validations:
required: true
- type: input
id: feedback_deadline
attributes:
label: Feedback deadline
description: Use an absolute date. Normal RFCs should stay open for 7-14 days unless urgent.
placeholder: "YYYY-MM-DD"
validations:
required: true
+10
View File
@@ -0,0 +1,10 @@
# actionlint configuration
# https://github.com/rhysd/actionlint/blob/main/docs/config.md
self-hosted-runner:
labels:
# Blacksmith CI runners
- blacksmith-4vcpu-ubuntu-2404
- blacksmith-8vcpu-ubuntu-2404
- blacksmith-16vcpu-ubuntu-2404
- blacksmith-32vcpu-ubuntu-2404
+13
View File
@@ -0,0 +1,13 @@
name: Setup Bun
description: Install the pinned Bun runtime and workspace dependencies.
runs:
using: composite
steps:
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
with:
bun-version: 1.3.10
- name: Install dependencies
shell: bash
run: bun install --frozen-lockfile
@@ -0,0 +1,16 @@
name: clawhub-codeql-actions-security
disable-default-queries: true
queries:
- uses: security-extended
query-filters:
- include:
precision:
- high
- very-high
tags contain: security
paths:
- .github/workflows
@@ -0,0 +1,70 @@
name: clawhub-codeql-backend-api-security
disable-default-queries: true
queries:
- uses: security-extended
query-filters:
- include:
precision:
- high
- very-high
tags contain: security
security-severity: /([7-9]|10)\.(\d)+/
paths:
- convex/auth.config.ts
- convex/auth.ts
- convex/http.ts
- convex/httpApi.ts
- convex/httpApiV1
- convex/packagePublishTokens.ts
- convex/packages.ts
- convex/publishers.ts
- convex/rateLimits.ts
- convex/skills.ts
- convex/skillTransfers.ts
- convex/tokens.ts
- convex/uploads.ts
- convex/vt.ts
- convex/webhooks.ts
- convex/lib/access.ts
- convex/lib/apiTokenAuth.ts
- convex/lib/githubActionsOidc.ts
- convex/lib/httpHeaders.ts
- convex/lib/httpRateLimit.ts
- convex/lib/httpUtils.ts
- convex/lib/manualOverrides.ts
- convex/lib/moderation.ts
- convex/lib/moderationEngine.ts
- convex/lib/moderationReasonCodes.ts
- convex/lib/packageRegistry.ts
- convex/lib/packageSecurity.ts
- convex/lib/publishers.ts
- convex/lib/publishLimits.ts
- convex/lib/reporting.ts
- convex/lib/securityPrompt.ts
- convex/lib/skillPublish.ts
- convex/lib/skillSafety.ts
- convex/lib/staticPublishScan.ts
- convex/lib/tokens.ts
- convex/lib/webhooks.ts
paths-ignore:
- "**/node_modules"
- "**/coverage"
- "**/dist"
- "**/dist/**"
- "**/*.generated.ts"
- "**/*.bundle.js"
- "**/*.test.ts"
- "**/*.test.tsx"
- "**/*.e2e.test.ts"
- "**/*.e2e.test.tsx"
- "**/*test-support*"
- "**/*test-helper*"
- "**/*mock*"
- "**/*fixture*"
- "**/*bench*"
- "convex/_generated/**"
@@ -0,0 +1,56 @@
name: clawhub-codeql-cli-package-security
disable-default-queries: true
queries:
- uses: security-extended
query-filters:
- include:
precision:
- high
- very-high
tags contain: security
security-severity: /([7-9]|10)\.(\d)+/
paths:
- packages/clawhub/src/browserAuth.ts
- packages/clawhub/src/http.ts
- packages/clawhub/src/cli/adminHelp.ts
- packages/clawhub/src/cli/authToken.ts
- packages/clawhub/src/cli/clawdbotConfig.ts
- packages/clawhub/src/cli/commands/auth.ts
- packages/clawhub/src/cli/commands/delete.ts
- packages/clawhub/src/cli/commands/github.ts
- packages/clawhub/src/cli/commands/moderation.ts
- packages/clawhub/src/cli/commands/ownership.ts
- packages/clawhub/src/cli/commands/packages.ts
- packages/clawhub/src/cli/commands/publish.ts
- packages/clawhub/src/cli/commands/transfer.ts
- packages/clawhub/src/schema/openclawContract.ts
- packages/clawhub/src/schema/packages.ts
- packages/clawhub/src/schema/routes.ts
- packages/clawhub/src/schema/schemas.ts
- packages/clawhub/src/schema/textFiles.ts
- packages/schema/src/openclawContract.ts
- packages/schema/src/packages.ts
- packages/schema/src/routes.ts
- packages/schema/src/schemas.ts
- packages/schema/src/textFiles.ts
paths-ignore:
- "**/node_modules"
- "**/coverage"
- "**/dist"
- "**/dist/**"
- "**/*.generated.ts"
- "**/*.bundle.js"
- "**/*.test.ts"
- "**/*.test.tsx"
- "**/*.e2e.test.ts"
- "**/*.e2e.test.tsx"
- "**/*test-support*"
- "**/*test-helper*"
- "**/*mock*"
- "**/*fixture*"
- "**/*bench*"
@@ -0,0 +1,64 @@
name: clawhub-codeql-frontend-publish-security
disable-default-queries: true
queries:
- uses: security-extended
query-filters:
- include:
precision:
- high
- very-high
tags contain: security
security-severity: /([7-9]|10)\.(\d)+/
paths:
- src/components/DetailSecuritySummary.tsx
- src/components/MarkdownPreview.tsx
- src/components/PackageSourceChooser.tsx
- src/components/SecurityAuditPage.tsx
- src/components/SkillSecurityScanResults.tsx
- src/components/securityAuditModel.ts
- src/lib/authErrorMessage.ts
- src/lib/packageApi.ts
- src/lib/packageUpload.ts
- src/lib/pluginPublishPrefill.ts
- src/lib/rehypeProxyImages.ts
- src/lib/roles.ts
- src/lib/uploadFiles.ts
- src/lib/uploadUtils.ts
- src/lib/useAuthError.ts
- src/lib/useAuthStatus.ts
- src/routes/admin.tsx
- src/routes/cli/auth.tsx
- src/routes/packages/new.tsx
- src/routes/plugins/publish.tsx
- src/routes/publish-plugin.tsx
- src/routes/publish-skill.tsx
- src/routes/skills/publish.tsx
- src/routes/upload.tsx
- src/routes/upload
- src/routes/$owner/$slug/security-audit.tsx
- src/routes/$owner/$slug/security
- src/routes/plugins/$name/security-audit.tsx
- src/routes/plugins/$name/security
- src/routes/plugins/$scope/$name/security-audit.tsx
- src/routes/plugins/$scope/$name/security
paths-ignore:
- "**/node_modules"
- "**/coverage"
- "**/dist"
- "**/dist/**"
- "**/*.generated.ts"
- "**/*.bundle.js"
- "**/*.test.ts"
- "**/*.test.tsx"
- "**/*.e2e.test.ts"
- "**/*.e2e.test.tsx"
- "**/*test-support*"
- "**/*test-helper*"
- "**/*mock*"
- "**/*fixture*"
- "**/*bench*"
@@ -0,0 +1,38 @@
name: clawhub-codeql-repository-automation-security
disable-default-queries: true
queries:
- uses: security-extended
query-filters:
- include:
precision:
- high
- very-high
tags contain: security
security-severity: /([7-9]|10)\.(\d)+/
paths:
- scripts/clawhub-cli-npm-release-check.mjs
- scripts/github
- scripts/verify-convex-contract.ts
- scripts/copy-og-assets.ts
- scripts/check-peer-deps.ts
paths-ignore:
- "**/node_modules"
- "**/coverage"
- "**/dist"
- "**/dist/**"
- "**/*.generated.ts"
- "**/*.bundle.js"
- "**/*.test.ts"
- "**/*.test.tsx"
- "**/*.e2e.test.ts"
- "**/*.e2e.test.tsx"
- "**/*test-support*"
- "**/*test-helper*"
- "**/*mock*"
- "**/*fixture*"
- "**/*bench*"
+47
View File
@@ -0,0 +1,47 @@
version: 2
updates:
- package-ecosystem: "bun"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "09:00"
timezone: "America/Los_Angeles"
# Preserve the old total Bun capacity: 10 general updates plus the
# dedicated 3-PR Plugin Inspector queue that cannot remain as a duplicate
# root Bun config.
open-pull-requests-limit: 13
ignore:
- dependency-name: "@auth/core"
update-types:
- "version-update:semver-minor"
- "version-update:semver-major"
- dependency-name: "undici"
update-types:
- "version-update:semver-major"
groups:
plugin-inspector:
patterns:
- "@openclaw/plugin-inspector"
production-minor-and-patch:
dependency-type: "production"
update-types:
- "minor"
- "patch"
development-minor-and-patch:
dependency-type: "development"
update-types:
- "minor"
- "patch"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "09:00"
timezone: "America/Los_Angeles"
groups:
github-actions:
patterns:
- "*"
+39
View File
@@ -0,0 +1,39 @@
## Summary
- What changed:
- Why:
## Linked Issue
- Closes #
- Related #
## Screenshots
For website/UI changes, attach screenshots or recordings from the real app. Include mobile/narrow views when layout changes.
- [ ] Screenshots/recordings attached, or `N/A`
## Behavioural Proof
Describe how you verified the user-facing behavior. For UI changes, include the path tested and what changed on screen. For backend/API changes, include the request, command, or scenario that proves the behavior.
- [ ] Behavioural proof included, or `N/A`
## Security / Trust Impact
- [ ] No security/trust impact
- [ ] Security/trust impact explained
## Data / Deploy Impact
- [ ] No data/deploy impact
- [ ] Data/deploy impact explained
## Verification
- [ ] `bun run ci:static`
- [ ] Focused tests for touched behavior:
- [ ] `bun run ci:unit` or `N/A` for docs/config-only:
- [ ] Broader gate when required (`ci:types-build`, `ci:packages`, `ci:e2e-http`, `ci:playwright-smoke`, `test:pw:local-auth`, `proof:ui`):
- [ ] Other:
+59
View File
@@ -0,0 +1,59 @@
name: Auto response
on:
issues:
types: [opened, edited, labeled]
issue_comment:
types: [created]
pull_request_target: # zizmor: ignore[dangerous-triggers] trusted base checkout only; no untrusted PR code execution
types: [opened, edited, synchronize, reopened, labeled]
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.issue.number || github.ref || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request_target' }}
permissions: {}
jobs:
auto-response:
permissions:
contents: read
issues: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.sha }}
persist-credentials: false
- uses: actions/create-github-app-token@v3
id: app-token
continue-on-error: true
with:
app-id: "2729701"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/create-github-app-token@v3
id: app-token-fallback
continue-on-error: true
if: steps.app-token.outcome == 'failure'
with:
app-id: "2971289"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
- name: Run Barnacle auto-response
uses: actions/github-script@v9
with:
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token || github.token }}
script: |
const { pathToFileURL } = require("node:url");
const moduleUrl = pathToFileURL(
`${process.env.GITHUB_WORKSPACE}/scripts/github/barnacle-auto-response.mjs`,
);
const { runBarnacleAutoResponse } = await import(moduleUrl.href);
await runBarnacleAutoResponse({ github, context, core });
+80
View File
@@ -0,0 +1,80 @@
name: Crabbox Testbox Backend
on:
workflow_dispatch:
inputs:
testbox_id:
type: string
description: "Testbox session ID"
required: true
permissions:
contents: read
env:
BUN_VERSION: "1.3.10"
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
check:
name: "check"
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 30
steps:
- name: Begin Testbox
uses: useblacksmith/begin-testbox@233448af4bfdc6fca509a7f0974411ac6d8a8043
with:
testbox_id: ${{ inputs.testbox_id }}
- uses: actions/checkout@v6
with:
fetch-depth: 50
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
with:
bun-version: ${{ env.BUN_VERSION }}
- name: Restore Bun install cache
id: bun-cache
uses: actions/cache/restore@v5
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-${{ env.BUN_VERSION }}-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-${{ env.BUN_VERSION }}-
- name: Install
run: bun install --frozen-lockfile
- name: Save Bun install cache
if: steps.bun-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v5
continue-on-error: true
with:
path: ~/.bun/install/cache
key: ${{ steps.bun-cache.outputs.cache-primary-key }}
- name: Prepare Testbox shell
shell: bash
run: |
set -euo pipefail
git fetch --no-tags --depth=50 origin "+refs/heads/main:refs/remotes/origin/main"
bun_bin="$(command -v bun)"
sudo ln -sf "$bun_bin" /usr/local/bin/bun
if command -v bunx >/dev/null 2>&1; then
sudo ln -sf "$(command -v bunx)" /usr/local/bin/bunx
fi
node_bin="$(dirname "$(node -p 'process.execPath')")"
sudo ln -sf "$node_bin/node" /usr/local/bin/node
sudo ln -sf "$node_bin/npm" /usr/local/bin/npm
sudo ln -sf "$node_bin/npx" /usr/local/bin/npx
- name: Run Testbox
uses: useblacksmith/run-testbox@3f60ff9ceb2c10c3feefa87dc0c6490cffae059d
if: always()
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
+185 -30
View File
@@ -4,45 +4,200 @@ on:
push:
branches: [main]
pull_request:
workflow_dispatch:
concurrency:
group: ci-${{ github.event_name == 'pull_request' && github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
env:
VITE_CONVEX_URL: https://example.invalid
jobs:
build:
runs-on: ubuntu-latest
static:
name: static
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: ./.github/actions/setup-bun
- name: Static checks
run: bun run ci:static
unit:
name: unit
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@e3914758a49697077f7bcd190d36582a61667aad
with:
bun-version: 1.3.10
- name: Install
run: bun install --frozen-lockfile
- name: Peer deps
run: bun run check:peers
- name: Lint
run: bun run lint
- name: Test
run: bun run test
env:
VITE_CONVEX_URL: https://example.invalid
- uses: ./.github/actions/setup-bun
- name: Coverage
run: bun run coverage
run: bun run ci:unit
packages:
name: packages
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-bun
- name: Package checks
run: bun run ci:packages
types-build:
name: types-build
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-bun
- name: Typecheck and build
run: bun run ci:types-build
e2e-http:
name: e2e-http
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 20
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-bun
- name: HTTP e2e
run: bun run ci:e2e-http
playwright-smoke:
name: playwright-smoke
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 25
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-bun
- name: Cache Playwright browsers
uses: actions/cache@v5
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-playwright-
- name: Install Playwright browsers
run: bunx playwright install chromium
- name: Browser e2e
run: bun run ci:playwright-smoke
- name: Upload Playwright report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v7
with:
name: playwright-report
path: playwright-report/
if-no-files-found: ignore
playwright-local-auth-shard:
name: playwright-local-auth / ${{ matrix.name }}
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 30
strategy:
fail-fast: false
max-parallel: 3
matrix:
include:
- name: delete-account
spec: e2e/local-auth/delete-account-resources.pw.test.ts
- name: delete-org
spec: e2e/local-auth/delete-org-resources.pw.test.ts
- name: header-profile-link
spec: e2e/local-auth/header-profile-link.pw.test.ts
- name: malicious-skill-ban
spec: e2e/local-auth/malicious-skill-ban-flow.pw.test.ts
- name: manage-context-proof
spec: e2e/local-auth/manage-context-proof.pw.test.ts
- name: plugin-inspector-findings
spec: e2e/local-auth/plugin-inspector-findings.pw.test.ts
- name: publish-generated-card
spec: e2e/local-auth/publish-skill-lifecycle.pw.test.ts
grep: publishing a skill queues scan
- name: publish-new-version
spec: e2e/local-auth/publish-skill-lifecycle.pw.test.ts
grep: skill publishers can create a skill
- name: skill-star-sync
spec: e2e/local-auth/skill-star-sync.pw.test.ts
- name: version-delete
spec: e2e/local-auth/version-delete.pw.test.ts
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-bun
- name: Cache Playwright browsers
uses: actions/cache@v5
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-playwright-
- name: Install Playwright browsers
run: bunx playwright install chromium
- name: Local-auth browser e2e
env:
VITE_CONVEX_URL: https://example.invalid
- name: ClawHub CLI Verify
run: bun run --cwd packages/clawhub verify
- name: Typecheck
PLAYWRIGHT_GREP: ${{ matrix.grep || '' }}
PLAYWRIGHT_SPEC: ${{ matrix.spec }}
run: |
bunx tsc --noEmit
bunx tsc -p packages/schema/tsconfig.json --noEmit
bunx tsc -p packages/clawhub/tsconfig.json --noEmit
set -euo pipefail
args=(--project=chromium "$PLAYWRIGHT_SPEC")
if [[ -n "$PLAYWRIGHT_GREP" ]]; then
args+=(--grep "$PLAYWRIGHT_GREP")
fi
bun run test:pw:local-auth -- "${args[@]}"
- name: Build
run: bun run build
- name: Upload Playwright report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v7
with:
name: playwright-local-auth-report-${{ matrix.name }}
path: playwright-report/
if-no-files-found: ignore
playwright-local-auth:
name: playwright-local-auth
runs-on: ubuntu-latest
needs: playwright-local-auth-shard
if: ${{ always() }}
timeout-minutes: 5
steps:
- name: Check local-auth shards
env:
LOCAL_AUTH_RESULT: ${{ needs.playwright-local-auth-shard.result }}
run: |
if [[ "$LOCAL_AUTH_RESULT" != "success" ]]; then
echo "playwright-local-auth shards finished with result: $LOCAL_AUTH_RESULT"
exit 1
fi
echo "playwright-local-auth shards passed."
@@ -0,0 +1,326 @@
name: ClawHub CLI GitHub Release
on:
workflow_dispatch:
inputs:
tag:
description: Release tag to create or repair, for example v0.17.0
required: true
type: string
main_run_id:
description: Optional successful main CI run id to include in release proof
required: false
type: string
preflight_run_id:
description: Optional successful CLI npm preflight run id to include in release proof
required: false
type: string
publish_run_id:
description: Optional successful CLI npm publish run id to include in release proof
required: false
type: string
update_existing:
description: Update an existing GitHub Release instead of failing
required: true
default: false
type: boolean
concurrency:
group: clawhub-cli-github-release-${{ inputs.tag }}
cancel-in-progress: false
permissions: {}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
NODE_VERSION: "24.x"
jobs:
create_or_update_github_release:
runs-on: ubuntu-latest
permissions:
actions: read
contents: write
steps:
- name: Checkout release tooling
uses: actions/checkout@v6
with:
ref: ${{ github.ref }}
path: release-tools
- name: Checkout release tag
uses: actions/checkout@v6
with:
ref: refs/tags/${{ inputs.tag }}
fetch-depth: 0
path: release
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
registry-url: https://registry.npmjs.org
- name: Validate release tag and package metadata
env:
RELEASE_TAG: ${{ inputs.tag }}
RELEASE_MAIN_REF: origin/main
run: |
set -euo pipefail
RELEASE_SHA="$(git rev-parse HEAD)"
echo "RELEASE_SHA=$RELEASE_SHA" >> "$GITHUB_ENV"
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
if ! git merge-base --is-ancestor "$RELEASE_SHA" "$RELEASE_MAIN_REF"; then
echo "Tagged commit ${RELEASE_SHA} is not contained in ${RELEASE_MAIN_REF}." >&2
exit 1
fi
node --input-type=module <<'EOF'
import { readFileSync } from "node:fs";
const releaseTag = process.env.RELEASE_TAG ?? "";
const pkg = JSON.parse(readFileSync("./packages/clawhub/package.json", "utf8"));
const version = String(pkg.version ?? "").trim();
const errors = [];
if (pkg.name !== "clawhub") {
errors.push(`packages/clawhub/package.json name must be "clawhub"; found "${pkg.name ?? ""}".`);
}
if (!/^\d+\.\d+\.\d+$/.test(version)) {
errors.push(`packages/clawhub/package.json version must be stable semver (X.Y.Z); found "${version || "<missing>"}".`);
}
if (!/^v\d+\.\d+\.\d+$/.test(releaseTag)) {
errors.push(`Release tag must match vX.Y.Z; found "${releaseTag || "<missing>"}".`);
}
if (releaseTag !== `v${version}`) {
errors.push(`Release tag ${releaseTag} does not match packages/clawhub/package.json version ${version}; expected v${version}.`);
}
if (errors.length > 0) {
for (const error of errors) console.error(error);
process.exit(1);
}
console.log(`Release metadata OK for clawhub@${version} (${releaseTag}).`);
EOF
working-directory: release
- name: Resolve release metadata
env:
RELEASE_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
PACKAGE_VERSION="$(node --input-type=module <<'EOF'
import { readFileSync } from "node:fs";
const pkg = JSON.parse(readFileSync("./packages/clawhub/package.json", "utf8"));
process.stdout.write(String(pkg.version ?? "").trim());
EOF
)"
NPM_DIST_JSON=""
for attempt in {1..12}; do
if NPM_DIST_JSON="$(npm view "clawhub@${PACKAGE_VERSION}" dist.tarball dist.integrity --json 2>/tmp/npm-view-error)" && [[ -n "$NPM_DIST_JSON" ]]; then
break
fi
if [[ "$attempt" == "12" ]]; then
cat /tmp/npm-view-error >&2 || true
exit 1
fi
sleep 5
done
NPM_TARBALL="$(NPM_DIST_JSON="$NPM_DIST_JSON" node --input-type=module <<'EOF'
const dist = JSON.parse(process.env.NPM_DIST_JSON ?? "{}");
process.stdout.write(String(dist["dist.tarball"] ?? ""));
EOF
)"
NPM_INTEGRITY="$(NPM_DIST_JSON="$NPM_DIST_JSON" node --input-type=module <<'EOF'
const dist = JSON.parse(process.env.NPM_DIST_JSON ?? "{}");
process.stdout.write(String(dist["dist.integrity"] ?? ""));
EOF
)"
if [[ -z "$NPM_TARBALL" || -z "$NPM_INTEGRITY" ]]; then
echo "npm dist metadata for clawhub@${PACKAGE_VERSION} is incomplete." >&2
exit 1
fi
{
echo "PACKAGE_VERSION=$PACKAGE_VERSION"
echo "NPM_PACKAGE_URL=https://www.npmjs.com/package/clawhub/v/${PACKAGE_VERSION}"
echo "NPM_TARBALL_URL=$NPM_TARBALL"
echo "NPM_INTEGRITY=$NPM_INTEGRITY"
echo "RELEASE_TITLE=clawhub ${PACKAGE_VERSION}"
} >> "$GITHUB_ENV"
working-directory: release
- name: Resolve proof workflow run URLs
env:
GH_TOKEN: ${{ github.token }}
MAIN_RUN_ID: ${{ inputs.main_run_id }}
PREFLIGHT_RUN_ID: ${{ inputs.preflight_run_id }}
PUBLISH_RUN_ID: ${{ inputs.publish_run_id }}
run: |
set -euo pipefail
resolve_run_url() {
local env_name="$1"
local run_id="$2"
local expected_workflow="$3"
local expected_event="$4"
local expected_branch="$5"
if [[ -z "$run_id" ]]; then
return 0
fi
local run_json
run_json="$(gh run view "$run_id" --repo "$GITHUB_REPOSITORY" --json conclusion,event,headBranch,headSha,url,workflowName)"
RUN_JSON="$run_json" RUN_ID="$run_id" EXPECTED_WORKFLOW="$expected_workflow" EXPECTED_EVENT="$expected_event" EXPECTED_BRANCH="$expected_branch" node --input-type=module <<'EOF'
const run = JSON.parse(process.env.RUN_JSON);
const expectedWorkflow = process.env.EXPECTED_WORKFLOW;
if (expectedWorkflow && run.workflowName !== expectedWorkflow) {
console.error(`Run ${process.env.RUN_ID} must be ${expectedWorkflow}; got ${run.workflowName ?? "<missing>"}.`);
process.exit(1);
}
if (run.conclusion !== "success") {
console.error(`Run ${process.env.RUN_ID} must have conclusion=success; got ${run.conclusion ?? "<missing>"}.`);
process.exit(1);
}
if (run.headSha !== process.env.RELEASE_SHA) {
console.error(`Run ${process.env.RUN_ID} must use release SHA ${process.env.RELEASE_SHA}; got ${run.headSha ?? "<missing>"}.`);
process.exit(1);
}
if (process.env.EXPECTED_EVENT && run.event !== process.env.EXPECTED_EVENT) {
console.error(`Run ${process.env.RUN_ID} must have event=${process.env.EXPECTED_EVENT}; got ${run.event ?? "<missing>"}.`);
process.exit(1);
}
if (process.env.EXPECTED_BRANCH && run.headBranch !== process.env.EXPECTED_BRANCH) {
console.error(`Run ${process.env.RUN_ID} must have headBranch=${process.env.EXPECTED_BRANCH}; got ${run.headBranch ?? "<missing>"}.`);
process.exit(1);
}
process.stdout.write(run.url);
EOF
echo "${env_name}=$(RUN_JSON="$run_json" RUN_ID="$run_id" EXPECTED_WORKFLOW="$expected_workflow" node --input-type=module <<'EOF'
const run = JSON.parse(process.env.RUN_JSON);
process.stdout.write(run.url);
EOF
)" >> "$GITHUB_ENV"
}
resolve_run_url MAIN_RUN_URL "$MAIN_RUN_ID" "CI" "" ""
resolve_run_url PREFLIGHT_RUN_URL "$PREFLIGHT_RUN_ID" "ClawHub CLI NPM Release" "workflow_dispatch" "main"
resolve_run_url PUBLISH_RUN_URL "$PUBLISH_RUN_ID" "ClawHub CLI NPM Release" "workflow_dispatch" "main"
- name: Verify preflight proof artifact
if: ${{ inputs.preflight_run_id != '' }}
env:
GH_TOKEN: ${{ github.token }}
PREFLIGHT_RUN_ID: ${{ inputs.preflight_run_id }}
RELEASE_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
PROOF_DIR="$RUNNER_TEMP/clawhub-cli-github-release-preflight-proof"
rm -rf "$PROOF_DIR"
mkdir -p "$PROOF_DIR"
gh run download "$PREFLIGHT_RUN_ID" \
--repo "$GITHUB_REPOSITORY" \
--name "clawhub-cli-npm-preflight-${RELEASE_TAG}" \
--dir "$PROOF_DIR"
if [[ "$(tr -d '\r\n' < "$PROOF_DIR/release-tag.txt")" != "$RELEASE_TAG" ]]; then
echo "Preflight artifact tag does not match ${RELEASE_TAG}." >&2
exit 1
fi
if [[ "$(tr -d '\r\n' < "$PROOF_DIR/release-sha.txt")" != "$RELEASE_SHA" ]]; then
echo "Preflight artifact SHA does not match ${RELEASE_SHA}." >&2
exit 1
fi
if [[ "$(tr -d '\r\n' < "$PROOF_DIR/package-version.txt")" != "$PACKAGE_VERSION" ]]; then
echo "Preflight artifact version does not match ${PACKAGE_VERSION}." >&2
exit 1
fi
- name: Verify publish proof artifact
if: ${{ inputs.publish_run_id != '' }}
env:
GH_TOKEN: ${{ github.token }}
PUBLISH_RUN_ID: ${{ inputs.publish_run_id }}
RELEASE_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
PROOF_DIR="$RUNNER_TEMP/clawhub-cli-github-release-publish-proof"
rm -rf "$PROOF_DIR"
mkdir -p "$PROOF_DIR"
gh run download "$PUBLISH_RUN_ID" \
--repo "$GITHUB_REPOSITORY" \
--name "clawhub-cli-npm-publish-${RELEASE_TAG}" \
--dir "$PROOF_DIR"
if [[ "$(tr -d '\r\n' < "$PROOF_DIR/release-tag.txt")" != "$RELEASE_TAG" ]]; then
echo "Publish artifact tag does not match ${RELEASE_TAG}." >&2
exit 1
fi
if [[ "$(tr -d '\r\n' < "$PROOF_DIR/release-sha.txt")" != "$RELEASE_SHA" ]]; then
echo "Publish artifact SHA does not match ${RELEASE_SHA}." >&2
exit 1
fi
if [[ "$(tr -d '\r\n' < "$PROOF_DIR/package-version.txt")" != "$PACKAGE_VERSION" ]]; then
echo "Publish artifact version does not match ${PACKAGE_VERSION}." >&2
exit 1
fi
if [[ "$(tr -d '\r\n' < "$PROOF_DIR/preflight-only.txt")" != "false" ]]; then
echo "Publish artifact must come from a real publish run." >&2
exit 1
fi
if [[ "$(tr -d '\r\n' < "$PROOF_DIR/npm-tarball-url.txt")" != "$NPM_TARBALL_URL" ]]; then
echo "Publish artifact tarball URL does not match npm metadata." >&2
exit 1
fi
if [[ "$(tr -d '\r\n' < "$PROOF_DIR/npm-integrity.txt")" != "$NPM_INTEGRITY" ]]; then
echo "Publish artifact integrity does not match npm metadata." >&2
exit 1
fi
- name: Build release notes
env:
RELEASE_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
node ../release-tools/scripts/extract-changelog-release.mjs --tag "$RELEASE_TAG" --changelog CHANGELOG.md > ../release-body.md
{
echo
echo "### Release Proof"
echo
echo "- npm: ${NPM_PACKAGE_URL}"
echo "- tarball: ${NPM_TARBALL_URL}"
echo "- integrity: ${NPM_INTEGRITY}"
if [[ -n "${MAIN_RUN_URL:-}" ]]; then
echo "- main CI: ${MAIN_RUN_URL}"
fi
if [[ -n "${PREFLIGHT_RUN_URL:-}" ]]; then
echo "- npm preflight: ${PREFLIGHT_RUN_URL}"
fi
if [[ -n "${PUBLISH_RUN_URL:-}" ]]; then
echo "- npm publish: ${PUBLISH_RUN_URL}"
fi
} >> ../release-body.md
working-directory: release
- name: Create or update GitHub Release
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ inputs.tag }}
UPDATE_EXISTING: ${{ inputs.update_existing }}
run: |
set -euo pipefail
if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
if [[ "$UPDATE_EXISTING" != "true" ]]; then
echo "GitHub Release ${RELEASE_TAG} already exists. Rerun with update_existing=true to repair it." >&2
exit 1
fi
gh release edit "$RELEASE_TAG" \
--repo "$GITHUB_REPOSITORY" \
--title "$RELEASE_TITLE" \
--notes-file release-body.md
else
gh release create "$RELEASE_TAG" \
--repo "$GITHUB_REPOSITORY" \
--title "$RELEASE_TITLE" \
--notes-file release-body.md
fi
+130 -2
View File
@@ -21,6 +21,8 @@ concurrency:
group: clawhub-cli-npm-release-${{ inputs.tag }}
cancel-in-progress: false
permissions: {}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
NODE_VERSION: "24.x"
@@ -45,6 +47,12 @@ jobs:
ref: refs/tags/${{ inputs.tag }}
fetch-depth: 0
- name: Checkout release tooling
uses: actions/checkout@v6
with:
ref: ${{ github.ref }}
path: release-tools
- name: Setup Node
uses: actions/setup-node@v6
with:
@@ -52,7 +60,7 @@ jobs:
registry-url: https://registry.npmjs.org
- name: Setup Bun
uses: oven-sh/setup-bun@e3914758a49697077f7bcd190d36582a61667aad
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
with:
bun-version: ${{ env.BUN_VERSION }}
@@ -106,6 +114,11 @@ jobs:
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
node scripts/clawhub-cli-npm-release-check.mjs
- name: Validate GitHub Release notes
env:
RELEASE_TAG: ${{ inputs.tag }}
run: node release-tools/scripts/extract-changelog-release.mjs --tag "$RELEASE_TAG" >/tmp/clawhub-cli-release-notes.md
- name: Verify CLI package
run: bun run --cwd "$PACKAGE_DIR" verify
@@ -181,7 +194,7 @@ jobs:
environment: npm-release
permissions:
actions: read
contents: read
contents: write
id-token: write
steps:
- name: Checkout
@@ -190,6 +203,12 @@ jobs:
ref: refs/tags/${{ inputs.tag }}
fetch-depth: 0
- name: Checkout release tooling
uses: actions/checkout@v6
with:
ref: ${{ github.ref }}
path: release-tools
- name: Setup Node
uses: actions/setup-node@v6
with:
@@ -233,6 +252,7 @@ jobs:
run: |
set -euo pipefail
RUN_JSON="$(gh run view "$PREFLIGHT_RUN_ID" --repo "$GITHUB_REPOSITORY" --json workflowName,headBranch,event,conclusion,url)"
# shellcheck disable=SC2016
printf '%s' "$RUN_JSON" | node --input-type=module -e 'const chunks=[]; process.stdin.on("data", (chunk) => chunks.push(chunk)); process.stdin.on("end", () => { const run = JSON.parse(Buffer.concat(chunks).toString("utf8")); const checks = [["workflowName", "ClawHub CLI NPM Release"], ["headBranch", "main"], ["event", "workflow_dispatch"], ["conclusion", "success"]]; for (const [key, expected] of checks) { if (run[key] !== expected) { console.error(`Referenced npm preflight run ${process.env.PREFLIGHT_RUN_ID} must have ${key}=${expected}, got ${run[key] ?? "<missing>"}.`); process.exit(1); } } console.log(`Using npm preflight run ${process.env.PREFLIGHT_RUN_ID}: ${run.url}`); });'
- name: Download prepared npm tarball
@@ -255,6 +275,11 @@ jobs:
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
node scripts/clawhub-cli-npm-release-check.mjs
- name: Validate GitHub Release notes
env:
RELEASE_TAG: ${{ inputs.tag }}
run: node release-tools/scripts/extract-changelog-release.mjs --tag "$RELEASE_TAG" >/tmp/clawhub-cli-release-notes.md
- name: Verify prepared tarball provenance
env:
RELEASE_TAG: ${{ inputs.tag }}
@@ -312,3 +337,106 @@ jobs:
publish_target="./${publish_target}"
fi
bash scripts/clawhub-cli-npm-publish.sh --publish "${publish_target}"
- name: Resolve npm release metadata
run: |
set -euo pipefail
PACKAGE_VERSION="$(node --input-type=module <<'EOF'
import { readFileSync } from "node:fs";
const pkg = JSON.parse(readFileSync(`./${process.env.PACKAGE_DIR}/package.json`, "utf8"));
process.stdout.write(String(pkg.version ?? "").trim());
EOF
)"
NPM_DIST_JSON=""
for attempt in {1..12}; do
if NPM_DIST_JSON="$(npm view "clawhub@${PACKAGE_VERSION}" dist.tarball dist.integrity --json 2>/tmp/npm-view-error)" && [[ -n "$NPM_DIST_JSON" ]]; then
break
fi
if [[ "$attempt" == "12" ]]; then
cat /tmp/npm-view-error >&2 || true
exit 1
fi
sleep 5
done
NPM_TARBALL="$(NPM_DIST_JSON="$NPM_DIST_JSON" node --input-type=module <<'EOF'
const dist = JSON.parse(process.env.NPM_DIST_JSON ?? "{}");
process.stdout.write(String(dist["dist.tarball"] ?? ""));
EOF
)"
NPM_INTEGRITY="$(NPM_DIST_JSON="$NPM_DIST_JSON" node --input-type=module <<'EOF'
const dist = JSON.parse(process.env.NPM_DIST_JSON ?? "{}");
process.stdout.write(String(dist["dist.integrity"] ?? ""));
EOF
)"
if [[ -z "$NPM_TARBALL" || -z "$NPM_INTEGRITY" ]]; then
echo "npm dist metadata for clawhub@${PACKAGE_VERSION} is incomplete." >&2
exit 1
fi
{
echo "PACKAGE_VERSION=$PACKAGE_VERSION"
echo "NPM_PACKAGE_URL=https://www.npmjs.com/package/clawhub/v/${PACKAGE_VERSION}"
echo "NPM_TARBALL_URL=$NPM_TARBALL"
echo "NPM_INTEGRITY=$NPM_INTEGRITY"
echo "RELEASE_TITLE=clawhub ${PACKAGE_VERSION}"
} >> "$GITHUB_ENV"
- name: Write npm publish proof artifact
id: publish_proof
env:
RELEASE_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
PUBLISH_PROOF_DIR="$RUNNER_TEMP/clawhub-cli-npm-publish-proof"
rm -rf "$PUBLISH_PROOF_DIR"
mkdir -p "$PUBLISH_PROOF_DIR"
printf '%s\n' "$RELEASE_TAG" > "$PUBLISH_PROOF_DIR/release-tag.txt"
git rev-parse HEAD > "$PUBLISH_PROOF_DIR/release-sha.txt"
printf '%s\n' "$PACKAGE_VERSION" > "$PUBLISH_PROOF_DIR/package-version.txt"
printf '%s\n' "$NPM_TARBALL_URL" > "$PUBLISH_PROOF_DIR/npm-tarball-url.txt"
printf '%s\n' "$NPM_INTEGRITY" > "$PUBLISH_PROOF_DIR/npm-integrity.txt"
printf '%s\n' "$GITHUB_RUN_ID" > "$PUBLISH_PROOF_DIR/publish-run-id.txt"
printf '%s\n' "false" > "$PUBLISH_PROOF_DIR/preflight-only.txt"
echo "dir=$PUBLISH_PROOF_DIR" >> "$GITHUB_OUTPUT"
- name: Upload npm publish proof artifact
uses: actions/upload-artifact@v7
with:
name: clawhub-cli-npm-publish-${{ inputs.tag }}
path: ${{ steps.publish_proof.outputs.dir }}
if-no-files-found: error
- name: Build GitHub Release notes
env:
RELEASE_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
node release-tools/scripts/extract-changelog-release.mjs --tag "$RELEASE_TAG" > release-body.md
{
echo
echo "### Release Proof"
echo
echo "- npm: ${NPM_PACKAGE_URL}"
echo "- tarball: ${NPM_TARBALL_URL}"
echo "- integrity: ${NPM_INTEGRITY}"
echo "- npm preflight: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${{ inputs.preflight_run_id }}"
echo "- npm publish: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
} >> release-body.md
- name: Create or update GitHub Release
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
gh release edit "$RELEASE_TAG" \
--repo "$GITHUB_REPOSITORY" \
--title "$RELEASE_TITLE" \
--notes-file release-body.md
else
gh release create "$RELEASE_TAG" \
--repo "$GITHUB_REPOSITORY" \
--title "$RELEASE_TITLE" \
--notes-file release-body.md
fi
@@ -0,0 +1,57 @@
name: ClawHub Rescan Guidance
on:
issues:
types: [labeled]
workflow_dispatch:
inputs:
issue:
description: "Issue number to check"
required: true
type: string
permissions:
contents: read
issues: write
concurrency:
group: clawhub-rescan-guidance-${{ github.event.issue.number || github.event.inputs.issue }}
cancel-in-progress: false
jobs:
rescan-guidance:
runs-on: ubuntu-latest
if: "${{ github.event_name == 'workflow_dispatch' || github.event.label.name == 'r: rescan-guidance' }}"
env:
CLAWHUB_RESCAN_GUIDANCE_APPLY: "1"
ISSUE_NUMBER: ${{ github.event.issue.number || github.event.inputs.issue }}
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.sha }}
persist-credentials: false
- uses: actions/create-github-app-token@v3
id: app-token
continue-on-error: true
with:
app-id: "2729701"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/create-github-app-token@v3
id: app-token-fallback
continue-on-error: true
if: steps.app-token.outcome == 'failure'
with:
app-id: "2971289"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
- name: Comment when rescan guidance label is present
env:
GH_TOKEN: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token || github.token }}
run: |
node scripts/github/clawhub-rescan-auto-response.mjs \
--repo "$GITHUB_REPOSITORY" \
--issue "$ISSUE_NUMBER" \
--comment-for-labeled-issue \
--apply
+158
View File
@@ -0,0 +1,158 @@
name: ClawSweeper Dispatch
on:
issues:
types: [opened, reopened, edited, labeled, unlabeled]
issue_comment:
types: [created, edited]
pull_request_target: # zizmor: ignore[dangerous-triggers] maintainer-owned external dispatch; no checkout or untrusted PR code execution
types: [opened, reopened, synchronize, ready_for_review, edited, labeled, unlabeled]
permissions:
contents: read
concurrency:
group: clawsweeper-dispatch-${{ github.repository }}-${{ github.event.issue.number || github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ github.event.action == 'edited' || github.event.action == 'synchronize' || github.event.action == 'ready_for_review' }}
jobs:
dispatch:
runs-on: ubuntu-latest
if: ${{ github.event_name == 'issue_comment' || !(endsWith(github.actor, '[bot]') && (github.event.action == 'labeled' || github.event.action == 'unlabeled')) }}
env:
HAS_CLAWSWEEPER_APP_PRIVATE_KEY: ${{ secrets.CLAWSWEEPER_APP_PRIVATE_KEY != '' }}
CLAWSWEEPER_APP_CLIENT_ID: Iv23liOECG0slfuhz093
SUPERSEDES_IN_PROGRESS: ${{ (github.event.action == 'edited' || github.event.action == 'synchronize' || github.event.action == 'ready_for_review') && 'true' || 'false' }}
steps:
- name: Debounce bursty metadata events
if: ${{ github.event.action == 'labeled' || github.event.action == 'unlabeled' }}
run: sleep 20
- name: Create ClawSweeper dispatch token
id: token
if: ${{ env.HAS_CLAWSWEEPER_APP_PRIVATE_KEY == 'true' }}
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
with:
client-id: ${{ env.CLAWSWEEPER_APP_CLIENT_ID }}
private-key: ${{ secrets.CLAWSWEEPER_APP_PRIVATE_KEY }}
owner: openclaw
repositories: clawsweeper
permission-contents: write
- name: Create target comment token
id: target_token
if: ${{ github.event_name == 'issue_comment' && env.HAS_CLAWSWEEPER_APP_PRIVATE_KEY == 'true' }}
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
with:
client-id: ${{ env.CLAWSWEEPER_APP_CLIENT_ID }}
private-key: ${{ secrets.CLAWSWEEPER_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: ${{ github.event.repository.name }}
permission-issues: write
permission-pull-requests: read
- name: Dispatch exact ClawSweeper review
if: ${{ github.event_name == 'issues' || github.event_name == 'pull_request_target' }}
env:
GH_TOKEN: ${{ steps.token.outputs.token }}
TARGET_REPO: ${{ github.repository }}
ITEM_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number }}
ITEM_KIND: ${{ github.event_name == 'pull_request_target' && 'pull_request' || 'issue' }}
SOURCE_EVENT: ${{ github.event_name }}
SOURCE_ACTION: ${{ github.event.action }}
run: |
if [ -z "$GH_TOKEN" ]; then
echo "::notice::Skipping ClawSweeper dispatch because no dispatch credential is configured."
exit 0
fi
payload="$(jq -nc \
--arg target_repo "$TARGET_REPO" \
--argjson item_number "$ITEM_NUMBER" \
--arg item_kind "$ITEM_KIND" \
--arg source_event "$SOURCE_EVENT" \
--arg source_action "$SOURCE_ACTION" \
--argjson supersedes_in_progress "$SUPERSEDES_IN_PROGRESS" \
'{event_type:"clawsweeper_item",client_payload:{target_repo:$target_repo,item_number:$item_number,item_kind:$item_kind,source_event:$source_event,source_action:$source_action,supersedes_in_progress:$supersedes_in_progress}}')"
gh api repos/openclaw/clawsweeper/dispatches \
--method POST \
--input - <<< "$payload"
- name: Acknowledge and dispatch ClawSweeper comment
if: ${{ github.event_name == 'issue_comment' }}
env:
DISPATCH_TOKEN: ${{ steps.token.outputs.token }}
TARGET_TOKEN: ${{ steps.target_token.outputs.token }}
TARGET_REPO: ${{ github.repository }}
ITEM_NUMBER: ${{ github.event.issue.number }}
COMMENT_ID: ${{ github.event.comment.id }}
COMMENT_BODY: ${{ github.event.comment.body }}
AUTHOR_ASSOCIATION: ${{ github.event.comment.author_association }}
SOURCE_ACTION: ${{ github.event.action }}
run: |
set -euo pipefail
if [ -z "$DISPATCH_TOKEN" ]; then
echo "::notice::Skipping ClawSweeper comment dispatch because no ClawSweeper app token is configured."
exit 0
fi
body_file="$RUNNER_TEMP/clawsweeper-comment-body.txt"
printf '%s\n' "$COMMENT_BODY" > "$body_file"
if ! grep -Eiq '(^|[[:space:]])@(clawsweeper|openclaw-clawsweeper)\b(\[bot\])?|(^|[[:space:]])/(clawsweeper|review|automerge|autoclose)\b' "$body_file"; then
echo "No ClawSweeper command found in comment."
exit 0
fi
if [ -n "$TARGET_TOKEN" ]; then
err="$(mktemp)"
if GH_TOKEN="$TARGET_TOKEN" gh api -X POST \
-H "Accept: application/vnd.github+json" \
"repos/$TARGET_REPO/issues/comments/$COMMENT_ID/reactions" \
-f content="eyes" 2>"$err" >/dev/null; then
echo "Acknowledged ClawSweeper command comment."
elif grep -qi "HTTP 422\\|already exists" "$err"; then
echo "ClawSweeper command comment already acknowledged."
else
cat "$err" >&2
echo "::warning::Could not acknowledge ClawSweeper command comment."
fi
rm -f "$err"
else
echo "::notice::Skipping ClawSweeper comment acknowledgement because no target token is configured."
fi
status_comment_id=""
if [ -n "$TARGET_TOKEN" ]; then
case "$AUTHOR_ASSOCIATION" in
OWNER|MEMBER|COLLABORATOR)
status_body="$(printf '%s\n' \
"<!-- clawsweeper-command-ack:$COMMENT_ID -->" \
"ClawSweeper picked this up." \
"" \
"Command router queued. I will update this comment with the next step.")"
status_payload="$(jq -nc --arg body "$status_body" '{body:$body}')"
status_err="$(mktemp)"
if status_response="$(GH_TOKEN="$TARGET_TOKEN" gh api \
"repos/$TARGET_REPO/issues/$ITEM_NUMBER/comments" \
--method POST \
--input - <<< "$status_payload" 2>"$status_err")"; then
status_comment_id="$(jq -r '.id // empty' <<< "$status_response")"
else
cat "$status_err" >&2
echo "::warning::Could not create ClawSweeper queued status comment; dispatching command router without one."
fi
rm -f "$status_err"
;;
esac
fi
payload="$(jq -nc \
--arg target_repo "$TARGET_REPO" \
--argjson item_number "$ITEM_NUMBER" \
--argjson comment_id "$COMMENT_ID" \
--arg status_comment_id "$status_comment_id" \
--arg source_event "issue_comment" \
--arg source_action "$SOURCE_ACTION" \
'{event_type:"clawsweeper_comment",client_payload:({target_repo:$target_repo,item_number:$item_number,comment_id:$comment_id,source_event:$source_event,source_action:$source_action,max_comments:"1"} + (if $status_comment_id != "" then {status_comment_id:($status_comment_id|tonumber)} else {} end))}')"
if GH_TOKEN="$DISPATCH_TOKEN" gh api repos/openclaw/clawsweeper/dispatches \
--method POST \
--input - <<< "$payload"; then
echo "Dispatched ClawSweeper comment router."
else
echo "::warning::Skipping ClawSweeper comment dispatch because the configured credential could not dispatch to openclaw/clawsweeper."
fi
+100
View File
@@ -0,0 +1,100 @@
name: CodeQL Light
on:
workflow_dispatch:
inputs:
profile:
description: CodeQL light profile to run
required: false
default: all
type: choice
options:
- all
- backend-api
- frontend-publish
- cli-package
- repository-automation
- actions
push:
branches: [main]
paths:
- ".github/codeql/**"
- ".github/workflows/**"
- "convex/**"
- "packages/clawhub/**"
- "packages/schema/**"
- "scripts/**"
- "src/**"
- "bun.lock"
- "package.json"
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths:
- ".github/codeql/**"
- ".github/workflows/**"
- "convex/**"
- "packages/clawhub/**"
- "packages/schema/**"
- "scripts/**"
- "src/**"
- "bun.lock"
- "package.json"
schedule:
- cron: "17 7 * * *"
concurrency:
group: codeql-light-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name == 'pull_request' && github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
permissions:
actions: read
contents: read
security-events: write
jobs:
analyze:
name: Analyze (${{ matrix.category }})
if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.draft }}
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
include:
- language: javascript-typescript
category: backend-api
config_file: ./.github/codeql/codeql-backend-api-security.yml
- language: javascript-typescript
category: frontend-publish
config_file: ./.github/codeql/codeql-frontend-publish-security.yml
- language: javascript-typescript
category: cli-package
config_file: ./.github/codeql/codeql-cli-package-security.yml
- language: javascript-typescript
category: repository-automation
config_file: ./.github/codeql/codeql-repository-automation-security.yml
- language: actions
category: actions
config_file: ./.github/codeql/codeql-actions-security.yml
steps:
- name: Checkout
if: ${{ github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == matrix.category }}
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
submodules: false
- name: Initialize CodeQL
if: ${{ github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == matrix.category }}
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
with:
languages: ${{ matrix.language }}
config-file: ${{ matrix.config_file }}
- name: Analyze
if: ${{ github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == matrix.category }}
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
with:
category: "/codeql-light/${{ matrix.category }}"
+86 -12
View File
@@ -12,11 +12,20 @@ on:
- full
- backend
- frontend
allow_deleting_large_indexes:
description: "Allow Convex to delete large indexes"
required: true
default: false
type: boolean
concurrency:
group: deploy-production
cancel-in-progress: true
permissions:
contents: write
statuses: read
jobs:
validate-deploy-request:
runs-on: ubuntu-latest
@@ -90,6 +99,7 @@ jobs:
fi
echo "Deploy target: ${{ needs.validate-deploy-request.outputs.target }}"
echo "Allow deleting large Convex indexes: ${{ inputs.allow_deleting_large_indexes }}"
if [[ -z "$PLAYWRIGHT_AUTH_STORAGE_STATE_JSON" ]]; then
echo "PLAYWRIGHT_AUTH_STORAGE_STATE_JSON not set; authenticated smoke will be skipped."
@@ -97,7 +107,7 @@ jobs:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@e3914758a49697077f7bcd190d36582a61667aad
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
with:
bun-version: 1.3.10
@@ -114,13 +124,20 @@ jobs:
- name: Deploy Convex
if: needs.validate-deploy-request.outputs.deploy_backend == 'true'
run: bun run convex:deploy
run: |
set -euo pipefail
if [[ "${{ inputs.allow_deleting_large_indexes }}" == "true" ]]; then
bunx convex deploy --typecheck=disable --yes --allow-deleting-large-indexes
else
bun run convex:deploy
fi
- name: Verify Convex contract
if: needs.validate-deploy-request.outputs.deploy_backend == 'true'
run: bun run verify:convex-contract -- --prod
- name: Wait for Vercel production deployment
id: vercel
if: needs.validate-deploy-request.outputs.deploy_frontend == 'true'
env:
GH_TOKEN: ${{ github.token }}
@@ -130,17 +147,26 @@ jobs:
run: |
set -euo pipefail
for attempt in {1..90}; do
if ! state="$(gh api "repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA/status" \
--jq '.statuses[] | select(.context == env.VERCEL_STATUS_CONTEXT) | .state' \
if ! status_json="$(gh api "repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA/status" \
--jq '.statuses[] | select(.context == env.VERCEL_STATUS_CONTEXT) | {state, target_url, description} | @base64' \
2>/dev/null | head -n1)"; then
echo "GitHub status check failed for $GITHUB_SHA; retrying..."
echo "GitHub status check failed for $GITHUB_SHA on attempt $attempt; retrying..."
sleep 10
continue
fi
if [[ -z "$status_json" ]]; then
state=""
target_url=""
else
state="$(printf '%s' "$status_json" | base64 -d | jq -r '.state // ""')"
target_url="$(printf '%s' "$status_json" | base64 -d | jq -r '.target_url // ""')"
fi
case "$state" in
success)
echo "Vercel production deployment ready for $GITHUB_SHA"
echo "deployment_url=$target_url" >> "$GITHUB_OUTPUT"
exit 0
;;
failure|error)
@@ -148,10 +174,10 @@ jobs:
exit 1
;;
pending)
echo "Vercel deployment pending for $GITHUB_SHA; waiting..."
echo "Vercel deployment pending for $GITHUB_SHA on attempt $attempt; waiting..."
;;
*)
echo "Vercel status for $GITHUB_SHA not published yet; waiting..."
echo "Vercel status for $GITHUB_SHA not published yet on attempt $attempt; waiting..."
;;
esac
@@ -162,15 +188,63 @@ jobs:
exit 1
- name: Install Playwright browser
if: needs.validate-deploy-request.outputs.run_smoke == 'true' && needs.validate-deploy-request.outputs.deploy_frontend == 'true'
run: bunx playwright install --with-deps chromium webkit
- name: Smoke test production HTTP
if: needs.validate-deploy-request.outputs.run_smoke == 'true'
run: bunx playwright install --with-deps chromium
run: bun run test:e2e:prod-http
- name: Write authenticated storage state
if: needs.validate-deploy-request.outputs.run_smoke == 'true' && env.PLAYWRIGHT_AUTH_STORAGE_STATE_JSON != ''
if: needs.validate-deploy-request.outputs.run_smoke == 'true' && needs.validate-deploy-request.outputs.deploy_frontend == 'true' && env.PLAYWRIGHT_AUTH_STORAGE_STATE_JSON != ''
run: |
echo "$PLAYWRIGHT_AUTH_STORAGE_STATE_JSON" > "$RUNNER_TEMP/playwright-auth.json"
echo "PLAYWRIGHT_AUTH_STORAGE_STATE=$RUNNER_TEMP/playwright-auth.json" >> "$GITHUB_ENV"
- name: Smoke test production
if: needs.validate-deploy-request.outputs.run_smoke == 'true'
run: bunx playwright test e2e/menu-smoke.pw.test.ts e2e/upload-auth-smoke.pw.test.ts
- name: Smoke test production UI
if: needs.validate-deploy-request.outputs.run_smoke == 'true' && needs.validate-deploy-request.outputs.deploy_frontend == 'true'
run: bunx playwright test --workers=1 e2e/menu-smoke.pw.test.ts e2e/publish-entry-workflows.pw.test.ts e2e/upload-auth-smoke.pw.test.ts
- name: Tag production frontend deployment
if: needs.validate-deploy-request.outputs.deploy_frontend == 'true'
env:
DEPLOY_TARGET: ${{ needs.validate-deploy-request.outputs.target }}
DEPLOYMENT_URL: ${{ steps.vercel.outputs.deployment_url }}
run: |
set -euo pipefail
deployed_at="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
tag_name="deploy/prod/$(date -u +"%Y%m%d-%H%M%SZ")-${GITHUB_SHA::7}"
version_prefix="prod/v$(date -u +"%Y.%m.%d")."
run_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
next_version=1
while IFS= read -r existing_tag; do
existing_tag="${existing_tag#refs/tags/}"
existing_tag="${existing_tag%\^\{\}}"
suffix="${existing_tag##*.}"
if [[ "$existing_tag" == "$version_prefix"* && "$suffix" =~ ^[0-9]+$ && "$suffix" -ge "$next_version" ]]; then
next_version=$((suffix + 1))
fi
done < <(git ls-remote --tags origin "refs/tags/${version_prefix}*" | awk '{print $2}' | sort -u)
version_tag="${version_prefix}${next_version}"
git tag -a "$tag_name" "$GITHUB_SHA" \
-m "Production frontend deploy $tag_name" \
-m "SHA: $GITHUB_SHA" \
-m "Version: $version_tag" \
-m "Deployed at: $deployed_at" \
-m "Target: $DEPLOY_TARGET" \
-m "Vercel: ${DEPLOYMENT_URL:-unknown}" \
-m "Run: $run_url"
git tag -a "$version_tag" "$GITHUB_SHA" \
-m "Production frontend deploy $version_tag" \
-m "SHA: $GITHUB_SHA" \
-m "Timestamp tag: $tag_name" \
-m "Deployed at: $deployed_at" \
-m "Target: $DEPLOY_TARGET" \
-m "Vercel: ${DEPLOYMENT_URL:-unknown}" \
-m "Run: $run_url"
git push origin "refs/tags/$tag_name" "refs/tags/$version_tag"
@@ -0,0 +1,36 @@
name: OpenClaw Docs Sync Dispatch
on:
push:
branches:
- main
paths:
- docs/**
- .github/workflows/openclaw-docs-sync-dispatch.yml
workflow_dispatch:
permissions:
contents: read
jobs:
dispatch-openclaw-docs-sync:
runs-on: ubuntu-latest
steps:
- name: Dispatch OpenClaw docs sync
env:
OPENCLAW_DOCS_SYNC_TOKEN: ${{ secrets.OPENCLAW_DOCS_SYNC_TOKEN }}
run: |
set -euo pipefail
if [ -z "${OPENCLAW_DOCS_SYNC_TOKEN:-}" ]; then
echo "::error::OPENCLAW_DOCS_SYNC_TOKEN is required to dispatch openclaw/openclaw docs sync."
exit 1
fi
curl --fail-with-body --silent --show-error \
--request POST \
--header "Authorization: Bearer ${OPENCLAW_DOCS_SYNC_TOKEN}" \
--header "Accept: application/vnd.github+json" \
--header "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/openclaw/openclaw/actions/workflows/docs-sync-publish.yml/dispatches \
--data '{"ref":"main"}'
+251 -7
View File
@@ -57,11 +57,28 @@ on:
description: Optional source ref override for local-folder publishes.
required: false
type: string
clawhub_version:
description: Legacy npm CLI version input. Kept for compatibility; the workflow now runs the checked-out source.
source_path:
description: Optional source path inside the repository for monorepo package publishes.
required: false
type: string
default: latest
package_artifact_name:
description: Optional Actions artifact name containing a prebuilt ClawPack .tgz to publish.
required: false
type: string
package_artifact_path:
description: Optional path to the .tgz inside package_artifact_name. Defaults to the only .tgz in the artifact.
required: false
type: string
inspector_artifact_name:
description: Artifact name for plugin inspector reports. Set a unique value when calling this workflow from a matrix.
required: false
type: string
default: plugin-inspector-report
publish_json_artifact_name:
description: Artifact name for the package publish JSON output. Set a unique value when calling this workflow from a matrix.
required: false
type: string
default: clawhub-package-publish-json
secrets:
clawhub_token:
required: false
@@ -73,11 +90,17 @@ on:
description: Published release id when dry_run is false.
value: ${{ jobs.publish.outputs.release_id }}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
permissions: {}
jobs:
publish:
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
actions: read
contents: read
id-token: write
outputs:
@@ -88,7 +111,7 @@ jobs:
with:
ref: ${{ github.sha }}
- uses: oven-sh/setup-bun@e3914758a49697077f7bcd190d36582a61667aad
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
with:
bun-version: 1.3.10
@@ -209,7 +232,56 @@ jobs:
PY
echo "CLAWHUB_CONFIG_PATH=$RUNNER_TEMP/clawhub-config.json" >> "$GITHUB_ENV"
- name: Download prebuilt package artifact
if: inputs.package_artifact_name != ''
uses: actions/download-artifact@v8
with:
name: ${{ inputs.package_artifact_name }}
path: ${{ runner.temp }}/prebuilt-package-artifact
- name: Resolve prebuilt package artifact
id: resolve_artifact
env:
INPUT_PACKAGE_ARTIFACT_NAME: ${{ inputs.package_artifact_name }}
INPUT_PACKAGE_ARTIFACT_PATH: ${{ inputs.package_artifact_path }}
run: |
python3 - <<'PY'
import os
from pathlib import Path
artifact_name = os.environ["INPUT_PACKAGE_ARTIFACT_NAME"].strip()
output_path = Path(os.environ["GITHUB_OUTPUT"])
if not artifact_name:
with output_path.open("a", encoding="utf-8") as fh:
fh.write("package_artifact_path=\n")
raise SystemExit(0)
artifact_root = Path(os.environ["RUNNER_TEMP"]) / "prebuilt-package-artifact"
requested_path = os.environ["INPUT_PACKAGE_ARTIFACT_PATH"].strip()
if requested_path:
candidate = (artifact_root / requested_path).resolve()
if artifact_root.resolve() not in candidate.parents and candidate != artifact_root.resolve():
raise SystemExit(f"Prebuilt artifact path escapes downloaded artifact: {requested_path}")
if not candidate.is_file():
raise SystemExit(f"Prebuilt package artifact path not found: {requested_path}")
else:
candidates = sorted(path for path in artifact_root.rglob("*.tgz") if path.is_file())
if not candidates:
raise SystemExit(f"Prebuilt package artifact {artifact_name!r} did not contain a .tgz file.")
if len(candidates) > 1:
joined = ", ".join(str(path.relative_to(artifact_root)) for path in candidates)
raise SystemExit(
"Prebuilt package artifact contains multiple .tgz files; set package_artifact_path. "
f"Found: {joined}"
)
candidate = candidates[0]
with output_path.open("a", encoding="utf-8") as fh:
fh.write(f"package_artifact_path={candidate}\n")
PY
- name: Resolve publish command
id: resolve_publish
env:
INPUT_SOURCE: ${{ inputs.source }}
INPUT_REF: ${{ inputs.ref }}
@@ -220,9 +292,12 @@ jobs:
INPUT_SOURCE_REPO: ${{ inputs.source_repo }}
INPUT_SOURCE_COMMIT: ${{ inputs.source_commit }}
INPUT_SOURCE_REF: ${{ inputs.source_ref }}
INPUT_SOURCE_PATH: ${{ inputs.source_path }}
PREBUILT_PACKAGE_ARTIFACT_PATH: ${{ steps.resolve_artifact.outputs.package_artifact_path }}
INPUT_SITE: ${{ inputs.site }}
INPUT_REGISTRY: ${{ inputs.registry }}
CLAWHUB_TOKEN: ${{ secrets.clawhub_token }}
GITHUB_TOKEN: ${{ github.token }}
GITHUB_EVENT_NAME: ${{ github.event_name }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_REF: ${{ github.ref }}
@@ -233,6 +308,78 @@ jobs:
import os
import shlex
from pathlib import Path
from urllib.error import HTTPError
from urllib.parse import quote, urlparse
from urllib.request import Request, urlopen
def split_ref_path(value):
if not value:
return "", ""
if ":" not in value:
return value, ""
ref, path = value.split(":", 1)
return ref, path.strip("/")
def github_commit_exists(repo, ref):
token = os.environ.get("GITHUB_TOKEN", "").strip()
headers = {
"Accept": "application/vnd.github+json",
"User-Agent": "clawhub-package-publish",
}
if token:
headers["Authorization"] = f"Bearer {token}"
request = Request(
f"https://api.github.com/repos/{repo}/commits/{quote(ref, safe='')}",
headers=headers,
)
try:
with urlopen(request, timeout=10) as response:
return 200 <= response.status < 300
except HTTPError as error:
if error.code in (404, 422):
return False
raise
def resolve_github_url_ref_and_path(repo, kind, segments):
min_path_segments = 1 if kind == "blob" else 0
max_ref_segments = len(segments) - min_path_segments
for ref_segment_count in range(max_ref_segments, 0, -1):
ref = "/".join(segments[:ref_segment_count])
path = "/".join(segments[ref_segment_count:]).strip("/")
if kind == "blob" and not path:
continue
if not github_commit_exists(repo, ref):
continue
if kind == "blob":
path = "/".join(path.split("/")[:-1]).strip("/")
return ref, path
raise SystemExit(f"GitHub ref not found in source URL for {repo}")
def parse_github_source(value):
raw = value.strip()
if raw.startswith("github:"):
raw = raw[len("github:"):]
if raw.startswith("https://") or raw.startswith("http://"):
parsed = urlparse(raw)
if parsed.netloc.lower() != "github.com":
return None
parts = [part for part in parsed.path.strip("/").split("/") if part]
if len(parts) < 2:
return None
repo_name = parts[1][:-4] if parts[1].endswith(".git") else parts[1]
repo = f"{parts[0]}/{repo_name}"
if len(parts) >= 4 and parts[2] in {"tree", "blob"}:
ref, path = resolve_github_url_ref_and_path(repo, parts[2], parts[3:])
return {"repo": repo, "ref": ref, "path": path}
return {"repo": repo, "ref": "", "path": ""}
source_part, at, ref_part = raw.partition("@")
repo_parts = source_part.split("/")
if len(repo_parts) != 2 or not repo_parts[0] or not repo_parts[1]:
return None
repo_name = repo_parts[1][:-4] if repo_parts[1].endswith(".git") else repo_parts[1]
ref, path = split_ref_path(ref_part if at else "")
return {"repo": f"{repo_parts[0]}/{repo_name}", "ref": ref, "path": path}
source = os.environ["INPUT_SOURCE"].strip()
if not source:
@@ -244,6 +391,33 @@ jobs:
is_local_source = source.startswith(".") or source.startswith("/") or Path(source).exists()
if ref and "@" not in source and not source.startswith("http") and not is_local_source:
source = f"{source}@{ref}"
source_path = os.environ["INPUT_SOURCE_PATH"].strip()
prebuilt_artifact_path = os.environ["PREBUILT_PACKAGE_ARTIFACT_PATH"].strip()
inspect_checkout_repository = ""
inspect_checkout_ref = ""
inspect_local_root = str(Path(os.environ["GITHUB_WORKSPACE"]).resolve())
inspect_subdir = source_path
if prebuilt_artifact_path:
inspect_local_root = str((Path(os.environ["RUNNER_TEMP"]) / "prebuilt-package-inspect").resolve())
inspect_subdir = ""
elif is_local_source:
inspect_local_root = str(Path(source).resolve())
else:
github_source = parse_github_source(source)
source_ref_differs_from_checkout = (
bool(github_source and github_source["ref"])
and github_source["ref"] != os.environ["GITHUB_SHA"]
)
if github_source and (
github_source["repo"] != os.environ["GITHUB_REPOSITORY"]
or source_ref_differs_from_checkout
):
inspect_checkout_repository = github_source["repo"]
inspect_checkout_ref = github_source["ref"]
inspect_local_root = str((Path(os.environ["GITHUB_WORKSPACE"]) / "clawhub-publish-source").resolve())
inspect_subdir = source_path or github_source["path"]
elif github_source:
inspect_subdir = source_path or github_source["path"]
cli_entry = (
Path(os.environ["GITHUB_WORKSPACE"])
@@ -256,12 +430,13 @@ jobs:
if not cli_entry.exists():
raise SystemExit(f"Missing ClawHub CLI entrypoint at {cli_entry}")
cmd_source = prebuilt_artifact_path or source
cmd = [
"bun",
str(cli_entry),
"package",
"publish",
source,
cmd_source,
"--site",
os.environ["INPUT_SITE"],
"--registry",
@@ -284,6 +459,16 @@ jobs:
source_repo = os.environ["INPUT_SOURCE_REPO"].strip()
source_commit = os.environ["INPUT_SOURCE_COMMIT"].strip()
source_ref = os.environ["INPUT_SOURCE_REF"].strip()
if prebuilt_artifact_path:
if not source_repo and not source_commit:
source_repo = os.environ["GITHUB_REPOSITORY"].strip()
source_commit = os.environ["GITHUB_SHA"].strip()
elif not source_repo or not source_commit:
raise SystemExit(
"Prebuilt artifact mode requires source_repo and source_commit together when overriding source attribution."
)
if not source_ref:
source_ref = os.environ["GITHUB_REF"].strip()
if source_repo:
cmd += ["--source-repo", source_repo]
if source_commit:
@@ -294,6 +479,8 @@ jobs:
github_ref = os.environ["GITHUB_REF"].strip()
if github_ref:
cmd += ["--source-ref", github_ref]
if source_path:
cmd += ["--source-path", source_path]
if os.environ["INPUT_DRY_RUN"] != "true" and os.environ["CLAWHUB_TOKEN"].strip():
cmd += [
"--manual-override-reason",
@@ -305,8 +492,65 @@ jobs:
path.write_text("#!/usr/bin/env bash\nset -euo pipefail\n" + shell_line + "\n", encoding="utf-8")
path.chmod(0o755)
print(shell_line)
output_path = Path(os.environ["GITHUB_OUTPUT"])
with output_path.open("a", encoding="utf-8") as fh:
fh.write(f"inspect_checkout_repository={inspect_checkout_repository}\n")
fh.write(f"inspect_checkout_ref={inspect_checkout_ref}\n")
fh.write(f"inspect_local_root={inspect_local_root}\n")
fh.write(f"inspect_subdir={inspect_subdir}\n")
PY
- name: Extract prebuilt package artifact for plugin validation
if: steps.resolve_artifact.outputs.package_artifact_path != ''
env:
PREBUILT_PACKAGE_ARTIFACT_PATH: ${{ steps.resolve_artifact.outputs.package_artifact_path }}
INSPECT_LOCAL_ROOT: ${{ steps.resolve_publish.outputs.inspect_local_root }}
run: |
set -euo pipefail
mkdir -p "$INSPECT_LOCAL_ROOT"
tar -xzf "$PREBUILT_PACKAGE_ARTIFACT_PATH" -C "$INSPECT_LOCAL_ROOT" --strip-components=1
- name: Checkout publish source for plugin inspector
if: steps.resolve_publish.outputs.inspect_checkout_repository != ''
uses: actions/checkout@v6
with:
repository: ${{ steps.resolve_publish.outputs.inspect_checkout_repository }}
ref: ${{ steps.resolve_publish.outputs.inspect_checkout_ref }}
path: clawhub-publish-source
- name: Run plugin validation
env:
INSPECT_LOCAL_ROOT: ${{ steps.resolve_publish.outputs.inspect_local_root }}
INSPECT_SUBDIR: ${{ steps.resolve_publish.outputs.inspect_subdir }}
run: |
set -euo pipefail
inspect_root="$(python3 - <<'PY'
import os
from pathlib import Path
root = Path(os.environ["INSPECT_LOCAL_ROOT"]).resolve()
subdir = os.environ["INSPECT_SUBDIR"].strip()
inspect_root = (root / subdir).resolve() if subdir else root
if inspect_root != root and root not in inspect_root.parents:
raise SystemExit(f"Inspector source path escapes publish source: {subdir}")
print(inspect_root)
PY
)"
if [ ! -f "$inspect_root/package.json" ] && [ ! -f "$inspect_root/openclaw.plugin.json" ]; then
echo "::warning::Plugin Inspector skipped because $inspect_root is not a plugin root."
exit 0
fi
bun "$GITHUB_WORKSPACE/clawhub-source/packages/clawhub/src/cli.ts" package validate "$inspect_root" --out "$RUNNER_TEMP/plugin-inspector"
- name: Upload plugin inspector reports
if: always()
uses: actions/upload-artifact@v7
with:
name: ${{ inputs.inspector_artifact_name }}
path: ${{ runner.temp }}/plugin-inspector
if-no-files-found: ignore
- name: Run package publish
run: |
set -euo pipefail
@@ -334,8 +578,8 @@ jobs:
PY
- name: Upload publish JSON artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: clawhub-package-publish-json
name: ${{ inputs.publish_json_artifact_name }}
path: ${{ runner.temp }}/package-publish.json
if-no-files-found: error
@@ -0,0 +1,63 @@
name: Plugin Inspector Bulk Scan
on:
workflow_dispatch:
inputs:
batch_size:
description: "Maximum plugin releases to scan"
required: false
default: "25"
dry_run:
description: "Preview impact without persisting findings or sending emails"
required: false
default: "false"
type: boolean
dry_run_max_batches:
description: "Maximum preview batches to scan when dry_run is enabled"
required: false
default: "20"
source_pr:
description: "Merged PR number that triggered this scan, when dispatched automatically"
required: false
default: ""
source_sha:
description: "Merged commit SHA that triggered this scan, when dispatched automatically"
required: false
default: ""
permissions:
contents: read
jobs:
scan:
name: Scan published plugins
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Bun
uses: oven-sh/setup-bun@v2
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Run plugin inspector bulk scan
env:
CLAWHUB_SITE_URL: ${{ vars.CLAWHUB_SITE_URL || 'https://clawhub.ai' }}
CLAWHUB_PLUGIN_INSPECTOR_WORKER_TOKEN: ${{ secrets.CLAWHUB_PLUGIN_INSPECTOR_WORKER_TOKEN }}
PLUGIN_INSPECTOR_BATCH_SIZE: ${{ inputs.batch_size || '25' }}
PLUGIN_INSPECTOR_DRY_RUN: ${{ inputs.dry_run && '1' || '0' }}
PLUGIN_INSPECTOR_DRY_RUN_MAX_BATCHES: ${{ inputs.dry_run_max_batches || '20' }}
PLUGIN_INSPECTOR_SOURCE_PR: ${{ inputs.source_pr || '' }}
PLUGIN_INSPECTOR_SOURCE_SHA: ${{ inputs.source_sha || '' }}
PLUGIN_INSPECTOR_ARTIFACT_DIR: plugin-inspector-bulk-scan-reports
run: bun scripts/package-inspector-nightly-scan.ts
- name: Upload inspector reports
if: always()
uses: actions/upload-artifact@v7
with:
name: plugin-inspector-bulk-scan-reports
path: plugin-inspector-bulk-scan-reports
if-no-files-found: warn
@@ -0,0 +1,55 @@
name: Plugin Inspector Pin Bump Dispatch
on:
push:
branches:
- main
paths:
- package.json
- packages/clawhub/package.json
- bun.lock
permissions:
actions: write
contents: read
jobs:
dispatch-plugin-inspector-bulk-scan:
name: Dispatch Plugin Inspector bulk scan
runs-on: ubuntu-latest
steps:
- name: Checkout main commit
uses: actions/checkout@v6
with:
ref: ${{ github.sha }}
fetch-depth: 0
- name: Fetch previous main commit
env:
BASE_SHA: ${{ github.event.before }}
run: git fetch --no-tags --depth=1 origin "$BASE_SHA"
- name: Detect pinned Plugin Inspector change
id: detect
env:
BASE_SHA: ${{ github.event.before }}
HEAD_SHA: ${{ github.sha }}
run: node scripts/github/plugin-inspector-pin-change.mjs --base "$BASE_SHA" --head "$HEAD_SHA"
- name: Dispatch Plugin Inspector bulk scan
if: ${{ steps.detect.outputs.changed == 'true' }}
env:
GH_TOKEN: ${{ github.token }}
run: |
gh workflow run plugin-inspector-bulk-scan.yml \
--ref main \
-f batch_size=25 \
-f dry_run=false \
-f dry_run_max_batches=20 \
-f source_sha=${{ github.sha }}
- name: Explain skipped dispatch
if: ${{ steps.detect.outputs.changed != 'true' }}
env:
DISPATCH_SKIP_REASON: ${{ steps.detect.outputs.reason }}
run: printf '%s\n' "$DISPATCH_SKIP_REASON"
@@ -0,0 +1,67 @@
name: Publish Hosted Catalog Feed
on:
schedule:
- cron: "17 */6 * * *"
workflow_dispatch:
inputs:
expires_in_days:
description: "How long the published feed remains fresh"
required: true
default: "7"
type: string
concurrency:
group: publish-catalog-feed
cancel-in-progress: true
permissions:
contents: read
jobs:
validate-ref:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Require main ref for production publication
run: |
set -euo pipefail
if [[ "${GITHUB_REF}" != "refs/heads/main" ]]; then
echo "Production catalog publications must run from main."
exit 1
fi
publish:
runs-on: ubuntu-latest
timeout-minutes: 15
needs: validate-ref
environment:
name: Production
url: https://registry.openclaw.ai/v1/feeds/plugins
env:
EXPIRES_IN_DAYS: ${{ inputs.expires_in_days || '7' }}
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
with:
bun-version: 1.3.10
- name: Install
run: bun install --frozen-lockfile
- name: Publish current production catalog
env:
CONVEX_DEPLOY_KEY: ${{ secrets.CONVEX_DEPLOY_KEY }}
run: |
set -euo pipefail
if [[ -z "$CONVEX_DEPLOY_KEY" ]]; then
echo "::error::Missing Production environment secret CONVEX_DEPLOY_KEY"
exit 1
fi
if ! [[ "$EXPIRES_IN_DAYS" =~ ^[1-9][0-9]*$ ]]; then
echo "::error::expires_in_days must be a positive integer"
exit 1
fi
expires_at="$(node -e 'const days = Number(process.env.EXPIRES_IN_DAYS); console.log(new Date(Date.now() + days * 86400000).toISOString())')"
bunx convex run catalogFeed:publish "{\"expiresAt\":\"$expires_at\"}" --prod
@@ -0,0 +1,84 @@
name: ClawHub Scheduled Live Checks
on:
schedule:
- cron: "17 5 * * *"
workflow_dispatch:
inputs:
github-repo:
description: GitHub skills repo to use for the source-backed canary
required: false
default: openclaw/agent-skills
github-skill:
description: Skill slug to verify from the GitHub skills repo
required: false
default: handoff
concurrency:
group: clawhub-scheduled-live-checks-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: read
env:
VITE_CONVEX_URL: https://example.invalid
jobs:
github-backed-skills:
name: GitHub-backed skills canary
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-bun
- name: Run GitHub-backed skills live canary
env:
CLAWHUB_LIVE_GITHUB_CANARY: "1"
CLAWHUB_LIVE_GITHUB_REPO: ${{ inputs.github-repo || 'openclaw/agent-skills' }}
CLAWHUB_LIVE_GITHUB_SKILL: ${{ inputs.github-skill || 'handoff' }}
GITHUB_TOKEN: ${{ github.token }}
run: bunx vitest run convex/githubSkillSync.live.test.ts
open-failure-issue:
name: Open failure issue
needs: github-backed-skills
if: ${{ always() && needs.github-backed-skills.result == 'failure' }}
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Open or update failure issue
env:
GH_TOKEN: ${{ github.token }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
WORKFLOW_NAME: ${{ github.workflow }}
run: |
set -euo pipefail
marker_token="clawhub-scheduled-live-checks-failure"
marker="<!-- $marker_token -->"
title="ClawHub scheduled live checks failing"
issue_number="$(gh issue list \
--repo "$GITHUB_REPOSITORY" \
--state open \
--search "$marker_token in:body" \
--json number \
--jq '.[0].number // empty')"
body_file="$(mktemp)"
cat > "$body_file" <<EOF
$marker
The scheduled ClawHub live checks failed.
Workflow: $WORKFLOW_NAME
Run: $RUN_URL
EOF
if [[ -n "$issue_number" ]]; then
gh issue comment "$issue_number" --repo "$GITHUB_REPOSITORY" --body-file "$body_file"
else
gh issue create --repo "$GITHUB_REPOSITORY" --title "$title" --body-file "$body_file"
fi
+3 -1
View File
@@ -6,6 +6,8 @@ on:
pull_request:
branches: [main, master]
permissions: {}
jobs:
trufflehog:
name: Scan for Verified Secrets
@@ -49,7 +51,7 @@ jobs:
id: trufflehog
# Use a concrete released ref that resolves in upstream action registry.
# v3 (major tag) is not published by trufflesecurity/trufflehog.
uses: trufflesecurity/trufflehog@v3.93.8
uses: trufflesecurity/trufflehog@v3.95.5
with:
path: ./
base: ${{ steps.scan_range.outputs.base }}
@@ -0,0 +1,223 @@
name: Security Dataset Snapshot
on:
workflow_dispatch:
inputs:
upload:
description: "Upload sanitized dataset files to Hugging Face"
required: true
default: "false"
type: choice
options:
- "false"
- "true"
limit:
description: "Optional source artifact cap for validation runs"
required: false
default: ""
hf-revision:
description: "Hugging Face branch/revision to upload to"
required: true
default: "main"
schedule:
- cron: "17 9 * * *"
permissions:
contents: read
id-token: write
concurrency:
group: clawhub-security-dataset-snapshot
cancel-in-progress: false
jobs:
publish-security-dataset:
name: Publish sanitized security dataset
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 360
environment: Production
env:
CONVEX_URL: ${{ vars.CONVEX_URL || vars.VITE_CONVEX_URL || 'https://wry-manatee-359.convex.cloud' }}
SECURITY_SCAN_WORKER_TOKEN: ${{ secrets.SECURITY_SCAN_WORKER_TOKEN }}
HF_DATASET_REPO: OpenClaw/clawhub-security-signals
HF_OIDC_RESOURCE: datasets/OpenClaw/clawhub-security-signals
HF_REVISION: ${{ inputs['hf-revision'] || 'main' }}
HF_UPLOAD: ${{ github.event_name == 'schedule' || inputs.upload == 'true' }}
SNAPSHOT_LIMIT: ${{ inputs.limit || '' }}
SANITIZED_OUT_DIR: /tmp/clawhub-security-dataset/sanitized
WORK_DIR: /tmp/clawhub-security-dataset
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-bun
- uses: actions/setup-python@v6
if: ${{ env.HF_UPLOAD == 'true' }}
with:
python-version: "3.12"
- name: Check configuration
run: |
set -euo pipefail
if [[ -z "$SECURITY_SCAN_WORKER_TOKEN" ]]; then
echo "::error::SECURITY_SCAN_WORKER_TOKEN is required"
exit 1
fi
echo "Upload enabled: $HF_UPLOAD"
echo "Convex URL: $CONVEX_URL"
echo "Hugging Face repo: $HF_DATASET_REPO"
echo "Hugging Face OIDC resource: $HF_OIDC_RESOURCE"
echo "Hugging Face revision: $HF_REVISION"
- name: Export sanitized live dataset
run: |
set -euo pipefail
mkdir -p "$SANITIZED_OUT_DIR"
source_snapshot_id="live-convex-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
args=(
--convex-url "$CONVEX_URL"
--worker-token "$SECURITY_SCAN_WORKER_TOKEN"
--source-snapshot-id "$source_snapshot_id"
--out-dir "$SANITIZED_OUT_DIR"
--hf-dataset
--hf-repo "$HF_DATASET_REPO"
--hf-revision "$HF_REVISION"
--page-size 25
--batch-pages 2
--concurrency 3
)
if [[ -n "$SNAPSHOT_LIMIT" ]]; then
args+=(--limit "$SNAPSHOT_LIMIT")
fi
bun scripts/security-dataset/export-snapshot.ts "${args[@]}" | tee "$SANITIZED_OUT_DIR/summary.json"
snapshot_dir="$(jq -r '.snapshotDir' "$SANITIZED_OUT_DIR/summary.json")"
if [[ -z "$snapshot_dir" || "$snapshot_dir" == "null" ]]; then
echo "::error::export summary did not include snapshotDir"
exit 1
fi
echo "SNAPSHOT_DIR=$snapshot_dir" >> "$GITHUB_ENV"
jq '.manifest.row_counts, .manifest.huggingface_dataset' "$SANITIZED_OUT_DIR/summary.json"
- name: Validate sanitized output guardrails
run: |
set -euo pipefail
test -d "$SNAPSHOT_DIR/hf-dataset/data"
test -f "$SNAPSHOT_DIR/manifest.json"
for split in train validation test eval_holdout; do
test -f "$SNAPSHOT_DIR/hf-dataset/data/$split.jsonl"
done
if grep -R -E 'storageId|skillVersions:|packageReleases:|_storage/' "$SNAPSHOT_DIR/hf-dataset" "$SNAPSHOT_DIR/manifest.json"; then
echo "::error::sanitized output contains raw storage or internal document identifiers"
exit 1
fi
if grep -R -E 'gh[pousr]_[A-Za-z0-9_]{20,}|sk-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16}|-----BEGIN [A-Z0-9 ]*(PRIVATE KEY|CERTIFICATE)-----' "$SNAPSHOT_DIR/hf-dataset"; then
echo "::error::sanitized output contains obvious secret-like values"
exit 1
fi
- name: Install Hugging Face uploader
if: ${{ env.HF_UPLOAD == 'true' }}
run: |
set -euo pipefail
python -m pip install --upgrade pip
python -m pip install 'huggingface_hub[hf_xet]'
- name: Upload sanitized dataset to Hugging Face
if: ${{ env.HF_UPLOAD == 'true' }}
run: |
set -euo pipefail
python - <<'PY'
import json
import os
import urllib.request
from pathlib import Path
from huggingface_hub import HfApi
def get_github_oidc_token() -> str:
request_url = os.environ["ACTIONS_ID_TOKEN_REQUEST_URL"]
separator = "&" if "?" in request_url else "?"
request = urllib.request.Request(
f"{request_url}{separator}audience=https://huggingface.co",
headers={
"Authorization": f"bearer {os.environ['ACTIONS_ID_TOKEN_REQUEST_TOKEN']}",
"Accept": "application/json",
},
)
with urllib.request.urlopen(request) as response:
payload = json.loads(response.read().decode("utf-8"))
return payload["value"]
def exchange_hugging_face_token(oidc_token: str, resource: str) -> str:
body = json.dumps({
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"subject_token_type": "urn:ietf:params:oauth:token-type:id_token",
"subject_token": oidc_token,
"resource": resource,
}).encode("utf-8")
request = urllib.request.Request(
"https://huggingface.co/oauth/token",
data=body,
headers={
"Content-Type": "application/json",
"Accept": "application/json",
},
method="POST",
)
with urllib.request.urlopen(request) as response:
payload = json.loads(response.read().decode("utf-8"))
return payload["access_token"]
snapshot_dir = Path(os.environ["SNAPSHOT_DIR"])
repo_id = os.environ["HF_DATASET_REPO"]
revision = os.environ["HF_REVISION"]
token = exchange_hugging_face_token(
get_github_oidc_token(),
os.environ["HF_OIDC_RESOURCE"],
)
api = HfApi(token=token)
data_commit = api.upload_folder(
folder_path=str(snapshot_dir / "hf-dataset" / "data"),
path_in_repo="data",
repo_id=repo_id,
repo_type="dataset",
revision=revision,
delete_patterns="data/*.jsonl",
commit_message="Update nightly ClawHub security dataset splits",
)
manifest_path = snapshot_dir / "manifest.json"
manifest = json.loads(manifest_path.read_text())
manifest["huggingface_dataset"]["commit"] = data_commit.oid
manifest["huggingface_dataset"]["revision"] = revision
manifest_path.write_text(json.dumps(manifest, indent=2) + "\n")
manifest_commit = api.upload_file(
path_or_fileobj=str(manifest_path),
path_in_repo="metadata/latest-manifest.json",
repo_id=repo_id,
repo_type="dataset",
revision=revision,
commit_message="Update nightly ClawHub security dataset manifest",
)
print(json.dumps({
"data_commit": data_commit.oid,
"manifest_commit": manifest_commit.oid,
"repo": repo_id,
"revision": revision,
}, indent=2))
PY
- name: Upload sanitized summary artifact
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v7
with:
name: security-dataset-summary-${{ github.run_id }}
path: |
${{ env.SANITIZED_OUT_DIR }}/summary.json
${{ env.SNAPSHOT_DIR }}/manifest.json
if-no-files-found: ignore
- name: Cleanup transient dataset files
if: ${{ always() }}
run: rm -rf "$WORK_DIR"
+110
View File
@@ -0,0 +1,110 @@
name: Security Scan Codex Worker
on:
workflow_dispatch:
inputs:
limit:
description: "Deprecated alias for batch-limit"
required: false
default: ""
batch-limit:
description: "Maximum Codex scans to run in parallel per worker shard"
required: true
default: "4"
max-jobs:
description: "Optional total jobs cap per worker shard"
required: false
default: ""
max-runtime-minutes:
description: "Stop claiming new batches after this many minutes"
required: true
default: "40"
schedule:
- cron: "*/5 * * * *"
permissions:
contents: read
concurrency:
group: clawhub-security-scan
cancel-in-progress: false
jobs:
codex-security-scan:
name: Codex security scan shard ${{ matrix.shard }}
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 60
environment: Production
strategy:
fail-fast: false
matrix:
shard: [0, 1, 2, 3]
env:
CONVEX_URL: ${{ vars.CONVEX_URL || vars.VITE_CONVEX_URL || 'https://wry-manatee-359.convex.cloud' }}
SECURITY_SCAN_WORKER_TOKEN: ${{ secrets.SECURITY_SCAN_WORKER_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
CODEX_SECURITY_SCAN_LIMIT: ${{ inputs.limit || inputs['batch-limit'] || '4' }}
CODEX_SECURITY_SCAN_MAX_JOBS: ${{ inputs['max-jobs'] || '' }}
CODEX_SECURITY_SCAN_MAX_RUNTIME_MINUTES: ${{ inputs['max-runtime-minutes'] || '40' }}
CODEX_SECURITY_SCAN_LEASE_MINUTES: "60"
CODEX_SECURITY_SCAN_DIAGNOSTICS_DIR: codex-security-scan-diagnostics-${{ matrix.shard }}
CODEX_SECURITY_SCAN_SHARD: ${{ matrix.shard }}
CODEX_SECURITY_SCAN_WORKER_ID: "github-actions:${{ github.run_id }}:${{ github.run_attempt }}:${{ matrix.shard }}"
SKILLSPECTOR_PROVIDER: openai
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-bun
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Check configuration
run: |
set -euo pipefail
if [[ -z "$SECURITY_SCAN_WORKER_TOKEN" ]]; then
echo "::error::SECURITY_SCAN_WORKER_TOKEN is required"
exit 1
fi
if [[ -z "$OPENAI_API_KEY" ]]; then
echo "::error::OPENAI_API_KEY is required"
exit 1
fi
- name: Install Codex CLI
run: |
set -euo pipefail
if ! command -v codex >/dev/null 2>&1; then
npm install -g @openai/codex@latest
fi
codex --version
- name: Install SkillSpector
run: |
set -euo pipefail
python -m venv "$RUNNER_TEMP/skillspector-venv"
source "$RUNNER_TEMP/skillspector-venv/bin/activate"
python -m pip install --upgrade pip
python -m pip install 'git+https://github.com/NVIDIA/skillspector.git'
echo "$RUNNER_TEMP/skillspector-venv/bin" >> "$GITHUB_PATH"
skillspector --help >/dev/null
- name: Authenticate Codex CLI
run: printf '%s' "$OPENAI_API_KEY" | codex login --with-api-key
- name: Run Codex security worker
run: |
bun scripts/security/run-codex-scan-worker.ts \
--batch-limit "$CODEX_SECURITY_SCAN_LIMIT" \
--max-jobs "$CODEX_SECURITY_SCAN_MAX_JOBS" \
--max-runtime-minutes "$CODEX_SECURITY_SCAN_MAX_RUNTIME_MINUTES" \
--lease-minutes "$CODEX_SECURITY_SCAN_LEASE_MINUTES"
- name: Upload Codex security diagnostics
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v7
with:
name: codex-security-scan-diagnostics-${{ github.run_id }}-${{ matrix.shard }}
path: ${{ env.CODEX_SECURITY_SCAN_DIAGNOSTICS_DIR }}
if-no-files-found: ignore
+98
View File
@@ -0,0 +1,98 @@
name: Skill Card Worker
on:
workflow_run:
workflows: ["Security Scan Codex Worker"]
types: [completed]
workflow_dispatch:
inputs:
batch-limit:
description: "Maximum Skill Card jobs to run in parallel per worker shard"
required: true
default: "4"
max-jobs:
description: "Optional total jobs cap per worker shard"
required: false
default: ""
max-runtime-minutes:
description: "Stop claiming new batches after this many minutes"
required: true
default: "40"
permissions:
contents: read
concurrency:
group: clawhub-skill-card-worker
cancel-in-progress: false
jobs:
skill-card-worker:
name: Skill Card worker shard ${{ matrix.shard }}
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 60
environment: Production
strategy:
fail-fast: false
matrix:
shard: [0, 1, 2, 3]
env:
CONVEX_URL: ${{ vars.CONVEX_URL || vars.VITE_CONVEX_URL || 'https://wry-manatee-359.convex.cloud' }}
# Shared Convex worker credential used by security and Skill Card workers.
SECURITY_SCAN_WORKER_TOKEN: ${{ secrets.SECURITY_SCAN_WORKER_TOKEN }}
SKILL_CARD_WORKER_LIMIT: ${{ github.event.inputs['batch-limit'] || '4' }}
SKILL_CARD_WORKER_MAX_JOBS: ${{ github.event.inputs['max-jobs'] || '' }}
SKILL_CARD_WORKER_MAX_RUNTIME_MINUTES: ${{ github.event.inputs['max-runtime-minutes'] || '40' }}
SKILL_CARD_WORKER_LEASE_MINUTES: "60"
SKILL_CARD_WORKER_SHARD: ${{ matrix.shard }}
SKILL_CARD_WORKER_ID: "github-actions:${{ github.run_id }}:${{ github.run_attempt }}:${{ matrix.shard }}"
NVIDIA_TRUSTWORTHY_AI_DIR: ${{ github.workspace }}/.artifacts/nvidia-trustworthy-ai
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v6
with:
repository: NVIDIA/Trustworthy-AI
ref: fb5867e9070b4080d28818242e20334e10ac55fc
path: .artifacts/nvidia-trustworthy-ai
- uses: ./.github/actions/setup-bun
- name: Check configuration
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
set -euo pipefail
if [[ -z "$SECURITY_SCAN_WORKER_TOKEN" ]]; then
echo "::error::SECURITY_SCAN_WORKER_TOKEN is required"
exit 1
fi
if [[ -z "$OPENAI_API_KEY" ]]; then
echo "::error::OPENAI_API_KEY is required"
exit 1
fi
- name: Install Codex CLI and renderer dependencies
run: |
set -euo pipefail
if ! command -v codex >/dev/null 2>&1; then
npm install -g @openai/codex@latest
fi
python3 -m pip install --user jinja2
codex --version
- name: Authenticate Codex CLI
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: printf '%s' "$OPENAI_API_KEY" | codex login --with-api-key
- name: Run Skill Card worker
run: |
args=(
--batch-limit "$SKILL_CARD_WORKER_LIMIT"
--max-jobs "$SKILL_CARD_WORKER_MAX_JOBS"
--max-runtime-minutes "$SKILL_CARD_WORKER_MAX_RUNTIME_MINUTES"
--lease-minutes "$SKILL_CARD_WORKER_LEASE_MINUTES"
--nvidia-tool-dir "$NVIDIA_TRUSTWORTHY_AI_DIR"
)
bun scripts/skill-cards/run-skill-card-worker.ts "${args[@]}"
+312
View File
@@ -0,0 +1,312 @@
name: Skill Publish
on:
workflow_call:
inputs:
skill_path:
description: Optional path to one skill folder. When set, only this skill is processed.
required: false
type: string
default: ""
root:
description: Directory containing skill folders for catalog publishing.
required: false
type: string
default: skills
dry_run:
description: Preview only. When true, no publish mutation is performed.
required: false
type: boolean
default: true
owner:
description: Optional owner/publisher handle for org publishing.
required: false
type: string
default: ""
tags:
description: Optional comma-separated tags override.
required: false
type: string
default: latest
registry:
description: ClawHub registry URL.
required: false
type: string
default: https://clawhub.ai
site:
description: ClawHub site URL.
required: false
type: string
default: https://clawhub.ai
ref:
description: Optional caller repository ref to check out.
required: false
type: string
default: ""
secrets:
clawhub_token:
required: false
outputs:
publish_json:
description: Structured JSON output from skill publishing.
value: ${{ jobs.publish.outputs.publish_json }}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
permissions: {}
jobs:
publish:
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
id-token: write
outputs:
publish_json: ${{ steps.capture.outputs.publish_json }}
steps:
- uses: actions/checkout@v6
with:
ref: ${{ inputs.ref || github.sha }}
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
with:
bun-version: 1.3.10
- name: Resolve ClawHub workflow source
id: clawhub_source
run: |
python3 - <<'PY'
import base64
import json
import os
from pathlib import Path
from urllib.request import Request, urlopen
request_token = os.environ.get("ACTIONS_ID_TOKEN_REQUEST_TOKEN", "").strip()
request_url = os.environ.get("ACTIONS_ID_TOKEN_REQUEST_URL", "").strip()
if not request_token or not request_url:
raise SystemExit("GitHub OIDC token request env vars are missing; id-token: write is required.")
audience = "clawhub-workflow-source"
joiner = "&" if "?" in request_url else "?"
request = Request(
f"{request_url}{joiner}audience={audience}",
headers={"Authorization": f"Bearer {request_token}"},
)
with urlopen(request) as response:
payload = json.load(response)
token = str(payload.get("value", "")).strip()
if not token:
raise SystemExit("GitHub OIDC token response did not include a token value.")
try:
encoded_payload = token.split(".")[1]
except IndexError as exc:
raise SystemExit("GitHub OIDC token was not a valid JWT.") from exc
padding = "=" * (-len(encoded_payload) % 4)
claims = json.loads(base64.urlsafe_b64decode(encoded_payload + padding).decode("utf-8"))
workflow_ref = str(claims.get("job_workflow_ref", "")).strip()
workflow_sha = str(claims.get("job_workflow_sha", "")).strip()
repo, marker, _ = workflow_ref.partition("/.github/workflows/")
if not marker or not repo or not workflow_sha:
raise SystemExit(
"Unable to resolve reusable workflow source from GitHub OIDC claims: "
f"job_workflow_ref={workflow_ref!r} job_workflow_sha={workflow_sha!r}"
)
with Path(os.environ["GITHUB_OUTPUT"]).open("a", encoding="utf-8") as fh:
fh.write(f"repository={repo}\n")
fh.write(f"ref={workflow_sha}\n")
PY
- uses: actions/checkout@v6
with:
repository: ${{ steps.clawhub_source.outputs.repository }}
ref: ${{ steps.clawhub_source.outputs.ref }}
path: clawhub-source
- name: Install ClawHub CLI dependencies
working-directory: clawhub-source
run: bun install --frozen-lockfile
- name: Validate publish mode inputs
env:
DRY_RUN: ${{ inputs.dry_run }}
CLAWHUB_TOKEN: ${{ secrets.clawhub_token }}
run: |
if [[ "$DRY_RUN" == "true" || -n "$CLAWHUB_TOKEN" ]]; then
exit 0
fi
echo "::error::Real skill publishes need secrets.clawhub_token. GitHub OIDC trusted publishing for skills is not supported yet."
exit 1
- name: Write ClawHub config
env:
CLAWHUB_TOKEN: ${{ secrets.clawhub_token }}
CLAWHUB_REGISTRY: ${{ inputs.registry }}
run: |
if [[ -z "$CLAWHUB_TOKEN" ]]; then
echo "No ClawHub token provided, skipping config file creation."
exit 0
fi
python3 - <<'PY'
import json
import os
from pathlib import Path
path = Path(os.environ["RUNNER_TEMP"]) / "clawhub-config.json"
path.write_text(
json.dumps({"registry": os.environ["CLAWHUB_REGISTRY"], "token": os.environ["CLAWHUB_TOKEN"]}, indent=2) + "\n",
encoding="utf-8",
)
print(path)
PY
echo "CLAWHUB_CONFIG_PATH=$RUNNER_TEMP/clawhub-config.json" >> "$GITHUB_ENV"
- name: Run skill publishes
env:
INPUT_SKILL_PATH: ${{ inputs.skill_path }}
INPUT_ROOT: ${{ inputs.root }}
INPUT_DRY_RUN: ${{ inputs.dry_run }}
INPUT_OWNER: ${{ inputs.owner }}
INPUT_TAGS: ${{ inputs.tags }}
INPUT_SITE: ${{ inputs.site }}
INPUT_REGISTRY: ${{ inputs.registry }}
INPUT_REF: ${{ inputs.ref }}
SOURCE_REPOSITORY: ${{ github.repository }}
SOURCE_REF: ${{ github.ref }}
run: |
python3 - <<'PY'
import json
import os
import subprocess
import sys
from pathlib import Path
workspace = Path(os.environ["GITHUB_WORKSPACE"]).resolve()
cli_entry = workspace / "clawhub-source" / "packages" / "clawhub" / "src" / "cli.ts"
if not cli_entry.is_file():
raise SystemExit(f"Missing ClawHub CLI entrypoint at {cli_entry}")
def resolve_inside_workspace(raw_path):
path = (workspace / raw_path).resolve()
try:
path.relative_to(workspace)
except ValueError as exc:
raise SystemExit(f"Publish path must be inside the caller repository: {raw_path}") from exc
return path
def is_skill_folder(path):
return path.is_dir() and any((path / name).is_file() for name in ("SKILL.md", "skill.md"))
skill_path = os.environ["INPUT_SKILL_PATH"].strip()
root_input = os.environ["INPUT_ROOT"].strip() or "skills"
if skill_path:
targets = [resolve_inside_workspace(skill_path)]
if not is_skill_folder(targets[0]):
raise SystemExit(f"skill_path is not a skill folder: {skill_path}")
else:
root = resolve_inside_workspace(root_input)
if is_skill_folder(root):
targets = [root]
elif root.is_dir():
targets = sorted(
(child for child in root.iterdir() if is_skill_folder(child)),
key=lambda child: child.name.lower(),
)
else:
targets = []
if not targets:
raise SystemExit(f"No skill folders found under: {root_input}")
source_commit = subprocess.check_output(
["git", "rev-parse", "HEAD"], cwd=workspace, text=True
).strip()
source_ref = os.environ["INPUT_REF"].strip() or os.environ["SOURCE_REF"].strip()
dry_run = os.environ["INPUT_DRY_RUN"] == "true"
owner = os.environ["INPUT_OWNER"].strip()
tags = os.environ["INPUT_TAGS"].strip()
results = {"wouldPublish": [], "published": [], "alreadySynced": [], "skipped": [], "failed": []}
status_keys = {
"would-publish": "wouldPublish",
"published": "published",
"unchanged": "alreadySynced",
}
for target in targets:
relative_path = target.relative_to(workspace).as_posix()
command = [
"bun", str(cli_entry),
"--workdir", str(workspace),
"--site", os.environ["INPUT_SITE"],
"--registry", os.environ["INPUT_REGISTRY"],
"skill", "publish", relative_path,
"--json",
"--source-repo", os.environ["SOURCE_REPOSITORY"],
"--source-commit", source_commit,
"--source-path", relative_path,
]
if dry_run:
command.append("--dry-run")
if owner:
command += ["--owner", owner]
if tags:
command += ["--tags", tags]
if source_ref:
command += ["--source-ref", source_ref]
completed = subprocess.run(command, cwd=workspace, capture_output=True, text=True)
if completed.returncode != 0:
message = completed.stderr.strip() or completed.stdout.strip() or f"exit {completed.returncode}"
results["failed"].append({"slug": target.name, "folder": relative_path, "message": message})
continue
try:
result = json.loads(completed.stdout)
results[status_keys[result["status"]]].append(result)
except (KeyError, ValueError, json.JSONDecodeError) as exc:
results["failed"].append({"slug": target.name, "folder": relative_path, "message": f"Invalid publish output: {exc}"})
output = {
"ok": not results["failed"],
"dryRun": dry_run,
"registry": os.environ["INPUT_REGISTRY"],
"roots": [skill_path or root_input],
**({"owner": owner.lstrip("@") } if owner else {}),
"summary": {key: len(value) for key, value in results.items()},
**results,
}
output_path = Path(os.environ["RUNNER_TEMP"]) / "skill-publish.json"
output_path.write_text(json.dumps(output, indent=2) + "\n", encoding="utf-8")
print(json.dumps(output, indent=2))
if results["failed"]:
sys.exit(1)
PY
- name: Capture workflow outputs
id: capture
run: |
python3 - <<'PY'
import json
import os
from pathlib import Path
output_path = Path(os.environ["RUNNER_TEMP"]) / "skill-publish.json"
parsed = json.loads(output_path.read_text(encoding="utf-8"))
with Path(os.environ["GITHUB_OUTPUT"]).open("a", encoding="utf-8") as fh:
fh.write("publish_json<<__CLAWHUB_JSON__\n")
fh.write(json.dumps(parsed, indent=2))
fh.write("\n__CLAWHUB_JSON__\n")
PY
- name: Upload publish JSON artifact
uses: actions/upload-artifact@v7
with:
name: clawhub-skill-publish-json
path: ${{ runner.temp }}/skill-publish.json
if-no-files-found: error
+171
View File
@@ -0,0 +1,171 @@
name: Stale
on:
schedule:
- cron: "17 3 * * *"
workflow_dispatch:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
permissions: {}
jobs:
stale:
permissions:
issues: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Mark stale unassigned issues and pull requests
uses: actions/stale@v10
with:
repo-token: ${{ github.token }}
days-before-issue-stale: 14
days-before-issue-close: 7
days-before-pr-stale: 7
days-before-pr-close: 5
stale-issue-label: stale
stale-pr-label: stale
exempt-issue-labels: enhancement,maintainer,pinned,security,no-stale,bad-barnacle
exempt-pr-labels: maintainer,no-stale,bad-barnacle
operations-per-run: 1000
ascending: true
exempt-all-assignees: true
remove-stale-when-updated: true
stale-issue-message: |
This issue has been automatically marked as stale due to inactivity.
Please add updated ClawHub details or it will be closed.
stale-pr-message: |
This pull request has been automatically marked as stale due to inactivity.
Please update it or it will be closed.
close-issue-message: |
Closing due to inactivity.
If this still affects ClawHub, reopen or file a new issue with the current URL, skill/package name, and fresh reproduction details.
close-issue-reason: not_planned
close-pr-message: |
Closing due to inactivity.
If this PR should be revived, reopen it with current context and a fresh validation plan.
- name: Mark stale assigned issues
uses: actions/stale@v10
with:
repo-token: ${{ github.token }}
days-before-issue-stale: 30
days-before-issue-close: 10
days-before-pr-stale: -1
days-before-pr-close: -1
stale-issue-label: stale
exempt-issue-labels: enhancement,maintainer,pinned,security,no-stale,bad-barnacle
operations-per-run: 1000
ascending: true
include-only-assigned: true
remove-stale-when-updated: true
stale-issue-message: |
This assigned issue has been automatically marked as stale after 30 days of inactivity.
Please add an update or it will be closed.
close-issue-message: |
Closing due to inactivity.
If this still affects ClawHub, reopen or file a new issue with current evidence.
close-issue-reason: not_planned
- name: Mark stale assigned pull requests
uses: actions/stale@v10
with:
repo-token: ${{ github.token }}
days-before-issue-stale: -1
days-before-issue-close: -1
days-before-pr-stale: 27
days-before-pr-close: 5
stale-pr-label: stale
exempt-pr-labels: maintainer,no-stale,bad-barnacle
operations-per-run: 1000
ascending: true
include-only-assigned: true
ignore-pr-updates: true
remove-stale-when-updated: true
stale-pr-message: |
This assigned pull request has been automatically marked as stale after being open for 27 days.
Please add an update or it will be closed.
close-pr-message: |
Closing due to inactivity.
If this PR should be revived, reopen it with current context and a fresh validation plan.
lock-closed-issues:
permissions:
issues: write
runs-on: ubuntu-latest
steps:
- name: Lock closed issues after 48h of no comments
uses: actions/github-script@v9
with:
github-token: ${{ github.token }}
script: |
const lockAfterHours = 48;
const lockAfterMs = lockAfterHours * 60 * 60 * 1000;
const cutoffMs = Date.now() - lockAfterMs;
const { owner, repo } = context.repo;
let locked = 0;
let inspected = 0;
let page = 1;
while (true) {
const { data: issues } = await github.rest.issues.listForRepo({
owner,
repo,
state: "closed",
sort: "updated",
direction: "desc",
per_page: 100,
page,
});
if (issues.length === 0) {
break;
}
for (const issue of issues) {
if (issue.pull_request || issue.locked || !issue.closed_at) {
continue;
}
inspected += 1;
const closedAtMs = Date.parse(issue.closed_at);
if (!Number.isFinite(closedAtMs) || closedAtMs > cutoffMs) {
continue;
}
let lastCommentMs = 0;
if (issue.comments > 0) {
const { data: comments } = await github.rest.issues.listComments({
owner,
repo,
issue_number: issue.number,
per_page: 1,
page: 1,
sort: "created",
direction: "desc",
});
if (comments.length > 0) {
lastCommentMs = Date.parse(comments[0].created_at);
}
}
if (Math.max(closedAtMs, lastCommentMs || 0) > cutoffMs) {
continue;
}
await github.rest.issues.lock({
owner,
repo,
issue_number: issue.number,
lock_reason: "resolved",
});
locked += 1;
}
page += 1;
}
core.info(`Inspected ${inspected} closed issues; locked ${locked}.`);
@@ -0,0 +1,121 @@
name: Update Convex AI Files
on:
schedule:
# Midnight Pacific during daylight saving time. GitHub cron uses UTC.
- cron: "0 7 * * 1"
workflow_dispatch:
concurrency:
group: update-convex-ai-files
cancel-in-progress: false
permissions: {}
env:
BUN_VERSION: "1.3.10"
UPDATE_BRANCH: automation/update-convex-ai-files
jobs:
update:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
with:
bun-version: ${{ env.BUN_VERSION }}
- uses: actions/create-github-app-token@v3
id: app-token
continue-on-error: true
with:
app-id: "2729701"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/create-github-app-token@v3
id: app-token-fallback
continue-on-error: true
if: steps.app-token.outcome == 'failure'
with:
app-id: "2971289"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Update Convex AI files
run: |
"$(bun pm bin)/convex" ai-files update
- name: Check Convex AI files status
run: |
"$(bun pm bin)/convex" ai-files status
- name: Detect changes
id: changes
run: |
set -euo pipefail
if [[ -n "$(git status --porcelain)" ]]; then
echo "changed=true" >> "$GITHUB_OUTPUT"
else
echo "changed=false" >> "$GITHUB_OUTPUT"
fi
- name: Commit and push update branch
if: steps.changes.outputs.changed == 'true'
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -B "$UPDATE_BRANCH"
git add AGENTS.md CLAUDE.md .agents/skills convex/_generated/ai/guidelines.md convex/_generated/ai/ai-files.state.json
git commit -m "chore: update Convex AI files"
git push --force-with-lease origin "$UPDATE_BRANCH"
- name: Open or update pull request
if: steps.changes.outputs.changed == 'true'
env:
GH_TOKEN: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token || github.token }}
run: |
set -euo pipefail
body_file="$(mktemp)"
{
printf '%s\n' '## Summary'
printf '\n'
printf '%s\n' '- refresh Convex-managed AI guidance files'
printf '%s\n' '- keep AGENTS.md / CLAUDE.md Convex sections in sync when Convex updates them'
printf '%s\n' '- update repo-local Convex developer skills under .agents/skills'
printf '\n'
printf '%s\n' '## Validation'
printf '\n'
# shellcheck disable=SC2016
printf '%s\n' '- `$(bun pm bin)/convex ai-files status`'
} > "$body_file"
if gh pr view "$UPDATE_BRANCH" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
gh pr edit "$UPDATE_BRANCH" \
--repo "$GITHUB_REPOSITORY" \
--title "[automation] Update Convex AI files" \
--body-file "$body_file"
else
gh pr create \
--repo "$GITHUB_REPOSITORY" \
--base main \
--head "$UPDATE_BRANCH" \
--title "[automation] Update Convex AI files" \
--body-file "$body_file"
fi
+23 -4
View File
@@ -2,6 +2,11 @@ node_modules
.DS_Store
.bun-build
*.bun-build
.artifacts/
artifacts/
.proof/
.cache/
.data/
bin/docs-list
dist
dist-ssr
@@ -10,7 +15,9 @@ dist-ssr
*.local
.vercel
count.txt
.env
.env*
!.env.local.example
!.env.example
.nitro
.tanstack
.wrangler
@@ -21,10 +28,22 @@ todos.json
.vscode
.env*.local
coverage
eval/cache/
eval/results/
playwright-report
test-results
.playwright
convex/_generated/
/public/llms.txt
convex/_generated/*
!convex/_generated/ai/
convex/_generated/ai/*
!convex/_generated/ai/guidelines.md
!convex/_generated/ai/ai-files.state.json
skills-lock.json
*/skills/*
skills/*
skills/*
.codex/*
!.codex/environments/
!.codex/environments/environment.toml
.crabbox/
/.comux-hooks
/.comux
+1
View File
@@ -11,6 +11,7 @@
"eslint-plugin-unicorn/prefer-array-find": "off",
"eslint-plugin-unicorn/no-array-sort": "off",
"eslint/no-await-in-loop": "off",
"eslint/no-underscore-dangle": "off",
"eslint/no-new": "off",
"oxc/no-accumulating-spread": "off",
"oxc/no-async-endpoint-handlers": "off",
Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

+21
View File
@@ -0,0 +1,21 @@
# ClawHub publisher search proof
Status: pass (production bug reproduction before backend deploy)
## Convex read-only validation (`wry-manatee-359.convex.cloud`)
| Query | `listPublicPage` handles |
| --- | --- |
| `vyctor` | `[]` |
| `vyctorbrzezowski` | `[]` |
| `vincent` | `["vincentchan"]` |
| `vincentkoc` | `[]` |
## Profiles that exist but are missing from search
- `vyctorbrzezowski` → 5 skills, 1 package, 46 installs
- `vincentkoc` → public profile, 0 published skills
## Unit tests
`VITE_CONVEX_URL=https://example.invalid bunx vitest run convex/publishers.test.ts`

Some files were not shown because too many files have changed in this diff Show More