Add template marketplace with EN/ZH agent libraries - #7
Merged
Conversation
Adds a "template marketplace" drawer reachable from the Add Worker dialog.
Users browse pre-built role prompts by category, preview the markdown body,
and import a chosen agent into the worker form with one click. The two
upstream prompt libraries are vendored into vendor/marketplace/{en,zh}/ so
the runtime works offline; pnpm sync:marketplace refreshes the snapshot.
Bundles snapshots of two MIT-licensed community libraries:
- en: msitarzewski/agency-agents (184 agents)
- zh: jnMetaCode/agency-agents-zh (216 agents)
Source-language switches with the UI language. Imported agents land in
the user's role_templates table as 'custom'; no DB schema change, no new
auth surface — read-only routes wired through routes.ts.
Key pieces:
- scripts/sync-marketplace.mjs: single-tarball fetch via gh CLI, atomic swap
- src/server/marketplace-store.ts: env-overridable vendor root for tests,
isPathWithinRoot guard against traversal
- src/server/routes-marketplace.ts: GET /api/marketplace/{manifest,agent}
- web/src/marketplace/: drawer, category tree, card grid, preview pane
- web/src/worker/useWorkerComposer.ts: applyMarketplaceImport batches
form-state writes to avoid the workerRole-change useEffect clobbering
the imported description
- README + README.en: attribution to both upstream repos
Tests: 19 new (12 server route, 5 drawer, 2 wire-up integration); 789/789
full suite green, biome clean.
Five polish items on top of the marketplace MVP: - Localized category names via categoryLabels.ts (engineering → 工程, etc). Bilingual dictionary lives outside the i18n catalog so an unknown upstream category falls back to a prettified raw label instead of throwing. - AgentCard now uses the upstream `color` field as a 4px left rail, lifts the emoji to text-lg, and bolds the name. Already-imported agents (matching by name against the local customTemplates) show a ✓ pill. - AgentPreview swaps the raw <pre> body for marked + isomorphic-dompurify through .marketplace-prose CSS — proper headings, lists, code, links — while keeping the same import-flow disabled state on the button. - Toast feedback after import (`已把 X 填入表单` / `Imported X into the form`) so users don't have to scroll the form to verify the fill landed. - AddWorkerDialog wraps the marketplace onImport handler with the toast call and passes a memoized Set of customTemplate names to the drawer for the ✓ detection. Tests: 2 new (one for imported badge, one for toast trigger). Full suite 791/791 green, biome clean. No backend / route / DB changes.
Five rounds of UX feedback from three reviewing subagents collapsed into
the four highest-ROI fixes:
- Card surface dropped from --bg-elevated to --bg-2 so cards visibly sit
on the drawer "table" instead of blending into it. Selected cards now
use color-mix(accent 14%) + accent border. Removed the per-card 4px
color rail — at 200+ cards it looked like a sticker book and the data
was already in the emoji + name.
- Default category view filters to seven code-relevant categories
(engineering, design, product, testing, project-management,
specialized, integrations). Off-topic categories (marketing,
game-development, academic, etc.) collapse behind a "Show all
categories (+N)" toggle in the category tree footer.
- Three category labels rewritten for clarity:
specialized → Industry Verticals / 行业角色
paid-media → Paid Ads / 广告投放
integrations → System Integrations / 系统集成
- sync-marketplace.mjs now extracts the upstream `vibe` frontmatter field
(present on 182/184 EN agents, 0/216 ZH). AgentCard surfaces vibe in
place of description when available — much more flavorful at a glance.
Type added to MarketplaceAgentEntry in both web/src/api.ts and
src/server/marketplace-store.ts.
Plus the round-1 fix already in the branch: drawer width 1100→1280,
preview pane collapses when nothing is selected, card grid switched to
auto-fill minmax(220px, 1fr).
Tests: 1 new (toggle reveals + folds non-core). Full suite 792/792
green, biome clean.
Three reviewing subagents agreed the biggest remaining gaps were
EN/ZH card asymmetry (EN had snappy taglines, ZH read like a corporate
directory), card density still wasting vertical space, two latent
selection-stale bugs, and a few ergonomic gaps. Eight fixes:
- sync-marketplace.mjs grew an extractVibe() pass:
• EN: still uses fm.vibe, but rejects entries > 140 chars (they
were getting line-clamped to noise anyway).
• ZH: regex-extracts the **个性**: ... line from the markdown body
where upstream provides it. ZH vibe coverage jumped 0 → 183/216
(84%), closing the EN/ZH asymmetry without any per-agent curation.
• EN vibe net: 170 (180+ minus the >140 outliers).
- AgentCard tagline switched from line-clamp-2 to line-clamp-1; card
height drops, ~50% more cards visible per scroll.
- AgentCard gained a focus-visible accent ring so keyboard nav has a
visible target. Tab-through is still long; arrow-key roving tabindex
deferred.
- Imported ✓ pill flips to solid var(--accent) + white when the card is
also selected — fixes the case where pill bg (accent 18%) dissolved
into the selected-card bg (accent 14%).
- Drawer width changed from fixed w-[1280px] to min(1280px, calc(100vw
- 32px)) so small laptops don't overflow.
- Sidebar widened 180→200px; "System Integrations" was clipping on EN.
- Show-all toggle now sits below a border-t divider with text-sec rest,
reads as a real action instead of filler.
- Two real bugs fixed in MarketplaceDrawer:
• Switching UI language mid-session left selectedAgent / selectedCategory
pointing at the old-language manifest. Preview would silently 404.
Added a useEffect on [language] that resets both.
• Collapsing "show all" back to core view left selectedAgent visible in
the preview pane even when its category had just disappeared. Now
cleared along with selectedCategory.
- ZH category sort: upstream emits categories alphabetically by EN
kebab key, which renders as random order to ZH readers. Added a
static ZH_CATEGORY_ORDER (工程→测试→产品→设计... → 学术→游戏
开发→空间计算→行业角色) applied to ZH only; EN stays alphabetical.
Tests: 1 new (collapsing all-categories clears stale selection). Full
suite 793/793 green, biome clean. No backend, schema, or i18n key
removal.
Three subagents flagged that bg-3 and bg-elevated are both #222, so every `hover:bg-3` on the drawer container was a silent no-op — categories, toggle, browse button, and cards all had invisible hover/active feedback. Eleven micro-fixes: - Category sidebar rewritten — rows now use explicit accent-mix wash for rest/hover/selected with a 2px accent inset on selected. Counts and tagline bumped from text-tertiary to text-secondary so they read as data, not chrome. Show-all toggle wrapped in a divider div instead of the broken mt-2+borderRadius:0 inline cocktail. - Card hover/active styles added via .marketplace-card CSS so cards actually look clickable. Selected cards skip the hover wash since they already carry an accent border. - AddWorkerDialog "Browse marketplace" button — explicit bg-0 + bright border, accent-mix hover, focus-visible ring. - 3 overflow-y-auto regions (sidebar / grid / preview body) swapped for the project's themed `.scroll-y` class so they pick up the thin dark scrollbar instead of OS chrome. - Grid section now re-keyed by selectedCategory; switching category resets scroll to top instead of leaving content jumping under cursor. - Preview body re-keyed by agent.path with the same effect. - Preview inner border dropped — outer border-l + inner rounded panel was double-bordered; now just bg-2 panel. - Import button disabled state went from opacity-50 (looked like hover) to neutral bg-3 + text-tertiary surface. - "View source" link styled as secondary text with ExternalLink icon instead of tertiary underlined (was reading as a dead link). - Imported pill unselected: bg accent 18%→28%, text accent 60%-white blend — bumps contrast from 2.4:1 (below AA) into readable range. - Padding unified: header was px-5 while body sections were px-3, an 8px notch. Everything is px-4 now. - Loading / error / empty states wrapped in flex h-full center so they occupy the grid pane instead of sitting top-left as a lonely "…". New i18n key marketplace.loading (Loading… / 加载中…). Tests: 793/793 green, biome clean. No backend or schema changes.
Three subagents on typography, microcopy, and edge-case bugs flagged 15
finds. Land all of them.
Real bugs:
- Search query now resets when switching category or UI language.
Previously a "redis" filter would silently empty the grid after
changing context with no clue to the user.
- Preview h3 long agent names get `break-words + min-w-0`. Long
upstream titles no longer push the preview column wider than 380px.
- `readAgent` adds a `statSync().isFile()` guard before reading. If
upstream ever pushes a directory whose name ends in `.md`, the route
returns 404 instead of crashing with EISDIR.
- `marked.parse(body, { async: false })` is now called per-site with
an explicit narrow type check. If a future marked upgrade flips the
default to async, we fail loudly instead of rendering `[object
Promise]` through DOMPurify.
Copy:
- Import button: "Import as template" → "Use this agent" / "导入为模板"
→ "使用此角色". No template is actually persisted on click, so the
old verb was lying about the action.
- Toast: "Imported {name} into the form" → "Filled the form from
{name}". ZH was already correct.
- "View source" → "View on GitHub" (matches the existing ZH wording).
- "Source: {repo}" → "Curated from {repo}" / "由 {repo} 提供".
Reframes a raw GitHub slug as attribution instead of metadata noise.
- Show-all toggle: "Show all categories (+11)" → "Show 11 more
categories" / "显示其他 11 个分类". The (+N) pattern read as math.
- ZH "只看常用分类" → "只看核心分类". "常用" implies user history;
the actual filter is a curator-picked subset.
- ✓ pill gets a proper aria-label + tooltip via a new
`marketplace.importedBadge` key ("You have a saved template with
this name" / "本地已有同名模板"). Doesn't fix the false-positive
detection but at least the user knows what the badge means.
Typography:
- Drawer title `text-base` → `text-lg`. Restores the 16/13/12/11
ladder from modal → preview → card → tagline.
- Source repo slug rendered in DM Mono via a `<span className="mono">`
inside `Dialog.Description`. `owner/repo` is a path, not prose;
the slash + descenders read cleanly in mono.
- ✓ pill `tracking-wide` → `tracking-wider` for 10px uppercase
legibility.
- Card tagline `text-[11px]` → `text-xs` (12px) to match preview body
font-size and ditch the inline literal.
- Selected category row gets `font-medium` on top of the existing
color swap, mirroring the workspace-row anchor pattern.
Tests: 793/793 green, biome clean. No backend, schema, or test
breakage.
Three subagents on dialog-family consistency, first-time-user friction, and vendor content quality. Eleven fixes: Family consistency (marketplace now reuses hive's design tokens instead of one-off styles): - Import button: bespoke inline-style block → `.icon-btn--primary`, the same primary CTA shape used in every other dialog footer. - Search input: custom wrapper div → `.input` class with the Search icon overlaid via padding. Border / radius / placeholder weight now match the rest of the form-input family. - Close X: from bordered 8x8 filled button to ghost-style 7x7 matching `ServerBrowseDialog`'s top-right close. - Header padding: `px-4 py-3` → `px-5 py-4` to match every other dialog header in the family. Copy + iconography: - Import button copy: "Use this agent" → "Fill into form" / "使用此角色" → "填入表单". "Use" implied install/commit when the actual effect is form pre-fill. - "Imported" pill icon: `Check` → `Bookmark`. A green check on a catalog card universally means "you've installed/used this", but the badge actually means "you have a local template with the same name". Different semantics, different icon. Sync-script quality (`scripts/sync-marketplace.mjs`): - `extractVibe` gained a description-lead fallback. When neither the vibe frontmatter nor the ZH **个性** body line is present, snip the first clause from `description` (split on em-dash / Chinese semicolons / "—精通/专注/covering/specializing" style joiners). EN vibe coverage 170→179; ZH 183→215 (215/216 = near-complete). - `looksLocalized` filter drops one half-translated entry (`integrations/mcp-memory/backend-architect-with-memory.md` in ZH ships English name + no emoji). ZH agent count 216→215. - `nameOverflows` flag on every manifest entry (CJK-aware visual- length, > 22 cells). UI uses it to set `title=` on the truncated name span so users can hover for the full name. EN 71 / ZH 4 agents marked. - Cross-category duplicate names get a `displayName: "X (category)"` so e.g. "Backend Architect" in `engineering/` doesn't visually collide with "Backend Architect" in `integrations/mcp-memory/`. Two collisions per language. `MarketplaceAgentEntry` extended with `displayName?: string` and `nameOverflows?: boolean` in both `web/src/api.ts` and `src/server/marketplace-store.ts`. Card uses `displayName ?? name` and sets `title` when `nameOverflows`. Tests: 793/793 green, biome clean (1 false-positive control-char warning on the visualLength regex rewritten to use codePointAt).
`.input` ships a `padding: 8px 12px` shorthand in unlayered CSS that out-cascades a Tailwind `pl-9` utility in v4 (unlayered wins over @layer utilities). Move the left-padding override to inline style so the placeholder lands at 36px and clears the absolute-positioned Search icon at 12px.
huangserva
added a commit
to huangserva/hive
that referenced
this pull request
May 26, 2026
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
huangserva
added a commit
to huangserva/hive
that referenced
this pull request
May 26, 2026
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
huangserva
added a commit
to huangserva/hive
that referenced
this pull request
May 29, 2026
…-a1i#6 tt-a1i#7) Bugs surfaced by an adversarial bug-finding workflow on the archived repo, then triaged against current hive-serva (all confirmed still present). Each fix ships with anti-fake-verified tests. - tt-a1i#1 agent-run-sync: reuse the first endedAt on terminal runs so post-exit syncPersistedRun stops pushing the DB endedAt forward every poll. - tt-a1i#7 agent-runtime: mark a run stopRequested before SIGTERM and exclude such runs from the active-run check, so stop-then-start spawns a fresh run instead of returning the dying one. - tt-a1i#4 terminal-stream-hub: only close/null viewer.flowState when the closing socket is the current io socket; a late close from an old socket no longer kills the new connection's active flow. - tt-a1i#5 terminal-flow-control: count bytes sent while paused so unacked accounting cannot drift below the real in-flight amount. - tt-a1i#6 session-capture: thread the identity discriminator into the codex/gemini capture path (parity with claude) so concurrent same-workspace agents stop grabbing each other's session id. Bugs tt-a1i#2 tt-a1i#3 tt-a1i#8 live in files (team-operations.ts, sqlite-schema.ts) that are entangled with uncommitted M24 server work and are deferred until that lands. Report: .hive/reports/archived-bug-triage-vs-current-2026-05-29.html Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
huangserva
added a commit
to huangserva/hive
that referenced
this pull request
Jun 17, 2026
…t-a1i#7 重启全丢) bug 普查(andy 工作流)+钟馗多轮复核的两高危(同根=approval 状态非持久):tt-a1i#1 resolve 先删内存 ledger、注入 orch ALLOW/DENY fire-and-forget,失败只 log 但 user 已见"已处理"→高风险放行决策丢失;tt-a1i#7 ledger 纯内存 Map 重启全丢,三入口收不了尾、DB approval_request 残留。 修:approval 状态进 SQLite(feishu_approvals,schema v35+v36)。resolve 原子抢占(UPDATE WHERE status=pending→resolving,持久化 decision)——相反 decision 不可翻盘、同 decision 重试幂等;注入成功才 markResolved,失败保留可重试、不假成功。mobile/relay/飞书三入口一致 + 重启穿透测试。钟馗 2 轮独立审(逐出"相反 decision 翻盘"blocking)。 known-limitation:同 decision at-least-once 重复注入(相反翻盘已堵),后续加 approval_id delivery 幂等。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
huangserva
added a commit
to huangserva/hive
that referenced
this pull request
Jun 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a "template marketplace" drawer reachable from the Add Worker dialog. Users browse pre-built role prompts by category, preview the markdown body, and import a chosen agent into the worker form with one click. Snapshots of two MIT-licensed community libraries ride along in
vendor/marketplace/so the runtime works offline.UI language switches the source repo automatically. Imported agents land in the existing
role_templatestable ascustom— no DB schema change, no new auth surface. The marketplace is read-only; routes wire throughroutes.tslike any other endpoint.What's in the diff
scripts/sync-marketplace.mjs— single-tarball fetch viagh, atomic swap. Runpnpm sync:marketplacebefore each release. Note:vendor/marketplace/accounts for ~5.8 MB / ~400 markdown files in this PR — feel free to skip that path while reviewing.src/server/marketplace-store.ts(vendor path invariant +HIVE_MARKETPLACE_VENDOR_ROOTtest override +isPathWithinRootguard),src/server/routes-marketplace.ts(GET/api/marketplace/{manifest,agent})web/src/marketplace/drawer + category tree + card grid + preview,useMarketplacehook (manifest + agent body cache, ~2MB session ceiling)useWorkerComposer.applyMarketplaceImportbatches form-state writes to avoid theworkerRole-change useEffect clobbering the imported descriptionTests
routes-marketplace), 5 drawer, 2 wire-up integration (add-worker-marketplace-import)web/src/worker/AddWorkerDialog.tsx:181,189are pre-existingexactOptionalPropertyTypesissues unrelated to this PRTest plan
pnpm install(gray-matter is a new dep)pnpm vitest run— should be 789/789pnpm dev— open Add Worker dialog, click "Browse marketplace", verify drawer opens with 184 EN agentspnpm buildto verifyprepare-build-artifacts.mjscopiesvendor/marketplace/intodist/vendor/Out of scope (intentional)
customemoji/color/vibepersisted to DB — no UI surfaces for thempnpm sync:marketplaceonly