diff --git a/docs/superpowers/plans/2026-06-19-qqbot-bilibili-card-plugin-implementation-plan.md b/docs/superpowers/plans/2026-06-19-qqbot-bilibili-card-plugin-implementation-plan.md
new file mode 100644
index 0000000..2b306c1
--- /dev/null
+++ b/docs/superpowers/plans/2026-06-19-qqbot-bilibili-card-plugin-implementation-plan.md
@@ -0,0 +1,2257 @@
+# QQBot Bilibili Card Plugin Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Build a QQBot built-in event plugin that parses Bilibili links from QQ/NapCat card messages and replies with a concise video summary.
+
+**Architecture:** Add a new third-phase plugin package under `src/modules/qqbot/plugins/bilibili-card`. Keep parsing and formatting in package-local domain files, keep Bilibili HTTP and short-link redirect access behind the generic plugin host bridge, and keep activation controlled by existing account plugin bindings.
+
+**Tech Stack:** NestJS 11 host platform, QQBot plugin worker thread runtime, Jest, TypeScript, Node `http`/`https` for host-mediated HTTP, existing `pnpm` scripts.
+
+---
+
+## File Structure
+
+Create:
+
+- `src/modules/qqbot/plugins/bilibili-card/plugin.json`
+ Built-in plugin manifest: key, event, runtime budget, permissions, config keys, no command operations.
+- `src/modules/qqbot/plugins/bilibili-card/src/index.ts`
+ Package entry. Exports only `createPlugin`.
+- `src/modules/qqbot/plugins/bilibili-card/src/domain/bilibili-card.types.ts`
+ Package-local message, host, config, video and reference types.
+- `src/modules/qqbot/plugins/bilibili-card/src/domain/bilibili-url-parser.ts`
+ Pure URL cleanup, domain allowlist and BV/av extraction.
+- `src/modules/qqbot/plugins/bilibili-card/src/domain/bilibili-url-extractor.ts`
+ Pure recursive extraction from normalized message and raw OneBot card payloads.
+- `src/modules/qqbot/plugins/bilibili-card/src/domain/bilibili-reply-formatter.ts`
+ Pure text reply formatting and number/duration formatting.
+- `src/modules/qqbot/plugins/bilibili-card/src/config/bilibili-card-config.ts`
+ Runtime config parsing from package host.
+- `src/modules/qqbot/plugins/bilibili-card/src/infrastructure/integration/bilibili-card-host.ts`
+ Package-local host adapter types and generic worker host calls.
+- `src/modules/qqbot/plugins/bilibili-card/src/infrastructure/integration/bilibili-video-client.ts`
+ Host-mediated Bilibili video API client.
+- `src/modules/qqbot/plugins/bilibili-card/src/application/bilibili-card-application.ts`
+ Binding check, dedupe, redirect resolution, API fetch, send and warn orchestration.
+- `src/modules/qqbot/plugins/bilibili-card/src/events/message/bilibili-card-message.handler.ts`
+ Event handler factory that delegates `message` events to the application.
+- `test/modules/qqbot/plugins/bilibili-card/bilibili-url-parser.spec.ts`
+- `test/modules/qqbot/plugins/bilibili-card/bilibili-url-extractor.spec.ts`
+- `test/modules/qqbot/plugins/bilibili-card/bilibili-video-client.spec.ts`
+- `test/modules/qqbot/plugins/bilibili-card/bilibili-card-application.spec.ts`
+- `test/modules/qqbot/plugin-platform/plugin-http-client.spec.ts`
+
+Modify:
+
+- `src/modules/qqbot/plugin-platform/infrastructure/integration/sdk/plugin-http-client.service.ts`
+ Add bounded `resolveRedirect` generic HTTP capability.
+- `src/modules/qqbot/plugin-platform/infrastructure/integration/runtime/plugin-host-bridge.service.ts`
+ Dispatch `resolveRedirect` host calls to the plugin HTTP client.
+- `src/modules/qqbot/plugin-platform/infrastructure/integration/runtime/plugin-worker.thread.ts`
+ Add an explicit argument mapper for `resolveRedirect`.
+- `test/modules/qqbot/plugin-platform/plugin-host-bridge.spec.ts`
+ Assert bridge delegates `resolveRedirect`.
+- `test/modules/qqbot/plugins/plugin-platform-migration.spec.ts`
+ Include `bilibili-card` in plugin discovery and manifest parse expectations.
+- `test/modules/qqbot/architecture/qqbot-plugin-package-boundary.spec.ts`
+ Include `bilibili-card` in approved built-in plugin keys and structure gates.
+- `test/modules/qqbot/architecture/qqbot-current-operation-matrix.spec.ts`
+ Freeze `bilibili-card` event capability and keep command seed checks command-only.
+- `sql/refactor-v3/01-seed-core.sql`
+ Seed `bilibili-card` plugin, version, installation and event handler metadata.
+- `sql/refactor-v3/99-verify.sql`
+ Add seed verification for `bilibili-card`.
+- `TASKS.md`
+ Record implementation evidence after code and verification.
+
+Do not create:
+
+- `src/modules/qqbot/plugin-platform/infrastructure/integration/builtins/**`
+- per-plugin Nest wrapper services
+- `src/modules/qqbot/plugins/bilibili-card/src/index.ts` re-export buckets
+- `.gitkeep`
+- command operation metadata for this plugin
+
+---
+
+### Task 1: Pure Bilibili URL Parser
+
+**Files:**
+- Create: `test/modules/qqbot/plugins/bilibili-card/bilibili-url-parser.spec.ts`
+- Create: `src/modules/qqbot/plugins/bilibili-card/src/domain/bilibili-card.types.ts`
+- Create: `src/modules/qqbot/plugins/bilibili-card/src/domain/bilibili-url-parser.ts`
+
+- [ ] **Step 1: Write the failing parser tests**
+
+Create `test/modules/qqbot/plugins/bilibili-card/bilibili-url-parser.spec.ts`:
+
+```ts
+import {
+ cleanBilibiliUrlCandidate,
+ isAllowedBilibiliUrl,
+ parseBilibiliVideoReference,
+} from '../../../../../src/modules/qqbot/plugins/bilibili-card/src/domain/bilibili-url-parser';
+
+describe('Bilibili URL parser', () => {
+ it('parses BV video URLs while ignoring query, hash, and trailing punctuation', () => {
+ const reference = parseBilibiliVideoReference(
+ 'https://www.bilibili.com/video/BV1xx411c7mD/?share_source=qq#reply。',
+ );
+
+ expect(reference).toEqual({
+ canonicalVideoId: 'BV1xx411c7mD',
+ kind: 'bvid',
+ sourceUrl:
+ 'https://www.bilibili.com/video/BV1xx411c7mD/?share_source=qq#reply',
+ value: 'BV1xx411c7mD',
+ });
+ });
+
+ it('parses av video URLs from mobile Bilibili links', () => {
+ expect(
+ parseBilibiliVideoReference('https://m.bilibili.com/video/av170001'),
+ ).toMatchObject({
+ canonicalVideoId: 'av170001',
+ kind: 'aid',
+ value: '170001',
+ });
+ });
+
+ it('allows only Bilibili and b23.tv hosts', () => {
+ expect(isAllowedBilibiliUrl('https://b23.tv/abc123')).toBe(true);
+ expect(isAllowedBilibiliUrl('https://space.bilibili.com/1')).toBe(true);
+ expect(isAllowedBilibiliUrl('https://example.com/video/BV1xx411c7mD')).toBe(
+ false,
+ );
+ });
+
+ it('cleans card wrappers, html entities, and trailing brackets', () => {
+ expect(
+ cleanBilibiliUrlCandidate(
+ '"https://www.bilibili.com/video/BV1xx411c7mD?p=1")',
+ ),
+ ).toBe('https://www.bilibili.com/video/BV1xx411c7mD?p=1');
+ });
+});
+```
+
+- [ ] **Step 2: Run the parser tests and verify RED**
+
+Run:
+
+```powershell
+pnpm exec jest --runTestsByPath test/modules/qqbot/plugins/bilibili-card/bilibili-url-parser.spec.ts --runInBand
+```
+
+Expected: FAIL because `bilibili-url-parser.ts` does not exist.
+
+- [ ] **Step 3: Add parser types and minimal implementation**
+
+Create `src/modules/qqbot/plugins/bilibili-card/src/domain/bilibili-card.types.ts`:
+
+```ts
+export type BilibiliVideoReference =
+ | {
+ canonicalVideoId: string;
+ kind: 'bvid';
+ sourceUrl: string;
+ value: string;
+ }
+ | {
+ canonicalVideoId: string;
+ kind: 'aid';
+ sourceUrl: string;
+ value: string;
+ };
+```
+
+Create `src/modules/qqbot/plugins/bilibili-card/src/domain/bilibili-url-parser.ts`:
+
+```ts
+import type { BilibiliVideoReference } from './bilibili-card.types';
+
+const ALLOWED_HOSTS = new Set([
+ 'b23.tv',
+ 'bilibili.com',
+ 'm.bilibili.com',
+ 'www.bilibili.com',
+]);
+
+const TRAILING_WRAPPERS = /[\s"'<>,。!?、;:))\]}]+$/u;
+const LEADING_WRAPPERS = /^[\s"'<>(([{]+/u;
+const BVID_PATTERN = /(?:^|\/)(BV[0-9A-Za-z]{10,})/;
+const AID_PATTERN = /(?:^|\/)(?:av|AV)(\d+)(?:$|[/?#])/;
+
+/**
+ * Removes QQ card wrappers and punctuation that often stick to copied URLs.
+ * @param candidate - Raw string fragment found in text or card JSON.
+ * @returns Cleaned URL candidate ready for `URL` parsing.
+ */
+export function cleanBilibiliUrlCandidate(candidate: string) {
+ return candidate
+ .replaceAll('&', '&')
+ .replaceAll('"', '"')
+ .replaceAll('"', '"')
+ .replace(LEADING_WRAPPERS, '')
+ .replace(TRAILING_WRAPPERS, '')
+ .trim();
+}
+
+/**
+ * Checks whether a URL belongs to the Bilibili domains this plugin is allowed to parse.
+ * @param candidate - URL string collected from a QQ message or redirect result.
+ * @returns `true` when the host is Bilibili-owned or `b23.tv`.
+ */
+export function isAllowedBilibiliUrl(candidate: string) {
+ try {
+ const url = new URL(cleanBilibiliUrlCandidate(candidate));
+ return (
+ url.protocol === 'http:' ||
+ url.protocol === 'https:'
+ ) && ALLOWED_HOSTS.has(url.hostname.toLowerCase());
+ } catch {
+ return false;
+ }
+}
+
+/**
+ * Extracts a Bilibili video identifier from a supported URL.
+ * @param candidate - Direct Bilibili video URL or short-link URL that already embeds BV/av.
+ * @returns Parsed video reference, or `null` when the URL is not a supported video URL.
+ */
+export function parseBilibiliVideoReference(
+ candidate: string,
+): BilibiliVideoReference | null {
+ const cleaned = cleanBilibiliUrlCandidate(candidate);
+ if (!isAllowedBilibiliUrl(cleaned)) return null;
+
+ const url = new URL(cleaned);
+ const probe = `${url.pathname}${url.search}${url.hash}`;
+ const bvid = probe.match(BVID_PATTERN)?.[1];
+ if (bvid) {
+ return {
+ canonicalVideoId: bvid,
+ kind: 'bvid',
+ sourceUrl: cleaned,
+ value: bvid,
+ };
+ }
+
+ const aid = `${url.pathname}/`.match(AID_PATTERN)?.[1];
+ if (aid) {
+ return {
+ canonicalVideoId: `av${aid}`,
+ kind: 'aid',
+ sourceUrl: cleaned,
+ value: aid,
+ };
+ }
+
+ return null;
+}
+```
+
+- [ ] **Step 4: Run the parser tests and verify GREEN**
+
+Run:
+
+```powershell
+pnpm exec jest --runTestsByPath test/modules/qqbot/plugins/bilibili-card/bilibili-url-parser.spec.ts --runInBand
+```
+
+Expected: PASS.
+
+- [ ] **Step 5: Commit parser slice**
+
+```powershell
+git add test/modules/qqbot/plugins/bilibili-card/bilibili-url-parser.spec.ts src/modules/qqbot/plugins/bilibili-card/src/domain/bilibili-card.types.ts src/modules/qqbot/plugins/bilibili-card/src/domain/bilibili-url-parser.ts
+git commit -m "feat: 添加Bilibili链接解析域逻辑"
+```
+
+---
+
+### Task 2: RawEvent URL Extractor
+
+**Files:**
+- Create: `test/modules/qqbot/plugins/bilibili-card/bilibili-url-extractor.spec.ts`
+- Modify: `src/modules/qqbot/plugins/bilibili-card/src/domain/bilibili-card.types.ts`
+- Create: `src/modules/qqbot/plugins/bilibili-card/src/domain/bilibili-url-extractor.ts`
+
+- [ ] **Step 1: Write the failing extractor tests**
+
+Create `test/modules/qqbot/plugins/bilibili-card/bilibili-url-extractor.spec.ts`:
+
+```ts
+import { extractBilibiliUrls } from '../../../../../src/modules/qqbot/plugins/bilibili-card/src/domain/bilibili-url-extractor';
+
+describe('Bilibili URL extractor', () => {
+ it('extracts links from messageText and rawMessage while deduplicating them', () => {
+ expect(
+ extractBilibiliUrls({
+ messageText:
+ '看看 https://www.bilibili.com/video/BV1xx411c7mD',
+ rawMessage:
+ '重复 https://www.bilibili.com/video/BV1xx411c7mD?share=qq',
+ rawEvent: {},
+ }),
+ ).toEqual([
+ 'https://www.bilibili.com/video/BV1xx411c7mD',
+ 'https://www.bilibili.com/video/BV1xx411c7mD?share=qq',
+ ]);
+ });
+
+ it('extracts a QQ share card URL', () => {
+ const urls = extractBilibiliUrls({
+ messageText: '',
+ rawMessage: '',
+ rawEvent: {
+ message: [
+ {
+ data: {
+ content: '夏祭 视频',
+ title: 'Bilibili',
+ url: 'https://www.bilibili.com/video/BV1xx411c7mD',
+ },
+ type: 'share',
+ },
+ ],
+ },
+ });
+
+ expect(urls).toEqual(['https://www.bilibili.com/video/BV1xx411c7mD']);
+ });
+
+ it('extracts nested URLs from json and lightapp cards', () => {
+ const payload = JSON.stringify({
+ app: 'com.tencent.structmsg',
+ meta: {
+ detail: {
+ jumpUrl: 'https://b23.tv/abc123',
+ },
+ },
+ });
+
+ expect(
+ extractBilibiliUrls({
+ messageText: '',
+ rawMessage: '',
+ rawEvent: {
+ message: [
+ { data: { data: payload }, type: 'json' },
+ { data: { data: payload }, type: 'lightapp' },
+ ],
+ },
+ }),
+ ).toEqual(['https://b23.tv/abc123']);
+ });
+
+ it('extracts URLs from xml card text and ignores non-Bilibili URLs', () => {
+ expect(
+ extractBilibiliUrls({
+ messageText: 'https://example.com/video/BV1xx411c7mD',
+ rawMessage: '',
+ rawEvent: {
+ message: [
+ {
+ data: {
+ data: '',
+ },
+ type: 'xml',
+ },
+ ],
+ },
+ }),
+ ).toEqual(['https://m.bilibili.com/video/av170001']);
+ });
+});
+```
+
+- [ ] **Step 2: Run the extractor tests and verify RED**
+
+Run:
+
+```powershell
+pnpm exec jest --runTestsByPath test/modules/qqbot/plugins/bilibili-card/bilibili-url-extractor.spec.ts --runInBand
+```
+
+Expected: FAIL because `bilibili-url-extractor.ts` does not exist.
+
+- [ ] **Step 3: Add extraction types and implementation**
+
+Extend `src/modules/qqbot/plugins/bilibili-card/src/domain/bilibili-card.types.ts`:
+
+```ts
+export type BilibiliUrlExtractionInput = {
+ messageText?: string;
+ rawEvent?: Record;
+ rawMessage?: string;
+};
+```
+
+Create `src/modules/qqbot/plugins/bilibili-card/src/domain/bilibili-url-extractor.ts`:
+
+```ts
+import type { BilibiliUrlExtractionInput } from './bilibili-card.types';
+import {
+ cleanBilibiliUrlCandidate,
+ isAllowedBilibiliUrl,
+} from './bilibili-url-parser';
+
+const URL_PATTERN = /https?:\/\/[^\s<>"',。!?;、]+/giu;
+const MAX_DEPTH = 7;
+
+/**
+ * Extracts Bilibili URL candidates from normalized message text and raw QQ card payloads.
+ * @param input - Normalized QQBot message fields and raw OneBot event payload from NapCat.
+ * @returns Unique allowed Bilibili URL strings in discovery order.
+ */
+export function extractBilibiliUrls(input: BilibiliUrlExtractionInput) {
+ const candidates = collectStringCandidates(input);
+ const seen = new Set();
+ const output: string[] = [];
+
+ for (const text of candidates) {
+ for (const rawUrl of text.match(URL_PATTERN) || []) {
+ const cleaned = cleanBilibiliUrlCandidate(rawUrl);
+ if (!isAllowedBilibiliUrl(cleaned) || seen.has(cleaned)) continue;
+ seen.add(cleaned);
+ output.push(cleaned);
+ }
+ }
+
+ return output;
+}
+
+/**
+ * Collects string values that may contain links from text fields and nested QQ card objects.
+ * @param input - Extraction input built from normalized message state.
+ * @returns Candidate strings that may contain URLs.
+ */
+function collectStringCandidates(input: BilibiliUrlExtractionInput) {
+ const output: string[] = [];
+ const seen = new WeakSet