Topic Bookmarks Reorganizer(中文) 把一个书签导出文件中的目标主题目录重整为更清晰、可导入的新文件。 何时使用 当用户有以下需求时使用本技能: - 分析一个书签导出 HTML,并定位用户指定的主题目录 - 重新分门别类该目录下的链接与子目录 - 按 URL 去重 - 输出只包含该主题目录的可导入 HTML 不要使用 以下场景不应使用本技能: - 输入不是浏览器书签导出 HTML - 用户仅需要文字建议,不需要处理文件 - 用户需求与书签整理无关(如纯 JSON/PDF/Docx 处理) 使用说明 1. 先向用户确认必要参数: - 输入书签文件路径 - 主题目录名称 - 输出文件路径 2. 先跑分析与预览: 3. 视情况调整参数: - :自动选择分类策略 - :使用通用分类策略 - :不做 URL 去重 4. 生成最终文件: 5. 交付前检查并汇报: - 确认输出文件存在 - 汇总输入链接数、输出链接数、去重移除数 - 确认输出仅包含一个顶层目录(目标主题目录) 输出要求 - 输出必须是 Netscape 书签格式,可直接导入浏览器 - 输出仅保留目标主题目录 - 尽量保留原 属性(如 add-date/icon) - 目录结构按高层分类重组,便于后续维护 ---

)\nA_RE = re.compile(r'^(?P\u003cindent>\\s*)\u003cDT>\u003cA\\s+(?P\u003cattrs>[^>]*)>(?P\u003ctitle>.*?)\u003c/A>\\s*

Topic Bookmarks Reorganizer(中文) 把一个书签导出文件中的目标主题目录重整为更清晰、可导入的新文件。 何时使用 当用户有以下需求时使用本技能: - 分析一个书签导出 HTML,并定位用户指定的主题目录 - 重新分门别类该目录下的链接与子目录 - 按 URL 去重 - 输出只包含该主题目录的可导入 HTML 不要使用 以下场景不应使用本技能: - 输入不是浏览器书签导出 HTML - 用户仅需要文字建议,不需要处理文件 - 用户需求与书签整理无关(如纯 JSON/PDF/Docx 处理) 使用说明 1. 先向用户确认必要参数: - 输入书签文件路径 - 主题目录名称 - 输出文件路径 2. 先跑分析与预览: 3. 视情况调整参数: - :自动选择分类策略 - :使用通用分类策略 - :不做 URL 去重 4. 生成最终文件: 5. 交付前检查并汇报: - 确认输出文件存在 - 汇总输入链接数、输出链接数、去重移除数 - 确认输出仅包含一个顶层目录(目标主题目录) 输出要求 - 输出必须是 Netscape 书签格式,可直接导入浏览器 - 输出仅保留目标主题目录 - 尽量保留原 属性(如 add-date/icon) - 目录结构按高层分类重组,便于后续维护 ---

)\nHREF_RE = re.compile(r'HREF=\"([^\"]+)\"', re.IGNORECASE)\n\nLANG_LABELS = {\n \"zh\": {\n \"coding\": \"01 编程开发\",\n \"platform\": \"02 模型与平台\",\n \"agent\": \"03 Agent与协议自动化\",\n \"design\": \"04 设计与多媒体\",\n \"learning\": \"05 学习与内容\",\n \"nav\": \"06 榜单与导航\",\n \"account\": \"07 商业与账号\",\n \"ops\": \"08 工具与平台\",\n \"community\": \"09 社区与讨论\",\n \"backlog\": \"99 待整理\",\n \"uncategorized\": \"未分类\",\n },\n \"en\": {\n \"coding\": \"01 Coding Development\",\n \"platform\": \"02 Models Platforms\",\n \"agent\": \"03 Agent Protocol Automation\",\n \"design\": \"04 Design Multimedia\",\n \"learning\": \"05 Learning Content\",\n \"nav\": \"06 Rankings Navigation\",\n \"account\": \"07 Commercial Accounts\",\n \"ops\": \"08 Tools Platforms\",\n \"community\": \"09 Community Discussion\",\n \"backlog\": \"99 Backlog\",\n \"uncategorized\": \"Uncategorized\",\n },\n}\n\n\nAI_CODING_SECTIONS = {\n \"CLI\", \"Copilot\", \"Cursor\", \"kat-coder\", \"长亭百智云-MonkeyCode\", \"claude-cowork\", \"ZenMux\",\n}\n\nAI_PLATFORM_SECTIONS = {\n \"Anthropic\", \"OpenAI\", \"智谱AI\", \"mini max\", \"Google\", \"Manus\", \"DeepSeek\", \"Grok\", \"Qwen\",\n \"Moonshot\", \"豆包\", \"Poe\", \"OpenRouter\", \"Perplexxity\", \"腾讯元宝\", \"心流\", \"昆仑万维\",\n \"夸克\", \"character AI\",\n}\n\nAI_AGENT_SECTIONS = {\"扣子\", \"火山方舟\", \"anyrouter\", \"n8n\", \"dify\", \"lovable\", \"teamo\"}\nAI_DESIGN_SECTIONS = {\"AI 设计\", \"即梦\", \"comfy\", \"海螺\", \"labnana\"}\n\nAI_TOP_AGENT = {\"OpenClaw\", \"AI Chatbot\", \"AI 协议\", \"Agent Skills\", \"mcp\"}\nAI_TOP_DESIGN = {\"AI PDF\", \"AI 内容生产\"}\nAI_TOP_LEARNING = {\"Course\", \"社区\", \"prompt\", \"SDD\"}\nAI_TOP_NAV = {\"Arena\", \"排行榜\", \"导航网\"}\nAI_TOP_ACCOUNT = {\"代充\"}\nAI_TOP_CODING = {\"OneCode\"}\nAI_TOP_BACKLOG = {\"todo\"}\n\nGENERIC_KEYWORDS = {\n \"coding\": [\"code\", \"coding\", \"program\", \"developer\", \"github\", \"gitlab\", \"api\", \"sdk\", \"cli\", \"terminal\", \"编程\", \"开发\"],\n \"learning\": [\"course\", \"tutorial\", \"guide\", \"learn\", \"docs\", \"blog\", \"wiki\", \"知乎\", \"掘金\", \"教程\", \"文档\"],\n \"agent\": [\"agent\", \"workflow\", \"automation\", \"protocol\", \"mcp\", \"自动化\", \"协议\", \"智能体\"],\n \"design\": [\"design\", \"image\", \"video\", \"pdf\", \"图像\", \"视频\", \"设计\"],\n \"nav\": [\"leaderboard\", \"ranking\", \"navigation\", \"directory\", \"榜\", \"导航\"],\n \"account\": [\"billing\", \"subscription\", \"recharge\", \"pay\", \"充值\", \"订阅\", \"账号\"],\n \"community\": [\"community\", \"forum\", \"discord\", \"reddit\", \"讨论\", \"社区\"],\n \"ops\": [\"cloud\", \"console\", \"platform\", \"dashboard\", \"workspace\", \"控制台\", \"平台\"],\n}\n\n\n@dataclass\nclass LinkEntry:\n href: str\n attrs: str\n title_raw: str\n title_dec: str\n path: List[str]\n mapped_path: List[str] = field(default_factory=list)\n\n\n@dataclass\nclass TreeNode:\n folders: OrderedDict = field(default_factory=OrderedDict)\n links: List[LinkEntry] = field(default_factory=list)\n\n\ndef decode_text(value: str) -> str:\n return html.unescape(value).strip()\n\n\ndef find_topic_range(lines: List[str], topic_folder: str) -> tuple[int, int, int]:\n start = -1\n topic_indent = -1\n target = topic_folder.strip().casefold()\n\n for idx, line in enumerate(lines):\n m = H3_RE.match(line)\n if not m:\n continue\n title = decode_text(m.group(\"title\"))\n if title.casefold() == target:\n start = idx\n topic_indent = len(m.group(\"indent\"))\n break\n\n if start \u003c 0:\n raise ValueError(f\"Topic folder '{topic_folder}' not found\")\n\n end = len(lines) - 1\n for idx in range(start + 1, len(lines)):\n m = H3_RE.match(lines[idx])\n if not m:\n continue\n if len(m.group(\"indent\")) == topic_indent:\n end = idx - 1\n break\n\n return start, end, topic_indent\n\n\ndef parse_topic_entries(lines: List[str], start: int, end: int) -> List[LinkEntry]:\n entries: List[LinkEntry] = []\n stack: Dict[int, str] = {}\n\n for idx in range(start, end + 1):\n line = lines[idx]\n\n h3 = H3_RE.match(line)\n if h3:\n indent = len(h3.group(\"indent\"))\n title_dec = decode_text(h3.group(\"title\"))\n for key in list(stack.keys()):\n if key >= indent:\n del stack[key]\n stack[indent] = title_dec\n continue\n\n a = A_RE.match(line)\n if not a:\n continue\n\n indent = len(a.group(\"indent\"))\n attrs = a.group(\"attrs\")\n title_raw = a.group(\"title\")\n title_dec = decode_text(title_raw)\n href_match = HREF_RE.search(attrs)\n href = href_match.group(1).strip() if href_match else \"\"\n\n path = [v for k, v in sorted(stack.items()) if k \u003c indent]\n if not path:\n continue\n\n entries.append(\n LinkEntry(\n href=href,\n attrs=attrs,\n title_raw=title_raw,\n title_dec=title_dec,\n path=path,\n )\n )\n\n return entries\n\n\ndef map_ai_path(rel: List[str], labels: Dict[str, str]) -> List[str]:\n if not rel:\n return [labels[\"backlog\"], labels[\"uncategorized\"]]\n\n top = rel[0]\n\n if top == \"工具\":\n sec = rel[1] if len(rel) > 1 else \"工具-未分类\"\n rest = rel[2:] if len(rel) > 2 else []\n\n if sec in AI_CODING_SECTIONS:\n bucket = \"coding\"\n elif sec in AI_PLATFORM_SECTIONS:\n bucket = \"platform\"\n elif sec in AI_AGENT_SECTIONS:\n bucket = \"agent\"\n elif sec in AI_DESIGN_SECTIONS:\n bucket = \"design\"\n elif sec == \"New folder\":\n bucket = \"backlog\"\n else:\n bucket = \"platform\"\n\n return [labels[bucket], sec] + rest\n\n if top in AI_TOP_AGENT:\n return [labels[\"agent\"], top] + rel[1:]\n if top in AI_TOP_DESIGN:\n return [labels[\"design\"], top] + rel[1:]\n if top in AI_TOP_LEARNING:\n return [labels[\"learning\"], top] + rel[1:]\n if top in AI_TOP_NAV:\n return [labels[\"nav\"], top] + rel[1:]\n if top in AI_TOP_ACCOUNT:\n return [labels[\"account\"], top] + rel[1:]\n if top in AI_TOP_CODING:\n return [labels[\"coding\"], top] + rel[1:]\n if top in AI_TOP_BACKLOG:\n return [labels[\"backlog\"], top] + rel[1:]\n\n return [labels[\"backlog\"], top] + rel[1:]\n\n\ndef classify_generic(entry: LinkEntry, labels: Dict[str, str]) -> str:\n haystack = \" \".join(entry.path + [entry.title_dec, entry.href]).casefold()\n\n for bucket, words in GENERIC_KEYWORDS.items():\n if any(word in haystack for word in words):\n return labels[bucket]\n\n return labels[\"ops\"]\n\n\ndef map_generic_path(rel: List[str], entry: LinkEntry, labels: Dict[str, str]) -> List[str]:\n top = rel[0] if rel else labels[\"uncategorized\"]\n bucket = classify_generic(entry, labels)\n return [bucket, top] + rel[1:]\n\n\ndef map_entries(entries: List[LinkEntry], topic_folder: str, mode: str, lang: str) -> tuple[List[LinkEntry], str]:\n labels = LANG_LABELS[lang]\n\n if mode == \"auto\":\n topic_cf = topic_folder.casefold()\n selected = \"ai\" if (\"ai\" in topic_cf or \"智能\" in topic_cf) else \"generic\"\n else:\n selected = mode\n\n for entry in entries:\n rel = entry.path[1:] if len(entry.path) > 1 else []\n if selected == \"ai\":\n entry.mapped_path = map_ai_path(rel, labels)\n else:\n entry.mapped_path = map_generic_path(rel, entry, labels)\n\n return entries, selected\n\n\ndef dedupe_entries(entries: List[LinkEntry], dedupe_url: bool) -> tuple[List[LinkEntry], int]:\n if not dedupe_url:\n return entries, 0\n\n seen = set()\n result: List[LinkEntry] = []\n removed = 0\n\n for entry in entries:\n href = entry.href.strip()\n if href and href in seen:\n removed += 1\n continue\n if href:\n seen.add(href)\n result.append(entry)\n\n return result, removed\n\n\ndef insert_tree(root: TreeNode, entry: LinkEntry) -> None:\n node = root\n for segment in entry.mapped_path:\n if segment not in node.folders:\n node.folders[segment] = TreeNode()\n node = node.folders[segment]\n node.links.append(entry)\n\n\ndef render_tree(node: TreeNode, indent: int, preferred: List[str]) -> List[str]:\n lines: List[str] = []\n keys = list(node.folders.keys())\n ordered = [k for k in preferred if k in node.folders] + [k for k in keys if k not in preferred]\n\n for name in ordered:\n child = node.folders[name]\n sp = \" \" * indent\n lines.append(f\"{sp}\u003cDT>\u003cH3>{html.escape(name)}\u003c/H3>\")\n lines.append(f\"{sp}\u003cDL>\u003cp>\")\n lines.extend(render_tree(child, indent + 4, preferred=[]))\n for link in child.links:\n lines.append(f\"{sp} \u003cDT>\u003cA {link.attrs}>{link.title_raw}\u003c/A>\")\n lines.append(f\"{sp}\u003c/DL>\u003cp>\")\n\n return lines\n\n\ndef build_output(topic_folder: str, root: TreeNode, mode: str, lang: str) -> str:\n labels = LANG_LABELS[lang]\n\n if mode == \"ai\":\n preferred = [\n labels[\"coding\"], labels[\"platform\"], labels[\"agent\"], labels[\"design\"],\n labels[\"learning\"], labels[\"nav\"], labels[\"account\"], labels[\"backlog\"],\n ]\n else:\n preferred = [\n labels[\"coding\"], labels[\"platform\"], labels[\"agent\"], labels[\"design\"],\n labels[\"learning\"], labels[\"community\"], labels[\"nav\"], labels[\"account\"],\n labels[\"ops\"], labels[\"backlog\"],\n ]\n\n lines = [\n \"\u003c!DOCTYPE NETSCAPE-Bookmark-file-1>\",\n \"\u003c!-- This is an automatically generated file.\",\n \" It will be read and overwritten.\",\n \" DO NOT EDIT! -->\",\n '\u003cMETA HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">',\n \"\u003cTITLE>Bookmarks\u003c/TITLE>\",\n \"\u003cH1>Bookmarks\u003c/H1>\",\n \"\u003cDL>\u003cp>\",\n f\" \u003cDT>\u003cH3>{html.escape(topic_folder)}\u003c/H3>\",\n \" \u003cDL>\u003cp>\",\n ]\n\n lines.extend(render_tree(root, indent=12, preferred=preferred))\n\n lines.extend([\n \" \u003c/DL>\u003cp>\",\n \"\u003c/DL>\u003cp>\",\n ])\n\n return \"\\n\".join(lines) + \"\\n\"\n\n\ndef collect_report(topic_folder: str, entries_before: List[LinkEntry], entries_after: List[LinkEntry], removed_duplicates: int, selected_mode: str) -> Dict:\n original_top = Counter()\n mapped_top = Counter()\n domains = Counter()\n\n for entry in entries_before:\n rel = entry.path[1:] if len(entry.path) > 1 else []\n key = rel[0] if rel else \"(direct)\"\n original_top[key] += 1\n\n for entry in entries_after:\n if entry.mapped_path:\n mapped_top[entry.mapped_path[0]] += 1\n if entry.href:\n host = urlparse(entry.href).hostname or \"\"\n host = host.lower().removeprefix(\"www.\")\n if host:\n domains[host] += 1\n\n return {\n \"topic_folder\": topic_folder,\n \"selected_mode\": selected_mode,\n \"input_links\": len(entries_before),\n \"output_links\": len(entries_after),\n \"removed_duplicates\": removed_duplicates,\n \"original_top_level_counts\": dict(original_top.most_common()),\n \"output_category_counts\": dict(mapped_top.most_common()),\n \"top_domains\": dict(domains.most_common(20)),\n }\n\n\ndef print_report(report: Dict) -> None:\n print(\"topic:\", report[\"topic_folder\"])\n print(\"mode:\", report[\"selected_mode\"])\n print(\"input_links:\", report[\"input_links\"])\n print(\"output_links:\", report[\"output_links\"])\n print(\"removed_duplicates:\", report[\"removed_duplicates\"])\n\n print(\"\\noriginal_top_level_counts:\")\n for k, v in report[\"original_top_level_counts\"].items():\n print(f\" {k}: {v}\")\n\n print(\"\\noutput_category_counts:\")\n for k, v in report[\"output_category_counts\"].items():\n print(f\" {k}: {v}\")\n\n\ndef main() -> None:\n parser = argparse.ArgumentParser(description=\"Reorganize one topic folder from browser bookmarks export\")\n parser.add_argument(\"--input\", required=True, help=\"Path to source bookmarks HTML\")\n parser.add_argument(\"--output\", required=True, help=\"Path to output bookmarks HTML\")\n parser.add_argument(\"--topic-folder\", default=\"AI\", help=\"Folder title to extract and reorganize\")\n parser.add_argument(\"--mode\", choices=[\"auto\", \"ai\", \"generic\"], default=\"auto\", help=\"Mapping strategy\")\n parser.add_argument(\"--lang\", choices=[\"zh\", \"en\"], default=\"zh\", help=\"Output category label language\")\n parser.add_argument(\"--no-dedupe-url\", action=\"store_true\", help=\"Do not deduplicate same URL\")\n parser.add_argument(\"--report\", help=\"Write JSON report path\")\n parser.add_argument(\"--print-report\", action=\"store_true\", help=\"Print report to console\")\n args = parser.parse_args()\n\n source = Path(args.input)\n output = Path(args.output)\n\n if not source.exists():\n raise SystemExit(f\"Input file not found: {source}\")\n\n lines = source.read_text(encoding=\"utf-8\", errors=\"ignore\").splitlines()\n start, end, _ = find_topic_range(lines, args.topic_folder)\n\n entries = parse_topic_entries(lines, start, end)\n if not entries:\n raise SystemExit(f\"No links found under topic folder '{args.topic_folder}'\")\n\n entries, selected_mode = map_entries(entries, args.topic_folder, args.mode, args.lang)\n entries_out, removed = dedupe_entries(entries, dedupe_url=not args.no_dedupe_url)\n\n root = TreeNode()\n for entry in entries_out:\n insert_tree(root, entry)\n\n output.parent.mkdir(parents=True, exist_ok=True)\n output.write_text(build_output(args.topic_folder, root, selected_mode, args.lang), encoding=\"utf-8\")\n\n report = collect_report(args.topic_folder, entries, entries_out, removed, selected_mode)\n if args.report:\n report_path = Path(args.report)\n report_path.parent.mkdir(parents=True, exist_ok=True)\n report_path.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding=\"utf-8\")\n\n if args.print_report:\n print_report(report)\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":14716,"content_sha256":"5ffc1aea3e0d706732c1234924590614b220667405965efb277c160a84d391b9"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Topic Bookmarks Reorganizer(中文)","type":"text"}]},{"type":"paragraph","content":[{"text":"把一个书签导出文件中的目标主题目录重整为更清晰、可导入的新文件。","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"何时使用","type":"text"}]},{"type":"paragraph","content":[{"text":"当用户有以下需求时使用本技能:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"分析一个书签导出 HTML,并定位用户指定的主题目录","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"重新分门别类该目录下的链接与子目录","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"按 URL 去重","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"输出只包含该主题目录的可导入 HTML","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"不要使用","type":"text"}]},{"type":"paragraph","content":[{"text":"以下场景不应使用本技能:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"输入不是浏览器书签导出 HTML","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"用户仅需要文字建议,不需要处理文件","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"用户需求与书签整理无关(如纯 JSON/PDF/Docx 处理)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"使用说明","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"先向用户确认必要参数:","type":"text"}]}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"输入书签文件路径","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"主题目录名称","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"输出文件路径","type":"text"}]}]}]},{"type":"ordered_list","attrs":{"order":2,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"先跑分析与预览:","type":"text"}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 scripts/reorganize_topic_bookmarks.py \\\n --input /path/to/bookmarks.html \\\n --output /tmp/topic-preview.html \\\n --topic-folder \"\u003ctopic-folder-name>\" \\\n --mode auto \\\n --lang zh \\\n --report /tmp/topic-report.json \\\n --print-report","type":"text"}]},{"type":"ordered_list","attrs":{"order":3,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"视情况调整参数:","type":"text"}]}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--mode auto","type":"text","marks":[{"type":"code_inline"}]},{"text":":自动选择分类策略","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--mode generic","type":"text","marks":[{"type":"code_inline"}]},{"text":":使用通用分类策略","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--no-dedupe-url","type":"text","marks":[{"type":"code_inline"}]},{"text":":不做 URL 去重","type":"text"}]}]}]},{"type":"ordered_list","attrs":{"order":4,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"生成最终文件:","type":"text"}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python3 scripts/reorganize_topic_bookmarks.py \\\n --input /path/to/bookmarks.html \\\n --output /path/to/topic-bookmarks-reorganized.html \\\n --topic-folder \"\u003ctopic-folder-name>\" \\\n --mode auto \\\n --lang zh","type":"text"}]},{"type":"ordered_list","attrs":{"order":5,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"交付前检查并汇报:","type":"text"}]}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"确认输出文件存在","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"汇总输入链接数、输出链接数、去重移除数","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"确认输出仅包含一个顶层目录(目标主题目录)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"输出要求","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"输出必须是 Netscape 书签格式,可直接导入浏览器","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"输出仅保留目标主题目录","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"尽量保留原 ","type":"text"},{"text":"\u003cA ...>","type":"text","marks":[{"type":"code_inline"}]},{"text":" 属性(如 add-date/icon)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"目录结构按高层分类重组,便于后续维护","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"topic-bookmarks-reorganizer-cn","author":"@skillopedia","source":{"stars":9,"repo_name":"awesome-agent-skills","origin_url":"https://github.com/yangsonhung/awesome-agent-skills/blob/HEAD/skills/zh-cn/topic-bookmarks-reorganizer-cn/SKILL.md","repo_owner":"yangsonhung","body_sha256":"a9abb4350364e2ddfbae777c6fa166caf6a467710dd93e7596dd02db3839ae3b","cluster_key":"d7288591feb10e3c2d7eea3d14be5c31afdcec27bd12651e1021a7d844607244","clean_bundle":{"format":"clean-skill-bundle-v1","source":"yangsonhung/awesome-agent-skills/skills/zh-cn/topic-bookmarks-reorganizer-cn/SKILL.md","attachments":[{"id":"bb44eb0c-6271-51d9-adad-8c3b4c1d8d3d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/bb44eb0c-6271-51d9-adad-8c3b4c1d8d3d/attachment.py","path":"scripts/reorganize_topic_bookmarks.py","size":14716,"sha256":"5ffc1aea3e0d706732c1234924590614b220667405965efb277c160a84d391b9","contentType":"text/x-python; charset=utf-8"}],"bundle_sha256":"c3547f18aae84921ded19aa5bff4a03c9e294284e007680f88573229ef2641cb","attachment_count":1,"text_attachments":1,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/zh-cn/topic-bookmarks-reorganizer-cn/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"web-development","category_label":"Web"},"exact_dupes_collapsed_into_this":0},"license":"MIT","version":"v1","category":"web-development","import_tag":"clean-skills-v1","description":"将浏览器导出的书签 HTML 中用户指定的主题目录重新分类整理,按 URL 去重,并导出可直接导入浏览器的 Netscape 书签文件。用于用户要求分析书签导出、提取一个主题目录、重分组链接与子目录并生成可导入 HTML 的场景。"}},"renderedAt":1782981869219}

Topic Bookmarks Reorganizer(中文) 把一个书签导出文件中的目标主题目录重整为更清晰、可导入的新文件。 何时使用 当用户有以下需求时使用本技能: - 分析一个书签导出 HTML,并定位用户指定的主题目录 - 重新分门别类该目录下的链接与子目录 - 按 URL 去重 - 输出只包含该主题目录的可导入 HTML 不要使用 以下场景不应使用本技能: - 输入不是浏览器书签导出 HTML - 用户仅需要文字建议,不需要处理文件 - 用户需求与书签整理无关(如纯 JSON/PDF/Docx 处理) 使用说明 1. 先向用户确认必要参数: - 输入书签文件路径 - 主题目录名称 - 输出文件路径 2. 先跑分析与预览: 3. 视情况调整参数: - :自动选择分类策略 - :使用通用分类策略 - :不做 URL 去重 4. 生成最终文件: 5. 交付前检查并汇报: - 确认输出文件存在 - 汇总输入链接数、输出链接数、去重移除数 - 确认输出仅包含一个顶层目录(目标主题目录) 输出要求 - 输出必须是 Netscape 书签格式,可直接导入浏览器 - 输出仅保留目标主题目录 - 尽量保留原 属性(如 add-date/icon) - 目录结构按高层分类重组,便于后续维护 ---