{"id":"63ffc717-4aa4-46fb-9c91-46a6154f6f2d","entityType":"agent","slug":"clawhub-fyniujin-dgngjx-skill","name":"dgngjx-skill","canonicalUrl":"https://www.xpersona.co/agent/clawhub-fyniujin-dgngjx-skill","canonicalPath":"/agent/clawhub-fyniujin-dgngjx-skill","generatedAt":"2026-10-10T21:55:34.569Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-10T17:17:30.035Z","emptyReason":null},"description":"多功能免费工具箱 - 图片处理、PDF转换、数据换算、文本工具、开发工具、视频工具、教育、生活娱乐、实用小工具、系统工具、AI办公。11大模块49个工具。v4.1 包化架构：SKILL.md 129KB→30KB + 11模块外置 + 四包机制（core/dev/ent/media）+ 首次体检报告。 Skill: dgngjx-skill Owner: fyniujin Summary: 多功能免费工具箱 - 图片处理、PDF转换、数据换算、文本工具、开发工具、视频工具、教育、生活娱乐、实用小工具、系统工具、AI办公。11大模块49个工具。v4.1 包化架构：SKILL.md 129KB→30KB + 11模块外置 + 四包机制（core/dev/ent/media）+ 首次体检报告。 Tags: latest:4.1.0 Version history: v4.1.0 | 2026-10-09T08:30:01.429Z | auto **v4.1.0 包化架构重大更新** - 全新包化架构：11大模块全部外置，SKILL.md 缩减至 30KB，支持四包机制（core/dev/ent/media）。 - 新增可选包管理：支持 --list-packs 查看、--install-pack 一键安装可选工具包。 - 支持首次","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.3K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s177r8w7p1d7cpbys9bn33kwhs89d0xw:dgngjx-skill","sourceUrl":"https://clawhub.ai/fyniujin/dgngjx-skill","homepage":"https://clawhub.ai/fyniujin/skills/dgngjx-skill","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/fyniujin/dgngjx-skill","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/fyniujin/skills/dgngjx-skill","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":62,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"多功能免费工具箱 - 图片处理、PDF转换、数据换算、文本工具、开发工具、视频工具、教育、生活娱乐、实用小工具、系统工具、AI办公。11大模块49个工具。v4.1 包化架构：SKILL.md 129KB→30KB + 11模块外置 + 四包机制（core/dev/ent/media）+ 首次体检报告。 Skill: d"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T17:17:30.035Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T17:17:30.035Z","emptyReason":null},"stars":null,"forks":null,"downloads":1322,"packageName":null,"latestVersion":"4.1.0","tractionLabel":"1.3K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T17:17:30.035Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T17:17:30.035Z","lastCrawledAt":"2026-10-10T17:17:30.035Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T17:17:30.035Z","lastVerifiedAt":null,"highlights":[{"version":"4.1.0","createdAt":"2026-10-09T08:30:01.429Z","changelog":"**v4.1.0 包化架构重大更新** - 全新包化架构：11大模块全部外置，SKILL.md 缩减至 30KB，支持四包机制（core/dev/ent/media）。 - 新增可选包管理：支持 `--list-packs` 查看、`--install-pack` 一键安装可选工具包。 - 支持首次环境体检：`--health` 生成系统支持及依赖体检报告，提升首次体验。 - 所有模块说明文档分离为 references/modules 目录，结构更清晰。 - 移除 legacy skill-card.md，registry.json 工具注册表全面升级。 - CLI 主命令支持包与健康相关新参数，配置文件自动管理已装包。","fileCount":17,"zipByteSize":54698},{"version":"4.0.0","createdAt":"2026-09-17T02:06:44.459Z","changelog":"v4.0.0 增加个税五险全国化、汇率缓存时效治理、独立个税计算器；优化五险一金支持13城市","fileCount":4,"zipByteSize":48566},{"version":"3.9.0","createdAt":"2026-08-24T11:53:28.775Z","changelog":"**v3.9.0 brings a unified CLI, tool registry, and experience upgrades.** - 新增统一命令行入口 `dgngjx`，49个工具均支持子命令参数化调用，无需进入交互模式 - 引入 tools registry（registry.json），工具元数据集中注册，CLI极速加载，支持参数schema/依赖/安全级别 - 所有工具参数化设计，支持无人值守模式（环境变量 `DGNGJX_ASSUME_YES=1` 或 `--yes` 跳过确认） - CLI支持 `--json` 机器可解析输出，便于自动化与计划任务 - 优化文档和说明，删除 skill-card.md，精简模块数/功能点、清晰版本变更 - 部分工具（如会议纪要/周报等辅助类）合并或调整，精简至 49 个核心工具","fileCount":3,"zipByteSize":43074},{"version":"3.8.0","createdAt":"2026-08-17T07:34:01.187Z","changelog":"**dgngjx-skill v3.8.0 Changelog** - 新增会议纪要生成器（支持语音转文本ASR三级降级） - 新增一键周报/月报生成工具 - 工具箱扩展为11大模块53个工具，涵盖AI办公类 - 优化系统资源监控（迁移至 PowerShell CIM 提高稳定性） - 更新文档使用指引和功能列表，聚合新功能入口 - skill-card.md 文档已移除","fileCount":3,"zipByteSize":38726},{"version":"3.7.0","createdAt":"2026-08-07T01:29:25.709Z","changelog":"**v3.7.0 introduces new tools, feature upgrades, and architecture improvements.** - 新增CSV查看器、密码强度检测、颜色格式转换、随机数、文本差异对比、BMI、番茄钟、汇率等工具。 - 图片工具扩展支持批量压缩及多格式互转。 - 增加历史记录、配置持久化与管道式联动架构，提升可用性和组合效率。 - 工具总数由42增至51，实用范围大幅提升。 - skill-card.md 文件已删除，调整说明文档结构。","fileCount":3,"zipByteSize":35396},{"version":"3.6.0","createdAt":"2026-07-24T09:29:18.582Z","changelog":"**dgngjx-skill v3.6.0 — 功能全面升级版** - 新增文件哈希/校验、UUID、时间戳、IP等免费工具，模块总数增至42个 - 单位换算功能扩展到28种，支持更多数值和尺寸转换 - 文本编码工具支持MD5、SHA、进制（如二/八/十六进制）等主流算法 - 工具命令表与描述文本同步更新，方便一键查找调用 - 移除 skill-card.md 文件，文档结构进一步优化","fileCount":3,"zipByteSize":25904},{"version":"3.5.0","createdAt":"2026-07-14T07:17:28.884Z","changelog":"**dgngjx-skill v3.5.0 changelog:** - 新增三大功能：系统资源监控、批量文件重命名、Markdown 转 HTML。 - 引入安全过滤规则，自动禁止处理高风险文件类型（如 .exe, .bat, .zip 等）。 - 增加硬件自适应：根据用户电脑配置自动分配并发任务与文件上限，优化低配设备体验。 - 工具箱扩展至10大模块38项工具，零开箱即用比例提升至53%。 - 移除 skill-card.md，完善文档说明和触发词示例。","fileCount":3,"zipByteSize":22300},{"version":"2.5.1","createdAt":"2026-07-07T23:15:36.919Z","changelog":"updated via tongyifabu","fileCount":3,"zipByteSize":36349}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s177r8w7p1d7cpbys9bn33kwhs89d0xw:dgngjx-skill","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-fyniujin-dgngjx-skill/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-fyniujin-dgngjx-skill/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-fyniujin-dgngjx-skill/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-fyniujin-dgngjx-skill/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-fyniujin-dgngjx-skill/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-fyniujin-dgngjx-skill/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T21:55:34.567Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-fyniujin-dgngjx-skill/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-fyniujin-dgngjx-skill/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-fyniujin-dgngjx-skill/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-fyniujin-dgngjx-skill/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-10T17:17:30.035Z","emptyReason":null},"readme":"Skill: dgngjx-skill\n\nOwner: fyniujin\n\nSummary: 多功能免费工具箱 - 图片处理、PDF转换、数据换算、文本工具、开发工具、视频工具、教育、生活娱乐、实用小工具、系统工具、AI办公。11大模块49个工具。v4.1 包化架构：SKILL.md 129KB→30KB + 11模块外置 + 四包机制（core/dev/ent/media）+ 首次体检报告。\n\nTags: latest:4.1.0\n\nVersion history:\n\nv4.1.0 | 2026-10-09T08:30:01.429Z | auto\n\n**v4.1.0 包化架构重大更新**  \n- 全新包化架构：11大模块全部外置，SKILL.md 缩减至 30KB，支持四包机制（core/dev/ent/media）。\n- 新增可选包管理：支持 `--list-packs` 查看、`--install-pack` 一键安装可选工具包。\n- 支持首次环境体检：`--health` 生成系统支持及依赖体检报告，提升首次体验。\n- 所有模块说明文档分离为 references/modules 目录，结构更清晰。\n- 移除 legacy skill-card.md，registry.json 工具注册表全面升级。\n- CLI 主命令支持包与健康相关新参数，配置文件自动管理已装包。\n\nv4.0.0 | 2026-09-17T02:06:44.459Z | user\n\nv4.0.0 增加个税五险全国化、汇率缓存时效治理、独立个税计算器；优化五险一金支持13城市\n\nv3.9.0 | 2026-08-24T11:53:28.775Z | auto\n\n**v3.9.0 brings a unified CLI, tool registry, and experience upgrades.**\n\n- 新增统一命令行入口 `dgngjx`，49个工具均支持子命令参数化调用，无需进入交互模式\n- 引入 tools registry（registry.json），工具元数据集中注册，CLI极速加载，支持参数schema/依赖/安全级别\n- 所有工具参数化设计，支持无人值守模式（环境变量 `DGNGJX_ASSUME_YES=1` 或 `--yes` 跳过确认）\n- CLI支持 `--json` 机器可解析输出，便于自动化与计划任务\n- 优化文档和说明，删除 skill-card.md，精简模块数/功能点、清晰版本变更\n- 部分工具（如会议纪要/周报等辅助类）合并或调整，精简至 49 个核心工具\n\nv3.8.0 | 2026-08-17T07:34:01.187Z | auto\n\n**dgngjx-skill v3.8.0 Changelog**\n\n- 新增会议纪要生成器（支持语音转文本ASR三级降级）\n- 新增一键周报/月报生成工具\n- 工具箱扩展为11大模块53个工具，涵盖AI办公类\n- 优化系统资源监控（迁移至 PowerShell CIM 提高稳定性）\n- 更新文档使用指引和功能列表，聚合新功能入口\n- skill-card.md 文档已移除\n\nv3.7.0 | 2026-08-07T01:29:25.709Z | auto\n\n**v3.7.0 introduces new tools, feature upgrades, and architecture improvements.**\n\n- 新增CSV查看器、密码强度检测、颜色格式转换、随机数、文本差异对比、BMI、番茄钟、汇率等工具。\n- 图片工具扩展支持批量压缩及多格式互转。\n- 增加历史记录、配置持久化与管道式联动架构，提升可用性和组合效率。\n- 工具总数由42增至51，实用范围大幅提升。\n- skill-card.md 文件已删除，调整说明文档结构。\n\nv3.6.0 | 2026-07-24T09:29:18.582Z | auto\n\n**dgngjx-skill v3.6.0 — 功能全面升级版**\n\n- 新增文件哈希/校验、UUID、时间戳、IP等免费工具，模块总数增至42个\n- 单位换算功能扩展到28种，支持更多数值和尺寸转换\n- 文本编码工具支持MD5、SHA、进制（如二/八/十六进制）等主流算法\n- 工具命令表与描述文本同步更新，方便一键查找调用\n- 移除 skill-card.md 文件，文档结构进一步优化\n\nv3.5.0 | 2026-07-14T07:17:28.884Z | auto\n\n**dgngjx-skill v3.5.0 changelog:**\n\n- 新增三大功能：系统资源监控、批量文件重命名、Markdown 转 HTML。\n- 引入安全过滤规则，自动禁止处理高风险文件类型（如 .exe, .bat, .zip 等）。\n- 增加硬件自适应：根据用户电脑配置自动分配并发任务与文件上限，优化低配设备体验。\n- 工具箱扩展至10大模块38项工具，零开箱即用比例提升至53%。\n- 移除 skill-card.md，完善文档说明和触发词示例。\n\nv2.5.1 | 2026-07-07T23:15:36.919Z | user\n\nupdated via tongyifabu\n\nv3.0.0 | 2026-07-07T23:15:33.013Z | auto\n\n**dgngjx-skill v3.0.0 is a major upgrade with expanded tools and enhanced functionality.**\n\n- 新增 9 大模块 35 个工具，功能更全，覆盖图片、PDF、文本、开发、视频、二维码、密码、正则等。\n- 词频统计全面升级：支持 jieba 中文分词+停用词过滤，分析更准确。\n- 视频工具增强：支持视频加水印、信息查看、视频截图、GIF 优化等操作。\n- 二维码、随机密码、正则表达式测试工具全新加入。\n- FFmpeg 一键安装脚本，尤其简化 Windows 用户的安装流程。\n- 新增依赖检测与管理，依赖更清晰、安装更省心。\n- 部分文档与触发词示例同步更新，便于上手。\n- skill-card.md 文件已移除，内容全部整合于 SKILL.md。\n\nv2.5.0 | 2026-07-01T07:04:12.616Z | user\n\n修复 ClawHub 样式乱码：CRLF 换行符处理优化\n\nv2.2.0 | 2026-07-01T02:43:53.468Z | user\n\nv2.2.0 更新发布\n\nv2.1.0 | 2026-06-30T14:34:51.616Z | user\n\nv2.1.0: 多功能工具箱功能增强\n\nv2.0.0 | 2026-06-30T13:51:12.461Z | user\n\nv2.0.0: 国内联网优化（自动降级+国内镜像）+ 大文件性能增强\n\nv1.7.0 | 2026-06-26T04:57:55.901Z | user\n\nInitial ClawHub release: v1.7.0 - 多功能工具箱，从 skillhub 迁移至 clawhub\n\nArchive index:\n\nArchive v4.1.0: 17 files, 54698 bytes\n\nFiles: health_check.py (4823b), references/modules/ai.md (5355b), references/modules/calc.md (16727b), references/modules/dev.md (2869b), references/modules/edu.md (2265b), references/modules/img.md (5939b), references/modules/life.md (6102b), references/modules/pdf.md (3053b), references/modules/sys.md (23939b), references/modules/text.md (6378b), references/modules/util.md (14847b), references/modules/video.md (2081b), references/tax_2026.yaml (8588b), registry.json (6580b), skill-card.md (2112b), SKILL.md (31796b), _meta.json (131b)\n\nFile v4.1.0:SKILL.md\n\n---\nname: dgngjx-skill\ndescription: \"多功能免费工具箱 - 图片处理、PDF转换、数据换算、文本工具、开发工具、视频工具、教育、生活娱乐、实用小工具、系统工具、AI办公。11大模块49个工具。v4.1 包化架构：SKILL.md 129KB→30KB + 11模块外置 + 四包机制（core/dev/ent/media）+ 首次体检报告。\"\nversion: 4.1.0\n---\n\n\n# 多功能工具箱 dgngjx-skill v4.1.0\n\n## 🚀 dgngjx CLI 统一入口 ⭐ v3.9.0 新增\n\n**✅ 开箱即用**（纯 Python 标准库 argparse，零依赖）\n\n> v4.1.0 新增：`--install-pack` 安装可选包、`--list-packs` 列出可用包、`--health` 首次体检报告。\n\n<details>\n<summary>📋 CLI 使用方式</summary>\n\n```bash\n# 直接调用工具（参数化，非交互）\ndgngjx calc.mortgage --amount 1000000 --years 30 --rate 4.2\ndgngjx text.encode --method base64 --text \"Hello World\"\ndgngjx img.compress --path ./photos/ --quality 80 --format JPEG\ndgngjx hash.md5 --file document.pdf\ndgngjx exchange --amount 100 --from USD --to CNY\n\n# 机器可解析输出（供 CI/计划任务使用）\ndgngjx sys.monitor --json\n\n# 无人值守模式（跳过非危险确认点）\ndgngjx file.rename --dir ./photos/ --prefix \"vacation_\" --yes\n# 或设置环境变量\nexport DGNGJX_ASSUME_YES=1\ndgngjx img.convert --path ./photos/ --format PNG\n\n# 包管理（v4.1.0 新增）\ndgngjx --list-packs          # 列出所有可用包\ndgngjx --install-pack ent    # 安装娱乐包\ndgngjx --install-pack media  # 安装媒体包\ndgngjx --install-pack ai     # 安装 AI 包\n\n# 体检报告（v4.1.0 新增）\ndgngjx --health              # 首次运行体检\n\n# 无参数启动 → 进入交互向导（兼容旧体验）\ndgngjx\n```\n\n</details>\n\n<details>\n<summary>📋 CLI 入口代码（dgngjx 命令）</summary>\n\n```python\n#!/usr/bin/env python3\n\"\"\"dgngjx CLI - 多功能工具箱统一命令行入口\"\"\"\nimport argparse, json, os, sys\n\ndef load_registry():\n    \"\"\"加载工具注册表\"\"\"\n    reg_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), \"registry.json\")\n    with open(reg_path, encoding=\"utf-8\") as f:\n        return json.load(f)\n\ndef build_tool_index(registry):\n    \"\"\"将 modules[].tools 嵌套结构扁平化为 tool_id → info 索引\"\"\"\n    index = {}\n    for mod in registry.get(\"modules\", []):\n        for tool_id, tool_info in mod.get(\"tools\", {}).items():\n            tool_info[\"module_id\"] = mod[\"id\"]\n            tool_info[\"module_name\"] = mod[\"name\"]\n            tool_info[\"pack\"] = mod.get(\"pack\", \"core\")\n            index[tool_id] = tool_info\n    return index\n\ndef main():\n    parser = argparse.ArgumentParser(prog=\"dgngjx\", description=\"多功能工具箱 dgngjx-skill\")\n    parser.add_argument(\"tool\", nargs=\"?\", help=\"工具路径，如 calc.mortgage / text.encode / img.compress\")\n    parser.add_argument(\"--json\", action=\"store_true\", help=\"输出机器可解析 JSON\")\n    parser.add_argument(\"--yes\", \"-y\", action=\"store_true\", help=\"跳过非危险确认点\")\n    parser.add_argument(\"--help-tools\", action=\"store_true\", help=\"列出所有可用工具\")\n    parser.add_argument(\"--list-packs\", action=\"store_true\", help=\"列出所有可用包（v4.1.0）\")\n    parser.add_argument(\"--install-pack\", metavar=\"PACK\", help=\"安装指定包（v4.1.0）\")\n    parser.add_argument(\"--health\", action=\"store_true\", help=\"运行体检报告（v4.1.0）\")\n    \n    args, remaining = parser.parse_known_args()\n    \n    if os.environ.get(\"DGNGJX_ASSUME_YES\"):\n        args.yes = True\n    \n    registry = load_registry()\n    \n    # 包管理命令\n    if args.list_packs:\n        packs = registry.get(\"packs\", {})\n        print(\"📦 可用包：\")\n        for pid, pinfo in packs.items():\n            default = \"（默认）\" if pinfo.get(\"default\") else \"\"\n            print(f\"  {pid}: {pname}{default} — {pinfo.get('description','')}\")\n        return\n    \n    if args.install_pack:\n        pack_id = args.install_pack\n        packs = registry.get(\"packs\", {})\n        if pack_id not in packs:\n            print(f\"❌ 未知包: {pack_id}。可用: {', '.join(packs.keys())}\")\n            return\n        # 标记包已安装（写入配置文件）\n        config_path = os.path.join(os.path.expanduser(\"~\"), \".workbuddy\", \"dgngjx_config.json\")\n        config = {}\n        if os.path.exists(config_path):\n            with open(config_path, encoding=\"utf-8\") as f:\n                config = json.load(f)\n        installed_packs = config.get(\"installed_packs\", [\"core\"])\n        if pack_id not in installed_packs:\n            installed_packs.append(pack_id)\n        config[\"installed_packs\"] = installed_packs\n        os.makedirs(os.path.dirname(config_path), exist_ok=True)\n        with open(config_path, \"w\", encoding=\"utf-8\") as f:\n            json.dump(config, f, ensure_ascii=False, indent=2)\n        print(f\"✅ 已安装包: {packs[pack_id]['name']}\")\n        return\n    \n    if args.health:\n        # 运行体检\n        health_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), \"health_check.py\")\n        if os.path.exists(health_path):\n            os.system(f\"{sys.executable} {health_path} --force\")\n        else:\n            print(\"❌ 未找到 health_check.py\")\n        return\n    \n    tool_index = build_tool_index(registry)\n    \n    if args.help_tools or not args.tool:\n        if args.tool:\n            tool_info = tool_index.get(args.tool)\n            if tool_info:\n                print(f\"工具: {args.tool}\")\n                print(f\"描述: {tool_info.get('description','')}\")\n                print(f\"参数: {json.dumps(tool_info.get('params',{}), ensure_ascii=False, indent=2)}\")\n            else:\n                print(f\"❌ 未找到工具: {args.tool}\")\n        else:\n            print(\"🔧 dgngjx-skill 交互向导\")\n            print(\"=\" * 40)\n            for mod in registry.get(\"modules\", []):\n                print(f\"\\n{mod['name']}:\")\n                for tool_id, tool_info in mod.get(\"tools\", {}).items():\n                    print(f\"  {tool_id}: {tool_info.get('description','')}\")\n        return\n    \n    tool_info = tool_index.get(args.tool)\n    if not tool_info:\n        print(f\"❌ 未找到工具: {args.tool}\")\n        print(f\"可用工具: {', '.join(list(tool_index.keys())[:10])}...\")\n        sys.exit(1)\n    \n    tool_args = {}\n    i = 0\n    while i < len(remaining):\n        if remaining[i].startswith(\"--\"):\n            key = remaining[i][2:].replace(\"-\",\"_\")\n            if i + 1 < len(remaining) and not remaining[i+1].startswith(\"--\"):\n                tool_args[key] = remaining[i+1]\n                i += 2\n            else:\n                tool_args[key] = True\n                i += 1\n        else:\n            i += 1\n    \n    tool_args[\"_json\"] = args.json\n    tool_args[\"_yes\"] = args.yes\n    \n    module = tool_info.get(\"module_id\")\n    print(f\"▶ 运行: {args.tool} (模块 {module})\")\n\nif __name__ == \"__main__\":\n    main()\n```\n\n</details>\n\n---\n\n## 30 秒速查表\n\n| 我想做 | 直接说 |\n|--------|--------|\n| 算房贷 / 五险一金 | `\"算房贷\"` `\"算五险一金\"` |\n| 日期 / 单位换算 | `\"日期计算\"` `\"公里转英里\"` `\"MB转GB\"` `\"100美元换人民币\"` |\n| 统计字数 / 编码 | `\"字数统计\"` `\"Base64编码\"` `\"算MD5\"` `\"进制转换\"` |\n| 词频分析 / 差异 | `\"词频统计\"` `\"对比两段文本\"` |\n| JSON / HTTP / Token | `\"JSON格式化\"` `\"测试接口\"` |\n| 查知识 / 下壁纸 | `\"查勾股定理\"` `\"下壁纸\"` |\n| 压缩 / 修复图片 | `\"帮我压缩 D:\\photo.jpg\"` `\"修复老照片\"` |\n| 合并 / 拆分 PDF | `\"合并这几个PDF\"` `\"拆分第5-10页\"` |\n| 视频格式转换 | `\"MP4转GIF\"` |\n| 二维码 / 密码 / 正则 | `\"生成二维码\"` `\"生成随机密码\"` `\"测试正则 \\d+\"` `\"检测密码强度\"` |\n| 文件哈希 / UUID | `\"校验文件哈希\"` `\"生成UUID\"` |\n| 时间戳 / IP工具 | `\"时间戳转换\"` `\"查本机IP\"` `\"算子网\"` |\n| CSV / 颜色 / 随机数 | `\"看CSV文件\"` `\"#FF0000转HSL\"` `\"随机数\"` |\n| BMI / 番茄钟 | `\"算BMI\"` `\"开始番茄钟\"` |\n| 历史 / 配置 / 周报 | `\"查看历史记录\"` `\"修改配置\"` `\"生成本周周报\"` |\n| 会议纪要 | `\"生成会议纪要\"` `\"语音转会议纪要\"` |\n| 系统资源监控 | `\"看看CPU使用率\"` `\"内存够不够\"` |\n| 批量文件重命名 | `\"把这些文件都改名\"` `\"批量添加前缀\"` |\n| Markdown转HTML | `\"把这份MD转成HTML\"` |\n\n---\n\n## 🆕 v4.1.0 更新提醒\n\n> 🔔 **您正在使用 dgngjx-skill v4.1.0**\n> \n> 检查更新：`skillhub search dgngjx-skill`\n> \n> 升级命令：`clawhub update dgngjx-skill`\n> \n> 📧 **有任何建议？联系作者邮箱：njskills@agent.qq.com**\n\n---\n\n## 快速开始\n\n### 第一次用？做这 3 步\n\n| 步骤 | 做什么 | 为什么 |\n|:----:|--------|--------|\n| ① | **快速试一个**：对 AI 说 `\"算房贷\"` | 零依赖，秒回，建立信心 |\n| ② | **按需装依赖**：用到图片/PDF功能时，AI 会提示安装，输入 `install` 确认 | 一次安装，后面都能用 |\n| ③ | **高级功能**：参考下方「📦 依赖管理」或跳过直接用在线工具 | 按需取用 |\n\n### 你可以直接说（触发词）\n\n| 说 | 做 | 备注 |\n|----|----|------|\n| `\"算房贷 100万 30年 4.2%\"` | 等额本息计算 | ✅ 开箱即用 |\n| `\"算五险一金 北京 15000\"` | 税后工资 | ✅ 开箱即用 |\n| `\"从 2026-06-26 到国庆多少天\"` | 日期计算 | ✅ 开箱即用 |\n| `\"Base64编码 Hello\"` | 编码转换 | ✅ 开箱即用 |\n| `\"统计字数\"` 然后粘贴文本 | 中英词频统计 | ✅ 开箱即用 |\n| `\"测试 https://api.github.com\"` | HTTP 请求 | ✅ 开箱即用 |\n| `\"帮我压缩 D:\\photo.jpg\"` | 图片压缩 | 📦 需确认装 Pillow |\n| `\"合并这几个PDF\"` | PDF 合并 | 📦 需确认装 PyPDF2 |\n| `\"MP4转GIF\"` | 视频转换 | 📦 需确认装 FFmpeg |\n| `\"给视频加水印 文字=我的水印\"` | 视频编辑 | 📦 需确认装 FFmpeg |\n| `\"生成二维码 内容=Hello\"` | 二维码 | ✅ 开箱即用 |\n| `\"生成随机密码 长度=16\"` | 密码生成 | ✅ 开箱即用 |\n| `\"测试正则 \\d+ 文本=abc123\"` | 正则测试 | ✅ 开箱即用 |\n| `\"看看CPU使用率\"` | 系统资源监控 | ✅ 开箱即用 |\n| `\"把这个MD转成HTML\"` | Markdown转HTML | ✅ 开箱即用 |\n\n---\n\n## 📋 registry.json 工具注册表 ⭐ v3.9.0 新增\n\n> 49 个工具的元数据注册表，包含模块/参数 schema/依赖声明/示例/安全等级。CLI 启动时只加载此文件（轻量），工具代码延迟加载。\n\n<details>\n<summary>📋 registry.json 结构说明</summary>\n\n```json\n{\n  \"version\": \"4.1.0\",\n  \"total_tools\": 49,\n  \"packs\": {\n    \"core\": {\"name\": \"核心包\", \"description\": \"默认加载\", \"default\": true},\n    \"ent\": {\"name\": \"娱乐包\", \"description\": \"娱乐/生活模块\", \"default\": false},\n    \"media\": {\"name\": \"媒体包\", \"description\": \"图片/PDF/视频\", \"default\": false},\n    \"ai\": {\"name\": \"AI包\", \"description\": \"AI 办公\", \"default\": false}\n  },\n  \"modules\": [...]\n}\n```\n\n> **字段说明**：`deps` 为空表示零依赖；`safety` 为 `warn` 表示需用户确认；`pack` 表示所属包（core 默认加载，其余按需安装）。\n\n</details>\n\n---\n\n## 🔒 安全规则（v3.9.0 分级重构）\n\n> **dgngjx-skill v3.9.0 起采用三级安全策略**：🔴 硬拦截 / 🟡 警告+确认 / 🟢 自由读写。\n\n### 🔴 硬拦截（不处理，即使明确要求也拒绝）\n\n**Windows 可执行 / 批处理脚本：**\n`.bat`、`.cmd`、`.ps1`、`.vbs`、`.exe`、`.dll`、`.lnk`、`.msi`\n\n**其他风险脚本：**\n`.sh`、`.com`、`.scr`、`.hta`、`.reg`\n\n**二进制镜像 / 安装包：**\n`.iso`、`.dmg`、`.apk`、`.jar`\n\n**系统缓存 / 隐藏文件：**\n`.DS_Store`、`.git` 目录、`.env`、`.log`、`.tmp`\n\n### 🟡 警告+用户确认（显示风险说明，用户确认后可读写）\n\n**Office 二进制文档：**\n`.docx`、`.xlsx`、`.pptx`、`.doc`、`.xls`、`.ppt`、`.xlsm`、`.docm`、`.pptm`\n\n> ⚠️ 警告内容：\"此操作将读取/写入 Office 文档，可能包含宏或敏感数据。确认继续？\"\n\n### 🟢 自由读写（非风险类型，无需确认）\n\n图片（`.jpg/.png/.webp/.bmp/.gif/.tiff`）、文档（`.pdf/.md/.txt/.csv/.json/.xml`）、音频（`.mp3/.wav/.m4a`）、视频（`.mp4/.gif`）等非风险类型。\n\n<details>\n<summary>📋 安全过滤代码（v3.9.0 分级版）</summary>\n\n```python\nimport os\n\n# === v3.9.0 安全规则：三级分级策略 ===\n\nBLOCKED_EXTENSIONS = {\n    '.bat', '.cmd', '.ps1', '.vbs', '.exe', '.dll', '.lnk', '.msi',\n    '.sh', '.com', '.scr', '.hta', '.reg',\n    '.iso', '.dmg', '.apk', '.jar',\n    '.ds_store', '.env', '.log', '.tmp',\n}\n\nWARN_EXTENSIONS = {\n    '.docx', '.xlsx', '.pptx', '.doc', '.xls', '.ppt', '.xlsm', '.docm', '.pptm',\n}\n\nBLOCKED_DIRS = {'.git', '.svn', '.hg', '__pycache__', 'node_modules', '.idea', '.vscode'}\n\ndef _is_safe_file(filepath: str, assume_yes: bool = False) -> tuple:\n    \"\"\"检查文件安全等级。返回 (状态, 原因) 状态: 'safe' / 'warn' / 'block'\"\"\"\n    basename = os.path.basename(filepath)\n    if basename.startswith('.') and basename.lower() in {'.ds_store', '.env', '.gitconfig', '.bashrc'}:\n        return 'block', f\"隐藏系统文件 {basename} 被拦截\"\n    parts = filepath.replace('\\\\', '/').split('/')\n    for part in parts:\n        if part.lower() in BLOCKED_DIRS:\n            return 'block', f\"系统目录 {part} 被拦截\"\n    ext = os.path.splitext(filepath)[1].lower()\n    if ext in BLOCKED_EXTENSIONS:\n        return 'block', f\"{ext} 可执行/风险类型被安全规则禁止\"\n    if ext in WARN_EXTENSIONS:\n        if assume_yes:\n            return 'safe', \"\"\n        return 'warn', f\"将读写 Office 文档({ext})，可能包含宏或敏感数据\"\n    return 'safe', \"\"\n\ndef _is_safe_output(filepath: str) -> tuple:\n    \"\"\"检查输出文件类型是否安全（不允许生成可执行/危险文件）\"\"\"\n    ext = os.path.splitext(filepath)[1].lower()\n    if ext in {'.exe', '.dll', '.bat', '.cmd', '.ps1', '.vbs', '.sh', '.com', '.scr', '.hta', '.reg', '.msi', '.apk', '.jar'}:\n        return False, f\"安全规则禁止生成 {ext} 文件\"\n    return True, \"\"\n```\n\n</details>\n\n---\n\n## ⚡ 自适应硬件调度 （v3.5 新增）\n\n> dgngjx-skill 会自动检测你的电脑配置（CPU核心数、内存大小），并根据硬件能力调整并发任务数量和资源分配，**绝不拖累低配电脑**。\n\n<details>\n<summary>📋 硬件检测与智能调度脚本（v3.5 新增）</summary>\n\n```python\nimport os, sys, subprocess, platform\n\ndef get_hardware_info() -> dict:\n    \"\"\"自动检测用户电脑硬件信息，用于智能调度\"\"\"\n    info = {\n        'cpu_count': os.cpu_count() or 2,\n        'ram_mb': 0,\n        'platform': platform.system(),\n        'is_low_end': False,\n        'max_workers': 1,\n        'max_file_size_mb': 100,\n    }\n    \n    try:\n        if info['platform'] == 'Windows':\n            result = subprocess.run(\n                ['wmic', 'computersystem', 'get', 'TotalPhysicalMemory', '/value'],\n                capture_output=True, text=True, timeout=10\n            )\n            for line in result.stdout.split('\\n'):\n                if 'TotalPhysicalMemory' in line:\n                    bytes_val = int(line.split('=')[1].strip())\n                    info['ram_mb'] = bytes_val // (1024 * 1024)\n                    break\n        elif info['platform'] == 'Linux':\n            with open('/proc/meminfo', 'r') as f:\n                for line in f:\n                    if line.startswith('MemTotal'):\n                        info['ram_mb'] = int(line.split()[1]) // 1024\n                    break\n        elif info['platform'] == 'Darwin':\n            result = subprocess.run(\n                ['sysctl', '-n', 'hw.memsize'], capture_output=True, text=True, timeout=5\n            )\n            info['ram_mb'] = int(result.stdout.strip()) // (1024 * 1024)\n    except Exception:\n        info['ram_mb'] = 4096\n    \n    if info['ram_mb'] < 4096 or info['cpu_count'] <= 2:\n        info['is_low_end'] = True\n        info['max_workers'] = 1\n        info['max_file_size_mb'] = 50\n    elif info['ram_mb'] < 8192 or info['cpu_count'] <= 4:\n        info['max_workers'] = 2\n        info['max_file_size_mb'] = 200\n    else:\n        info['max_workers'] = min(info['cpu_count'], 4)\n        info['max_file_size_mb'] = 500\n    \n    return info\n\n_HW = get_hardware_info()\nprint(f\"🖥️ 系统检测: {_HW['cpu_count']}核 | {_HW['ram_mb']}MB RAM | {_HW['platform']}\")\nif _HW['is_low_end']:\n    print(\"⚡ 已启用低配模式：单任务运行，小文件优先，绝不卡顿\")\nelse:\n    print(f\"⚡ 已启用标准模式：最多 {_HW['max_workers']} 并发，文件上限 {_HW['max_file_size_mb']}MB\")\n```\n\n</details>\n\n**调度策略一览：**\n\n| 硬件级别 | 内存 | CPU | 并发数 | 单文件上限 | 保护策略 |\n|---------|------|-----|--------|-----------|---------|\n| 🟢 高配 | ≥8GB | ≥6核 | 4 | 500MB | 多任务并行，大文件分块 |\n| 🟡 中配 | 4-8GB | 2-4核 | 2 | 200MB | 双任务，中等文件 |\n| 🔴 低配 | <4GB | ≤2核 | 1 | 50MB | 单任务，小文件优先 |\n\n---\n\n## 📦 依赖管理\n\n### 检测环境\n\n任何功能使用前，AI 先跑这个脚本看看缺了什么：\n\n<details>\n<summary>📋 依赖检测脚本</summary>\n\n```python\nimport sys, subprocess\nprint(f\"Python: {sys.version.split()[0]}\")\npkgs = {'PIL':'Pillow(PIL)','rembg':'rembg','PyPDF2':'PyPDF2','cv2':'opencv-python','jieba':'jieba'}\nfor pkg, name in pkgs.items():\n    try: __import__(pkg); print(f\"  ✅ {name}\")\n    except: print(f\"  ❌ {name}（缺失）\")\ntry:\n    subprocess.run(['ffmpeg','-version'], capture_output=True, text=True)\n    print(\"  ✅ ffmpeg\")\nexcept: print(\"  ❌ ffmpeg（缺失）\")\n```\n\n</details>\n\n### 一键安装\n\n```\npip install Pillow PyPDF2 jieba\n```\n\n> AI 必须问用户确认后再执行。\n\n### 按功能安装\n\n| 你想用 | 只装这个 | 命令 |\n|--------|---------|------|\n| 压缩图片 / 证件照 / 基础修复 | Pillow (3MB) | `pip install Pillow` |\n| PDF 合并/拆分/加密 | PyPDF2 (200KB) | `pip install PyPDF2` |\n| 人像抠图 | rembg (300MB) | `pip install rembg` |\n| 精准中文分词 | jieba (10MB) | `pip install jieba` |\n| 视频转换 / 编辑 | ffmpeg (系统包) | 见下方一键安装 |\n\n> ⚠️ rembg 首次运行会自动下载 ~300MB 模型（1-5分钟），装好后可离线使用。\n> 💡 jieba 为可选依赖，词频统计不装 jieba 也能用（会自动降级到正则分词）。\n\n### FFmpeg 一键安装（v3.0 简化）\n\n> 💡 最简方案：打开 PowerShell，粘贴 `winget install ffmpeg` 一行搞定。\n\n---\n\n## ❌ 不支持（边界情况说明）\n\n| 不支持 | 原因 | 替代方案 |\n|--------|------|---------|\n| 在线编辑 PSD | .psd 为封闭格式 | [Photopea](https://www.photopea.com/) |\n| Excel 在线编辑 | 超出能力 | 可通过本工具转 PDF |\n| 视频录屏（浏览器外） | 需浏览器 API | 用电脑自带录屏 |\n| 大于 500MB 的视频 | 浏览器会卡 | 需本地安装 FFmpeg |\n| 密码加密后丢失 | 安全机制限制 | 密码在加密时展示一次，请截图保存 |\n| 实时网络请求拦截 | 超出能力范围 | 使用 Fiddler / Charles |\n| 批量视频压制 | 需专业编码软件 | HandBrake 批量队列 |\n| 处理可执行文件 | 安全规则禁止 | 使用专业反编译/分析工具 |\n\n---\n\n## 📦 包化架构（v4.1.0 新增）\n\n> v4.1.0 引入四包机制，SKILL.md 从 129KB 瘦身至 30KB，模块详情外置到 `references/modules/*.md`。\n\n### 四包一览\n\n| 包 ID | 名称 | 默认加载 | 包含模块 | 依赖 |\n|-------|------|:--------:|---------|------|\n| `core` | 核心包 | ✅ | 数据换算/文本/教育/开发/实用/系统工具 | 零依赖 |\n| `ent` | 娱乐包 | ❌ | 生活娱乐（笑话/壁纸/BMI/番茄钟） | 零依赖 |\n| `media` | 媒体包 | ❌ | 图片/PDF/视频 | Pillow/PyPDF2/FFmpeg |\n| `ai` | AI包 | ❌ | AI办公（会议纪要） | whisper（可选） |\n\n### 包安装方式\n\n```bash\n# CLI 安装\ndgngjx --install-pack ent\ndgngjx --install-pack media\ndgngjx --install-pack ai\n\n# 查看已安装包\ndgngjx --list-packs\n```\n\n### 模块详情文件\n\n| 模块 | 文件路径 |\n|------|---------|\n| 模块 1：数据换算 | `references/modules/calc.md` |\n| 模块 2：文本工具 | `references/modules/text.md` |\n| 模块 3：教育工具 | `references/modules/edu.md` |\n| 模块 4：生活娱乐 | `references/modules/life.md` |\n| 模块 5：开发工具 | `references/modules/dev.md` |\n| 模块 6：图片工具 | `references/modules/img.md` |\n| 模块 7：PDF转换 | `references/modules/pdf.md` |\n| 模块 8：视频工具 | `references/modules/video.md` |\n| 模块 9：实用小工具 | `references/modules/util.md` |\n| 模块 10：系统工具 | `references/modules/sys.md` |\n| 模块 11：AI办公 | `references/modules/ai.md` |\n\n---\n\n## 🏥 首次运行体检报告（v4.1.0 新增）\n\n> 首次运行时自动检测环境，输出就绪率和缺失依赖一键安装命令。\n\n<details>\n<summary>📋 体检报告代码</summary>\n\n```python\n#!/usr/bin/env python3\n\"\"\"dgngjx-skill 首次运行体检报告 v4.1.0\"\"\"\nimport sys, subprocess, os, json, platform\n\nHEALTH_FILE = os.path.join(os.path.expanduser(\"~\"), \".workbuddy\", \"dgngjx_health.json\")\n\ndef check_python():\n    v = sys.version_info\n    ok = v >= (3, 8)\n    return {\"name\": \"Python\", \"status\": \"ok\" if ok else \"warn\",\n            \"version\": f\"{v.major}.{v.minor}.{v.micro}\", \"required\": \"≥ 3.8\",\n            \"fix\": None if ok else \"请升级 Python 到 3.8+\"}\n\ndef check_module(pkg_name, import_name=None):\n    import_name = import_name or pkg_name\n    try:\n        __import__(import_name)\n        return {\"name\": pkg_name, \"status\": \"ok\", \"version\": \"\", \"required\": \"\", \"fix\": None}\n    except ImportError:\n        return {\"name\": pkg_name, \"status\": \"missing\", \"version\": \"\", \"required\": \"\", \"fix\": f\"pip install {pkg_name}\"}\n\ndef check_ffmpeg():\n    try:\n        r = subprocess.run([\"ffmpeg\", \"-version\"], capture_output=True, text=True, timeout=5)\n        if r.returncode == 0:\n            return {\"name\": \"FFmpeg\", \"status\": \"ok\", \"version\": r.stdout.split(chr(10))[0][:50], \"required\": \"\", \"fix\": None}\n    except: pass\n    return {\"name\": \"FFmpeg\", \"status\": \"missing\", \"version\": \"\", \"required\": \"视频转换/编辑\", \"fix\": \"winget install ffmpeg\"}\n\ndef check_network():\n    try:\n        import urllib.request\n        req = urllib.request.Request(\"https://api.github.com\", headers={\"User-Agent\":\"dgngjx/4.1\"})\n        urllib.request.urlopen(req, timeout=5)\n        return {\"name\": \"网络连接\", \"status\": \"ok\", \"version\": \"\", \"required\": \"\", \"fix\": None}\n    except Exception as e:\n        return {\"name\": \"网络连接\", \"status\": \"warn\", \"version\": \"\", \"required\": \"汇率/知识查询\", \"fix\": f\"网络受限: {type(e).__name__}\"}\n\ndef run_health_check(force=False):\n    if not force and os.path.exists(HEALTH_FILE):\n        try:\n            with open(HEALTH_FILE, encoding=\"utf-8\") as f:\n                prev = json.load(f)\n            if prev.get(\"skipped\", False):\n                return None\n        except: pass\n\n    checks = [\n        check_python(),\n        check_module(\"Pillow\", \"PIL\"),\n        check_module(\"PyPDF2\"),\n        check_module(\"rembg\"),\n        check_module(\"jieba\"),\n        check_ffmpeg(),\n        check_network(),\n    ]\n\n    ok_count = sum(1 for c in checks if c[\"status\"] == \"ok\")\n    missing = [c for c in checks if c[\"status\"] == \"missing\"]\n\n    print(\"=\" * 50)\n    print(\"  🏥 dgngjx-skill 首次运行体检报告\")\n    print(\"=\" * 50)\n    for c in checks:\n        icon = \"✅\" if c[\"status\"] == \"ok\" else (\"⚠️\" if c[\"status\"] == \"warn\" else \"❌\")\n        ver = f\" ({c['version']})\" if c[\"version\"] else \"\"\n        print(f\"  {icon} {c['name']}{ver}\")\n    print(f\"  就绪率: {ok_count}/{len(checks)} ({ok_count*100//len(checks)}%)\")\n    if missing:\n        print(\"  📦 缺失依赖（一键安装）：\")\n        cmds = []\n        for c in missing:\n            if c[\"fix\"]:\n                print(f\"    • {c['name']}: {c['fix']}\")\n                if \"pip install\" in c[\"fix\"]:\n                    cmds.append(c[\"fix\"].replace(\"pip install \", \"\"))\n        if cmds:\n            print(f\"  快速安装: pip install {' '.join(cmds)}\")\n    print(\"=\" * 50)\n\n    os.makedirs(os.path.dirname(HEALTH_FILE), exist_ok=True)\n    with open(HEALTH_FILE, \"w\", encoding=\"utf-8\") as f:\n        json.dump({\"checked\": True, \"skipped\": False, \"ok_count\": ok_count, \"total\": len(checks)}, f)\n    return checks\n\nif __name__ == \"__main__\":\n    force = \"--force\" in sys.argv\n    run_health_check(force=force)\n```\n\n</details>\n\n---\n\n## 功能模块\n\n> **v4.1.0 起模块详情外置**，主文档只保留模块名称索引，完整代码见 `references/modules/*.md`。\n\n| 模块 | 名称 | 包 | 文件 |\n|------|------|:--:|------|\n| 1 | 数据换算 | core | [calc.md](references/modules/calc.md) |\n| 2 | 文本工具 | core | [text.md](references/modules/text.md) |\n| 3 | 教育工具 | core | [edu.md](references/modules/edu.md) |\n| 4 | 生活娱乐 | ent | [life.md](references/modules/life.md) |\n| 5 | 开发工具 | core | [dev.md](references/modules/dev.md) |\n| 6 | 图片工具 | media | [img.md](references/modules/img.md) |\n| 7 | PDF转换 | media | [pdf.md](references/modules/pdf.md) |\n| 8 | 视频工具 | media | [video.md](references/modules/video.md) |\n| 9 | 实用小工具 | core | [util.md](references/modules/util.md) |\n| 10 | 系统工具 | core | [sys.md](references/modules/sys.md) |\n| 11 | AI办公 | ai | [ai.md](references/modules/ai.md) |\n\n---\n\n## 最佳实践\n\n### 场景 1：新电脑第一次用\n\n```\n你: \"帮我检测一下环境\"\nAI: 跑检测脚本 → 自适应硬件调度初始化 → 体检报告\n你: \"帮我装一下\"\nAI: pip install Pillow PyPDF2 jieba\n你: \"帮我压缩 D:\\photo.jpg\"\nAI: ✅ 完成\n```\n\n### 场景 2：低配电脑性能保护\n\n```\n处理 50MB 图片时\n→ 系统检测到 4GB RAM → 自动单任务模式\n→ 显示 \"⚠️ 50MB 文件 → 压缩可能需1-2分钟\"\n→ 分配最小资源，不卡顿\n```\n\n### 场景 3：安全文件拦截\n\n```\n你: \"帮我压缩 C:\\Windows\\system32\\cmd.exe\"\nAI: 🚫 .exe 可执行程序 — 安全规则禁止处理\n```\n\n---\n\n## ❓ 常见问题\n\n### Q1: 第一次使用怎么做？\n直接说一句话。零依赖功能直接跑。需安装功能会提示。\n\n### Q2: 发现 skill 有更新怎么办？\n```bash\nclawhub update dgngjx-skill\n```\n或检查：`skillhub search dgngjx-skill`\n\n### Q3: 有什么建议/bug？\n📧 **邮箱：njskills@agent.qq.com**\n作者会阅读每一条反馈。\n\n### Q4: 处理大文件太慢？\nv3.5.0 起已根据你的硬件自动调度，低配机会自动降为小文件单任务模式，不会出现卡死。\n\n### Q5: 批量重命名风险？\n必须先预览，确认输入 `YES` 才执行。可执行文件会自动跳过。\n\n### Q6: MD转HTML支持复杂语法吗？\n支持标题、列表、代码块、引用、粗体、行内代码。复杂表格建议用 pandoc。\n\n### Q7: 汇率查询需要联网吗？\n默认无需联网（内置 30+ 种常见货币缓存）。输入联网模式可获取实时汇率，联网失败自动降级到缓存。\n\n### Q8: 历史记录存在哪里？\n保存在 `~/.workbuddy/dgngjx_history.json`，保留最近 200 条。可随时搜索或清空。\n\n### Q9: 会议纪要需要安装 whisper 吗？\n不需要。默认使用模式 C（手动粘贴文本）即可零依赖运行。需要自动化 ASR 时再安装 whisper 或配置 Paraformer API。\n\n### Q10: 周报是自动记录的吗？\n是的。使用工具后，系统自动记录到 `~/.workbuddy/dgngjx_history.json`。周报生成时自动汇总本周期记录。\n\n### Q11: dgngjx CLI 怎么用？\n安装 skill 后，可直接用 `dgngjx 模块.工具 --参数` 调用。例如 `dgngjx calc.mortgage --amount 1000000 --years 30 --rate 4.2`。无参数启动进入交互向导。加 `--json` 输出机器可解析 JSON，加 `--yes` 或设置 `DGNGJX_ASSUME_YES=1` 跳过非危险确认点。\n\n### Q12: registry.json 是什么？\n49 个工具的元数据注册表，包含模块归属、参数 schema、依赖声明和安全等级。CLI 启动时只加载此文件（轻量），工具代码延迟加载。\n\n### Q13: v4.1.0 的四包机制怎么用？\n默认加载 `core` 核心包（37 个零依赖工具）。需要图片/PDF/视频功能时，运行 `dgngjx --install-pack media` 安装媒体包。需要娱乐功能时，运行 `dgngjx --install-pack ent` 安装娱乐包。运行 `dgngjx --list-packs` 查看所有可用包。\n\n---\n\n## 功能分类统计\n\n| 类别 | 数量 | 开箱即用 |\n|------|:----:|:--------:|\n| ✅ 零依赖即可运行 | 37 | ✅ |\n| 📦 需确认安装后运行 | 12 | ❌（有引导+在线替代） |\n| **总计** | **49** | **76%** |\n\n## 更新日志\n\n| v4.1.0 | 2026-09-17 | 增加：包化架构（SKILL.md 129KB→30KB + 11模块外置 references/modules/*.md）；增加：四包机制（core/ent/media/ai，CLI --install-pack/--list-packs）；增加：首次运行体检报告（Python/Pillow/PyPDF2/rembg/jieba/ffmpeg/网络/磁盘）；迁移：娱乐模块（笑话/壁纸/BMI/番茄钟）移至 ent 可选包；优化：registry.json 独立文件 + pack 字段；增加：个税五险全国化（tax_2026.yaml 数据包，13城市查表+7级分段累进引擎）；增加：汇率缓存时效治理（72h TTL + 陈旧标注 + 警示横幅）；增加：独立个税计算器（模块1.2.1） |\n| v3.9.0 | 2026-08-24 | 增加：统一 CLI 入口（argparse 子命令 + registry.json 注册表 + 49 工具参数化）；拆分：编码/哈希拆分为 encode + hash 独立工具；合并：图片五功能合并为 img 统一入口（compress/convert/removebg/idphoto/repair）；优化：安全规则分级重构（🔴 硬拦截 / 🟡 警告+确认 / 🟢 自由读写）；增加：无人值守支持（DGNGJX_ASSUME_YES 环境变量 + --yes + --json 输出） |\n| v3.8.0 | 2026-08-17 | 增加：会议纪要生成器（模块11.1 ASR三级降级链）；增加：周报/月报自动生成（模块10.6）；优化：系统资源监控（模块10.1 wmic→PowerShell CIM） |\n| v3.7.0 | 2026-08-07 | 增加：CSV查看器/密码强度/颜色转换/随机数/文本差异/BMI/番茄钟/汇率查询/历史记录/配置持久化/管道联动；优化：开箱即用率57%→65% |\n| v3.6.0 | 2026-07-07 | 增加：文件哈希/UUID/时间戳/IP工具；扩展：单位换算8→28种；扩展：编码新增MD5/SHA+进制互转 |\n| v3.5.0 | 2026-07-14 | 增加：硬件调度/安全过滤/系统监控/批量重命名/MD转HTML/更新提醒 |\n| v3.0.0 | 2026-07-13 | 增加：jieba分词/视频编辑/路径修复/FFmpeg脚本/模块9 |\n| v2.5.0 | 2026-07-12 | 增加：HTTP语法修复；扩充坑表格 |\n| v2.2.0 | 2026-07-11 | 修复：多个bug |\n| v2.1.0 | 2026-07-10 | 修复：单位换算float崩溃+壁纸urllib缺失 |\n| v2.0.0 | 2026-07-09 | 增加：国内联网优化；大文件性能增强 |\n| v1.0.0 | 2026-07-02 | 初始版本，8大模块29个工具 |\n\n## 一键安装后可用功能\n\n| 装这些 | 新增可用 |\n|-------|---------|\n| `pip install Pillow` | 压缩 / 证件照 / 基础修复 / PS |\n| `pip install PyPDF2` | PDF 合并 / 拆分 / 加密 / 解密 |\n| `pip install rembg` | 人像抠图（首次 300MB） |\n| `pip install jieba` | 精准中文分词 |\n| FFmpeg | 格式转换 / 视频编辑（6大功能） |\n| `pip install qrcode[pil]` | 二维码本地生成 |\n\n## 发布信息\n\n- **作者**：Admin\n- **联系邮箱**：njskills@agent.qq.com\n- **许可证**：MIT\n- **支持平台**：Windows / macOS / Linux\n- **当前版本**：v4.1.0\n- **检查更新**：`skillhub search dgngjx-skill`\n- **升级命令**：`clawhub update dgngjx-skill`\n\nFile v4.1.0:_meta.json\n\n{\n  \"ownerId\": \"kn7chdrwbdhaqkwajcyhtfvjx989ddb1\",\n  \"slug\": \"dgngjx-skill\",\n  \"version\": \"4.1.0\",\n  \"publishedAt\": 1791534601429\n}\n\nFile v4.1.0:references/modules/ai.md\n\n# 🤖 模块 11：AI 办公 ⭐ v3.8.0 新增模块\n\n---\n\n#### 11.1 会议纪要生成器 ⭐ v3.8.0 新增\n\n**✅ 开箱即用**（零依赖保底：支持本地 whisper / 在线 API / 手动粘贴文本 三级降级）\n\n> 语音→ASR 转写→LLM 摘要→结构化会议纪要。覆盖 TOP50 高频需求。\n\n<details>\n<summary>📋 模式 A：本地 Whisper CLI（离线，零成本）</summary>\n\n```python\nimport subprocess, os, sys\n\npath = input(\"音频文件路径(mp3/wav/m4a):\").strip().strip('\"').strip(\"'\")\nif not os.path.isfile(path):\n    print(f\"❌ 文件不存在: {path}\")\nelse:\n    r = subprocess.run([\"whisper\", \"--help\"], capture_output=True, text=True, timeout=5)\n    if r.returncode != 0:\n        print(\"❌ 未检测到 whisper CLI。请先安装：pip install openai-whisper\")\n        print(\"   或选择模式 B（在线 API）或模式 C（手动粘贴文本）\")\n    else:\n        print(\"🎙️ 正在转写（可能需要几分钟，取决于音频长度）...\")\n        result = subprocess.run(\n            [\"whisper\", path, \"--language\", \"zh\", \"--output_format\", \"txt\",\n             \"--output_dir\", os.path.dirname(path)],\n            capture_output=True, text=True, timeout=600\n        )\n        if result.returncode == 0:\n            out_path = os.path.splitext(path)[0] + \".txt\"\n            if os.path.exists(out_path):\n                with open(out_path, encoding=\"utf-8\") as f:\n                    text = f.read()\n                print(f\"✅ 转写完成！文本长度: {len(text)} 字\")\n                print(\"--- 转写结果 ---\")\n                print(text[:500])\n            else:\n                print(\"❌ 转写输出文件未生成\")\n        else:\n            print(f\"❌ 转写失败: {result.stderr[:200]}\")\n```\n\n</details>\n\n<details>\n<summary>📋 模式 B：Paraformer API（在线，中文识别率高）</summary>\n\n```python\nimport urllib.request, urllib.parse, json, os\n\npath = input(\"音频文件路径:\").strip().strip('\"').strip(\"'\")\nif not os.path.isfile(path):\n    print(f\"❌ 文件不存在: {path}\")\nelse:\n    API_URL = input(\"API URL(留空使用内置示例):\").strip()\n    API_KEY = input(\"API Key(留空使用内置示例):\").strip()\n    \n    if not API_URL:\n        print(\"💡 未配置 API，将使用模式 C（手动粘贴文本）\")\n        print(\"   如需在线识别，请提供 Paraformer API 地址和密钥\")\n    else:\n        with open(path, \"rb\") as f:\n            audio_data = f.read()\n        req = urllib.request.Request(\n            API_URL,\n            data=audio_data,\n            headers={\"Authorization\": f\"Bearer {API_KEY}\", \"Content-Type\": \"audio/wav\"}\n        )\n        try:\n            resp = urllib.request.urlopen(req, timeout=120)\n            result = json.loads(resp.read())\n            text = result.get(\"text\", result.get(\"result\", \"\"))\n            if text:\n                print(f\"✅ 转写完成！文本长度: {len(text)} 字\")\n                print(\"--- 转写结果 ---\")\n                print(text[:500])\n            else:\n                print(\"❌ API 返回空文本\")\n        except Exception as e:\n            print(f\"❌ API 调用失败: {e}\")\n            print(\"   请检查网络连接和 API 配置\")\n```\n\n</details>\n\n<details>\n<summary>📋 模式 C：手动粘贴文本（零依赖保底，推荐）</summary>\n\n```python\nimport datetime\n\nprint(\"📋 模式 C：手动粘贴会议录音转写文本\")\nprint(\"   （如无 ASR 工具，可手动复制会议录音转写内容）\")\nprint(\"   输入文本后输入 END 结束：\")\nlines = []\nwhile True:\n    line = input()\n    line = line.strip() if line else \"\"\n    if line.upper() == \"END\":\n        break\n    lines.append(line)\ntext = \"\\n\".join(lines)\n\nif not text.strip():\n    print(\"❌ 未输入文本\")\nelse:\n    sentences = [s.strip() for s in text.replace(\"。\",\"\\n\").replace(\"！\",\"\\n\").replace(\"？\",\"\\n\").split(\"\\n\") if len(s.strip()) > 5]\n    key_sentences = [s for s in sentences if any(kw in s for kw in [\"决定\",\"同意\",\"通过\",\"计划\",\"负责\",\"截止\",\"下周\",\"跟进\",\"负责人\",\"审核\"])]\n    today = datetime.date.today().strftime(\"%Y-%m-%d\")\n    print(f\"\\n{'='*50}\")\n    print(f\"  📝 会议纪要（结构化摘要）\")\n    print(f\"  日期: {today} | 原文: {len(text)} 字\")\n    print(f\"{'='*50}\")\n    print(f\"\\n## 📋 会议要点\\n\")\n    if key_sentences:\n        for i, s in enumerate(key_sentences[:10], 1):\n            print(f\"{i}. {s}\")\n    else:\n        for i, s in enumerate(sentences[:5], 1):\n            print(f\"{i}. {s}\")\n    print(f\"\\n## 📝 完整文本\\n\")\n    print(text[:1000])\n    if len(text) > 1000:\n        print(f\"\\n...（共 {len(text)} 字，已截断）\")\n    print(f\"\\n{'='*50}\")\n    print(\"⚠️ 零依赖模式使用规则引擎摘要，安装 LLM 后可获得更智能的归纳\")\n```\n\n</details>\n\n**⚠️ 三级降级链说明：**\n\n| 优先级 | 模式 | 依赖 | 中文识别率 | 适用场景 |\n|:------:|------|------|:----------:|----------|\n| 1 | 本地 Whisper CLI | `pip install openai-whisper` | 85-90% | 离线、大文件、隐私敏感 |\n| 2 | Paraformer API | 联网 + API Key | 92-95% | 中文会议、高准确率需求 |\n| 3 | 手动粘贴文本 | **零依赖** | 100%（人工） | 无安装条件、短文本、兜底 |\n\n> 推荐：先用模式 C 体验，需要自动化时再安装 whisper 或配置 API。\n\nFile v4.1.0:references/modules/calc.md\n\n# 📊 模块 1：数据换算\n\n---\n\n#### 1.1 房贷计算器\n\n**✅ 开箱即用** ｜ 🌐 [在线房贷计算器](https://www.zhujisuanqi.com/)\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport math\ntry:\n    P = float(input(\"贷款总额(万元): \") or 100) * 10000\n    n = int(float(input(\"贷款年限: \") or 30) * 12)\n    r = float(input(\"年利率(%): \") or 4.2) / 100 / 12\n    method = input(\"方式(等额本息/等额本金): \").strip() or \"等额本息\"\n    if n <= 0:\n        print(\"❌ 贷款年限必须大于0\")\n    elif r <= 0:\n        print(\"❌ 年利率必须大于0\")\n    elif method == \"等额本息\":\n        m = P*r*(1+r)**n / ((1+r)**n-1)\n        total = m * n\n        print(f\"月供: {m:.2f}元 | 总利息: {total-P:.2f}元 | 还款总额: {total:.2f}元\")\n    else:\n        tot = sum(P/n + (P-P*i/n)*r for i in range(n))\n        print(f\"首月: {P/n+P*r:.2f}元 | 末月: {P/n+r*(P/n):.2f}元 | 总利息: {tot-P:.2f}元\")\nexcept ValueError:\n    print(\"❌ 输入错误：请输入有效数字。例如：贷款100万输 100，年限30年输 30\")\n```\n\n</details>\n\n---\n\n#### 1.2 五险一金计算器 ⭐ v4.0.0 查表累进引擎\n\n**✅ 开箱即用** ｜ 🌐 [个税计算器](https://www.taxcalculator.com)\n\n> v4.0 重写计算引擎：数据与引擎分离，读取 `references/tax_2026.yaml` 数据驱动计算。支持 13 个城市，查表确定基数上下限，7 级超额累进个税。\n\n<details>\n<summary>📋 五险一金 + 个税计算器（查表 + 分段累进）</summary>\n\n```python\nimport os, yaml, math\n\ndef calc_insurance_tax(salary, city_code=\"beijing\"):\n    \"\"\"读取 tax_2026.yaml，查表+分段累进计算五险一金和个税\"\"\"\n    yaml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), \"references\", \"tax_2026.yaml\")\n    with open(yaml_path, encoding=\"utf-8\") as f:\n        data = yaml.safe_load(f)\n    \n    meta = data.get(\"meta\", {})\n    bases = data.get(\"social_insurance_bases\", {})\n    rates = data.get(\"insurance_rates\", {})\n    overrides = rates.get(\"overrides\", {})\n    tax_brackets = data.get(\"individual_income_tax\", {}).get(\"brackets\", [])\n    monthly_threshold = data.get(\"individual_income_tax\", {}).get(\"monthly_threshold\", 5000)\n    \n    if city_code not in bases:\n        city_name = city_code\n        supported = [f\"{v.get('name', k)} ({k})\" for k, v in bases.items()]\n        return None, f\"❌ 不支持的城市: {city_name}。支持: {', '.join(supported)}\"\n    \n    city_data = bases[city_code]\n    city_name = city_data.get(\"name\", city_code)\n    \n    city_overrides = overrides.get(city_code, {})\n    personal_rates = {**rates.get(\"default\", {}).get(\"personal\", {}), **city_overrides.get(\"personal\", {})}\n    company_rates = {**rates.get(\"default\", {}).get(\"company\", {}), **city_overrides.get(\"company\", {})}\n    \n    def get_base(insurance_type):\n        base_info = city_data.get(insurance_type, {})\n        lower = base_info.get(\"lower\", 0)\n        upper = base_info.get(\"upper\", float(\"inf\"))\n        return max(lower, min(salary, upper))\n    \n    personal_items = {}\n    personal_total = 0\n    for insurance_type in [\"pension\", \"medical\", \"unemployed\", \"injury\", \"maternity\"]:\n        base = get_base(insurance_type)\n        rate = personal_rates.get(insurance_type, 0)\n        amount = base * rate\n        if amount > 0:\n            names = {\"pension\": \"养老\", \"medical\": \"医疗\", \"unemployed\": \"失业\", \"injury\": \"工伤\", \"maternity\": \"生育\"}\n            personal_items[names.get(insurance_type, insurance_type)] = round(amount, 2)\n        personal_total += amount\n    \n    hf_base = get_base(\"housing_fund\")\n    hf_rate = personal_rates.get(\"housing_fund\", 0.12)\n    hf_amount = hf_base * hf_rate\n    personal_items[\"公积金\"] = round(hf_amount, 2)\n    personal_total += hf_amount\n    \n    taxable_income = max(0, salary - personal_total - monthly_threshold)\n    \n    tax = 0\n    tax_detail = None\n    for bracket in tax_brackets:\n        if taxable_income <= 0:\n            break\n        taxable_at_bracket = min(taxable_income, bracket[\"limit\"])\n        tax += taxable_at_bracket * bracket[\"rate\"]\n        taxable_income -= taxable_at_bracket\n        if taxable_at_bracket > 0:\n            tax_detail = f\"{bracket['rate']*100:.0f}%\"\n    \n    net_salary = salary - personal_total - tax\n    \n    company_total = 0\n    for insurance_type in [\"pension\", \"medical\", \"unemployed\", \"injury\", \"maternity\"]:\n        base = get_base(insurance_type)\n        rate = company_rates.get(insurance_type, 0)\n        company_total += base * rate\n    company_total += hf_base * company_rates.get(\"housing_fund\", 0.12)\n    \n    return {\n        \"city\": city_name,\n        \"data_version\": meta.get(\"version\", \"unknown\"),\n        \"data_effective\": meta.get(\"effective_date\", \"unknown\"),\n        \"salary\": salary,\n        \"personal_insurance\": personal_items,\n        \"personal_total\": round(personal_total, 2),\n        \"housing_fund_employee\": round(hf_amount, 2),\n        \"housing_fund_employer\": round(hf_base * company_rates.get(\"housing_fund\", 0.12), 2),\n        \"tax\": round(tax, 2),\n        \"tax_rate_used\": tax_detail,\n        \"net_salary\": round(net_salary, 2),\n        \"company_total\": round(company_total, 2),\n    }, None\n\nCITY_MAP = {\n    \"北京\": \"beijing\", \"上海\": \"shanghai\", \"广州\": \"guangzhou\", \"深圳\": \"shenzhen\",\n    \"杭州\": \"hangzhou\", \"成都\": \"chengdu\", \"南京\": \"nanjing\", \"武汉\": \"wuhan\",\n    \"西安\": \"xian\", \"重庆\": \"chongqing\", \"天津\": \"tianjin\", \"苏州\": \"suzhou\",\n    \"郑州\": \"zhengzhou\", \"长沙\": \"changsha\"\n}\n\ntry:\n    s = float(input(\"税前月薪: \") or 15000)\n    city_input = input(\"城市(北京/上海/广州/深圳/杭州/成都/南京/武汉/西安/重庆/天津/苏州/郑州/长沙): \").strip() or \"北京\"\n    \n    if s <= 0:\n        print(\"❌ 月薪必须大于0\")\n    else:\n        city_code = CITY_MAP.get(city_input, city_input.lower())\n        result, err = calc_insurance_tax(s, city_code)\n        if err:\n            print(err)\n        else:\n            print(f\"=== {result['city']} 五险一金计算明细 (数据版本: {result['data_version']}, 生效: {result['data_effective']}) ===\")\n            print(f\"税前月薪: ¥{result['salary']:,.2f}\")\n            print(f\"\\n--- 个人缴纳 ---\")\n            for name, amount in result[\"personal_insurance\"].items():\n                if name == \"公积金\":\n                    print(f\"  {name}: ¥{amount:,.2f} (单位另缴 ¥{result['housing_fund_employer']:,.2f})\")\n                else:\n                    print(f\"  {name}: ¥{amount:,.2f}\")\n            print(f\"  个人社保合计: ¥{result['personal_total']:,.2f}\")\n            print(f\"\\n--- 个税 ---\")\n            print(f\"  应缴个税: ¥{result['tax']:,.2f}\")\n            print(f\"\\n实发工资: ¥{result['net_salary']:,.2f}\")\n            print(f\"  (相当于税前 {result['net_salary']/result['salary']*100:.1f}%)\")\n            print(f\"\\n--- 单位缴纳（供参考）---\")\n            print(f\"  单位社保公积金合计: ¥{result['company_total']:,.2f}\")\nexcept ValueError:\n    print(\"❌ 输入错误：月薪请输入纯数字。例如：15000\")\nexcept Exception as e:\n    print(f\"❌ 计算失败: {e}\")\n```\n\n</details>\n\n---\n\n#### 1.2.1 个税计算器（单独查询）\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 个税计算器</summary>\n\n```python\nimport os, yaml\n\ndef calc_personal_tax_only(salary, social_insurance_deduction=0, special_deduction=0):\n    \"\"\"单独计算个税（7级超额累进）\"\"\"\n    yaml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), \"references\", \"tax_2026.yaml\")\n    with open(yaml_path, encoding=\"utf-8\") as f:\n        data = yaml.safe_load(f)\n    \n    monthly_threshold = data.get(\"individual_income_tax\", {}).get(\"monthly_threshold\", 5000)\n    tax_brackets = data.get(\"individual_income_tax\", {}).get(\"brackets\", [])\n    \n    taxable_income = max(0, salary - social_insurance_deduction - special_deduction - monthly_threshold)\n    tax = 0\n    bracket_used = \"0%\"\n    for bracket in tax_brackets:\n        if taxable_income <= 0:\n            break\n        taxable_at_bracket = min(taxable_income, bracket[\"limit\"])\n        tax += taxable_at_bracket * bracket[\"rate\"]\n        taxable_income -= taxable_at_bracket\n        if taxable_at_bracket > 0:\n            bracket_used = f\"{bracket['rate']*100:.0f}%\"\n    \n    return round(tax, 2), bracket_used\n\ntry:\n    s = float(input(\"税前月薪: \") or 20000)\n    ins = float(input(\"社保公积金扣除(默认0): \") or 0)\n    special = float(input(\"专项附加扣除(默认0): \") or 0)\n    \n    if s <= 0:\n        print(\"❌ 月薪必须大于0\")\n    else:\n        tax, bracket = calc_personal_tax_only(s, ins, special)\n        net = s - ins - tax\n        print(f\"应纳税: ¥{tax:,.2f} (适用 {bracket} 税率)\")\n        print(f\"税后工资: ¥{net:,.2f}\")\nexcept ValueError:\n    print(\"❌ 请输入数字\")\n```\n\n</details>\n\n---\n\n#### 1.3 日期计算器\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nfrom datetime import date\ntry:\n    a = date.today()\n    b = input(\"目标日期 YYYY-MM-DD: \").strip() or \"2026-10-01\"\n    y,m,d = map(int, b.split(\"-\"))\n    target = date(y,m,d)\n    diff = (target - a).days\n    print(f\"相差: {diff} 天 ({diff//7} 周)\")\n    if diff < 0:\n        print(\"⚠️ 目标日期已经过去了\")\nexcept ValueError:\n    print(\"❌ 日期格式错误。正确格式：年-月-日，例如：2026-10-01\")\n```\n\n</details>\n\n---\n\n#### 1.4 单位换算器\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nc = {\n    # 长度\n    \"km_mi\":  (lambda v: v*0.621371,  \"公里\", \"英里\"),\n    \"mi_km\":  (lambda v: v/0.621371,  \"英里\", \"公里\"),\n    \"m_ft\":   (lambda v: v*3.28084,   \"米\",   \"英尺\"),\n    \"ft_m\":   (lambda v: v/3.28084,   \"英尺\", \"米\"),\n    \"cm_in\":  (lambda v: v/2.54,      \"厘米\", \"英寸\"),\n    \"in_cm\":  (lambda v: v*2.54,      \"英寸\", \"厘米\"),\n    # 重量\n    \"kg_lb\":  (lambda v: v*2.20462,   \"公斤\", \"磅\"),\n    \"lb_kg\":  (lambda v: v/2.20462,   \"磅\",   \"公斤\"),\n    \"g_oz\":   (lambda v: v/28.3495,   \"克\",   \"盎司\"),\n    \"oz_g\":   (lambda v: v*28.3495,   \"盎司\", \"克\"),\n    # 温度\n    \"c_f\":    (lambda v: v*9/5+32,    \"°C\",   \"°F\"),\n    \"f_c\":    (lambda v: (v-32)*5/9,  \"°F\",   \"°C\"),\n    \"c_k\":    (lambda v: v+273.15,    \"°C\",   \"K\"),\n    \"k_c\":    (lambda v: v-273.15,    \"K\",    \"°C\"),\n    # 面积\n    \"sqm_sqft\": (lambda v: v*10.7639, \"平方米\", \"平方英尺\"),\n    \"mu_sqm\":   (lambda v: v*666.667, \"亩\",     \"平方米\"),\n    \"ha_mu\":    (lambda v: v*15,       \"公顷\",   \"亩\"),\n    # 速度\n    \"kmh_mph\":  (lambda v: v*0.621371, \"km/h\",  \"mph\"),\n    \"ms_kmh\":   (lambda v: v*3.6,      \"m/s\",   \"km/h\"),\n    \"knot_kmh\": (lambda v: v*1.852,    \"节\",    \"km/h\"),\n    # 数据存储\n    \"mb_gb\":  (lambda v: v/1024,       \"MB\",   \"GB\"),\n    \"gb_mb\":  (lambda v: v*1024,       \"GB\",   \"MB\"),\n    \"gb_tb\":  (lambda v: v/1024,       \"GB\",   \"TB\"),\n    \"byte_mb\":(lambda v: v/1048576,    \"字节\", \"MB\"),\n    # 压力\n    \"bar_psi\":(lambda v: v*14.5038,    \"bar\",  \"psi\"),\n    \"atm_kpa\":(lambda v: v*101.325,    \"atm\",  \"kPa\"),\n    # 能量\n    \"kcal_kj\":(lambda v: v*4.184,      \"千卡\", \"千焦\"),\n    \"kwh_kj\": (lambda v: v*3600,       \"度电\", \"千焦\"),\n}\ntry:\n    m = input(\"类型(如km_mi/mb_gb/mu_sqm):\").strip() or \"km_mi\"\n    v = float(input(\"数值: \") or 100)\n    if m not in c:\n        print(f\"❌ 不支持的换算：'{m}'\")\n        print(f\"支持的类型：{', '.join(c.keys())}\")\n    else:\n        r, uf, ut = c[m]\n        print(f\"✅ {v} {uf} = {r(v):.4f} {ut}\")\nexcept ValueError:\n    print(\"❌ 请输入纯数字，例如：100\")\n```\n\n</details>\n\n---\n\n#### 1.5 汇率查询 ⭐ v4.0.0 时效治理\n\n**✅ 开箱即用**（零依赖：内置常见汇率缓存 + 联网实时查询可选）\n\n> v4.0 时效治理：缓存条目带抓取时间戳，超 72 小时标注陈旧 + 警示横幅。在线刷新失败保留旧值 + 陈旧标注，绝不静默用旧值冒充新值。\n\n<details>\n<summary>📋 汇率查询（时效感知版）</summary>\n\n```python\nimport urllib.request, json, os, time\nfrom datetime import datetime, timedelta\n\nCACHE = {\n    \"USD\": 1.0, \"CNY\": 7.24, \"EUR\": 0.92, \"GBP\": 0.79, \"JPY\": 157.35, \"KRW\": 1385.0,\n    \"HKD\": 7.81, \"TWD\": 32.37, \"SGD\": 1.34, \"AUD\": 1.53, \"CAD\": 1.37, \"CHF\": 0.89,\n    \"INR\": 83.45, \"RUB\": 91.5, \"THB\": 36.2, \"VND\": 25100, \"MYR\": 4.72, \"PHP\": 58.6,\n    \"IDR\": 16250, \"BRL\": 5.43, \"MXN\": 17.15, \"ZAR\": 18.75, \"AED\": 3.67, \"SAR\": 3.75,\n    \"NZD\": 1.65, \"SEK\": 10.6, \"NOK\": 10.9, \"DKK\": 6.88, \"PLN\": 4.05, \"TRY\": 32.8,\n    \"UAH\": 40.5, \"EGP\": 48.5\n}\n\nCACHE_FILE = os.path.join(os.path.expanduser(\"~\"), \".workbuddy\", \"dgngjx_exchange_cache.json\")\nTTL_HOURS = 72\n\ndef load_cache():\n    if os.path.exists(CACHE_FILE):\n        try:\n            with open(CACHE_FILE, encoding=\"utf-8\") as f:\n                return json.load(f)\n        except (json.JSONDecodeError, IOError):\n            pass\n    return {}\n\ndef save_cache(cache):\n    os.makedirs(os.path.dirname(CACHE_FILE), exist_ok=True)\n    with open(CACHE_FILE, \"w\", encoding=\"utf-8\") as f:\n        json.dump(cache, f, ensure_ascii=False, indent=2)\n\ndef is_stale(timestamp_str):\n    try:\n        ts = datetime.fromisoformat(timestamp_str)\n        return datetime.now() - ts > timedelta(hours=TTL_HOURS)\n    except (ValueError, TypeError):\n        return True\n\ndef get_cached_rate(cur):\n    cache = load_cache()\n    entry = cache.get(cur)\n    if entry:\n        rate = entry.get(\"rate\")\n        ts = entry.get(\"timestamp\")\n        if rate and ts:\n            stale = is_stale(ts)\n            return rate, ts, stale\n    return None, None, False\n\ndef fetch_live_rate(fr, to):\n    try:\n        url = f\"https://api.exchangerate-api.com/v4/latest/{fr}\"\n        req = urllib.request.Request(url, headers={\"User-Agent\": \"dgngjx/4.0\"})\n        data = json.loads(urllib.request.urlopen(req, timeout=6).read())\n        if \"rates\" in data and to in data[\"rates\"]:\n            rate = data[\"rates\"][to]\n            cache = load_cache()\n            cache[to] = {\"rate\": rate, \"timestamp\": datetime.now().isoformat(), \"base\": fr}\n            cache[fr] = {\"rate\": 1.0, \"timestamp\": datetime.now().isoformat(), \"base\": fr}\n            save_cache(cache)\n            return rate, True\n    except Exception:\n        pass\n    return None, False\n\ndef convert_currency(amt, fr, to):\n    fr = fr.upper()\n    to = to.upper()\n    rate, live = fetch_live_rate(fr, to)\n    if live:\n        return rate, \"实时汇率\", False, None\n    if fr == to:\n        return 1.0, \"同币种\", False, None\n    fr_rate, fr_ts, fr_stale = get_cached_rate(fr)\n    to_rate, to_ts, to_stale = get_cached_rate(to)\n    if fr_rate and to_rate:\n        cross_rate = to_rate / fr_rate\n        stale = fr_stale or to_stale\n        older_ts = min(fr_ts, to_ts) if fr_ts and to_ts else (fr_ts or to_ts)\n        days_old = (datetime.now() - datetime.fromisoformat(older_ts)).days if older_ts else \"?\"\n        tag = f\"缓存汇率({days_old}天前)\"\n        return cross_rate, tag, stale, older_ts\n    if fr in CACHE and to in CACHE:\n        cross_rate = CACHE[to] / CACHE[fr]\n        return cross_rate, \"静态缓存(离线)\", True, None\n    return None, None, False, None\n\ns = input(\"输入(如 100 USD CNY，或回车查汇率表):\").strip()\nif not s:\n    print(\"=== 常见货币汇率 (1 USD 基准) ===\")\n    cache = load_cache()\n    for cur, rate in sorted(CACHE.items()):\n        cached_rate, ts, stale = get_cached_rate(cur)\n        if cached_rate and not stale:\n            print(f\"  1 USD = {cached_rate:>10.2f} {cur}  (实时)\")\n        elif cached_rate:\n            days = (datetime.now() - datetime.fromisoformat(ts)).days\n            print(f\"  1 USD = {cached_rate:>10.2f} {cur}  ({days}天前)\")\n        else:\n            print(f\"  1 USD = {rate:>10.2f} {cur}  (静态)\")\nelse:\n    parts = s.split()\n    try:\n        amt = float(parts[0])\n        fr = (parts[1] if len(parts) > 1 else \"USD\").upper()\n        to = (parts[2] if len(parts) > 2 else \"CNY\").upper()\n        rate, tag, stale, ts = convert_currency(amt, fr, to)\n        if rate is None:\n            print(f\"❌ 不支持 {fr}→{to}。可用：{', '.join(sorted(CACHE.keys()))}\")\n        else:\n            result = amt * rate\n            print(f\"✅ {amt:.2f} {fr} = {result:.2f} {to}  ({tag})\")\n            if stale:\n                print(f\"   ⚠️ 数据更新于 {ts[:10] if ts else 'N/A'}，可能已过期\")\n                print(f\"   💡 联网后可获取实时汇率\")\n    except (ValueError, IndexError):\n        print(\"❌ 格式：100 USD CNY（金额 源货币 目标货币）\")\n```\n\n</details>\n\nFile v4.1.0:references/modules/dev.md\n\n# 💻 模块 5：开发工具\n\n---\n\n#### 5.1 JSON 工具\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport json\ntry:\n    r = input(\"输入JSON:\").strip() or '{\"key\":\"value\"}'\n    if not r: print(\"❌ 没有输入\")\n    else:\n        p = json.loads(r)\n        print(f\"✅ 合法\\n格式化: {json.dumps(p,ensure_ascii=False,indent=2)}\")\n        print(f\"压缩: {json.dumps(p,ensure_ascii=False,separators=(',',':'))}\")\nexcept json.JSONDecodeError as e:\n    print(f\"❌ JSON错误: {e}\")\n```\n\n</details>\n\n---\n\n#### 5.2 HTTP 接口测试\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport urllib.request, socket, ssl\ntry:\n    u = input(\"URL:\").strip() or \"https://api.github.com\"\n    if not u: print(\"❌ URL不能为空\")\n    elif not u.startswith(\"http\"): print(\"❌ 应以 http:// 或 https:// 开头\")\n    else:\n        req = urllib.request.Request(u, headers={\"User-Agent\":\"dgngjx/3.5\"})\n        ctx = ssl.create_default_context()\n        ctx.check_hostname = False\n        resp = urllib.request.urlopen(req, timeout=15, context=ctx)\n        body = resp.read()\n        print(f\"✅ 状态: {resp.status} ({len(body)} bytes)\")\nexcept urllib.error.HTTPError as e:\n    print(f\"❌ HTTP {e.code}: {e.reason}\")\nexcept urllib.error.URLError as e:\n    print(f\"❌ 网络错误: {e.reason}\")\nexcept Exception as e:\n    print(f\"❌ {e}\")\n```\n\n</details>\n\n---\n\n#### 5.3 Token 计算器\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nt = input(\"文本:\") or \"计算Token数\"\nif not t.strip(): print(\"❌ 请输入文字\")\nelse:\n    cn = sum(1 for c in t if '一' <= c <= '鿿')\n    en = len(t) - cn\n    print(f\"中文: {cn} ≈{int(cn/1.5)} | 英文: {en} ≈{int(en/4)} | 总计 ≈{int(cn/1.5+en/4)}\")\n```\n\n</details>\n\n---\n\n#### 5.4 在线 Photoshop\n\n📦 **需 Pillow**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nfrom PIL import Image, ImageEnhance\ntry:\n    f = input(\"图片路径:\").strip().strip('\"').replace(\"\\\\\",\"/\") or \"photo.jpg\"\n    img = Image.open(f)\n    w,h = map(int, input(\"裁剪 宽,高:\").strip() or \"200,200\").split(\",\"))\n    if w > img.width or h > img.height: w,h = min(w,img.width), min(h,img.height)\n    img2 = img.crop(((img.width-w)//2,(img.height-h)//2,w+img.width//2,h+img.height//2))\n    img2.save(\"cropped.jpg\")\n    e = ImageEnhance.Brightness(img).enhance(float(input(\"亮度(1.2):\") or 1.2))\n    e.save(\"bright.jpg\")\n    print(\"✅ 已保存 cropped.jpg, bright.jpg\")\nexcept Exception as e: print(f\"❌ {e}\")\n```\n\n</details>\n\n---\n\n#### 5.5 Mermaid 时序图\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nseq = input(\"时序图代码:\").strip() or \"A->B: Hello\"\nif seq: print(f\"sequenceDiagram\\n{seq}\")\nelse: print(\"❌ 代码不能为空\")\n```\n\n</details>\n\nFile v4.1.0:references/modules/edu.md\n\n# 📚 模块 3：教育工具\n\n---\n\n#### 3.1 知识查询\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport urllib.request, urllib.parse, json, socket, re\n\ndef _wiki_fallback(query: str) -> list[dict]:\n    url = f\"https://baike.deno.dev/item/{urllib.parse.quote(query)}?encode=json\"\n    req = urllib.request.Request(url, headers={\"User-Agent\":\"dgngjx/3.5\"})\n    resp = urllib.request.urlopen(req, timeout=10)\n    data = json.loads(resp.read())\n    results = []\n    if isinstance(data, list):\n        for item in data:\n            title = item.get(\"title\", \"\")\n            abstract = item.get(\"abstract\", \"\").replace(\"\\n\", \" \").strip()\n            if title: results.append({\"title\": title, \"snippet\": abstract[:200]})\n    elif isinstance(data, dict):\n        title = data.get(\"title\", query)\n        abstract = data.get(\"abstract\", data.get(\"description\", \"\"))\n        results = [{\"title\": title, \"snippet\": str(abstract)[:200]}]\n    return results\n\ntry:\n    q = input(\"关键词:\") or \"勾股定理\"\n    if not q.strip():\n        print(\"❌ 关键词不能为空\")\n    else:\n        results = []\n        try:\n            url = f\"https://zh.wikipedia.org/w/api.php?action=query&list=search&srsearch={urllib.parse.quote(q)}&format=json&srlimit=3\"\n            req = urllib.request.Request(url, headers={\"User-Agent\":\"dgngjx/3.5\"})\n            resp = urllib.request.urlopen(req, timeout=10)\n            data = json.loads(resp.read())\n            results = data.get(\"query\",{}).get(\"search\",[])\n        except Exception as e:\n            print(f\"⚠️ Wikipedia 失败（{type(e).__name__}），切换到百度百科...\")\n        if not results:\n            try: results = _wiki_fallback(q)\n            except Exception as e2: print(f\"⚠️ 百度百科也失败: {e2}\")\n        if not results:\n            print(f\"❌ 没有找到关于「{q}」的内容\")\n            print(f\"   百度搜索: https://www.baidu.com/s?wd={urllib.parse.quote(q)}\")\n        else:\n            for r in results:\n                t = r.get('title','')\n                s = re.sub(r'<[^>]+>', '', r.get('snippet',''))[:200]\n                print(f\"\\n📖 {t}\\n   {s}...\")\nexcept Exception as e:\n    print(f\"❌ 意外错误: {e}\")\n```\n\n</details>\n\nFile v4.1.0:references/modules/img.md\n\n# 🖼️ 模块 6：图片工具\n\n---\n\n#### 6.1 img 统一入口 ⭐ v3.9.0 合并为子命令\n\n> v3.9.0 将图片 5 功能合并为 `img` 统一入口，通过子命令调用：`compress`（压缩）、`convert`（格式转换）、`removebg`（抠图）、`idphoto`（证件照）、`repair`（修复）。\n\n<details>\n<summary>📋 img.compress（图片压缩）</summary>\n\n```python\nfrom PIL import Image, ImageFile\nimport os, multiprocessing\nImageFile.LOAD_TRUNCATED_IMAGES = True\nImage.MAX_IMAGE_PIXELS = 180_000_000\n\ndef _fix_path(p):\n    return p.strip().strip('\"').strip(\"'\").replace(\"\\\\\",\"/\").replace(\"//\",\"/\")\n\ndef _auto_workers():\n    cpus = multiprocessing.cpu_count() or 2\n    try:\n        import psutil\n        ram_gb = psutil.virtual_memory().total / (1024**3)\n    except ImportError:\n        ram_gb = 8\n    if ram_gb < 4 or cpus <= 2:\n        return 1\n    elif ram_gb < 8 or cpus <= 4:\n        return min(cpus - 1, 2)\n    else:\n        return min(cpus, 4)\n\nEXTS = {\".jpg\",\".jpeg\",\".png\",\".webp\",\".bmp\",\".tiff\",\".gif\"}\nOUT_FMT = {\"JPEG\":\".jpg\", \"PNG\":\".png\", \"WEBP\":\".webp\", \"BMP\":\".bmp\"}\n\npath = _fix_path(input(\"图片路径或目录:\").strip() or \"photos\")\nif not os.path.exists(path):\n    print(f\"❌ 不存在: {path}\")\nelse:\n    if os.path.isdir(path):\n        files = [os.path.join(path, f) for f in os.listdir(path)\n                 if os.path.splitext(f)[1].lower() in EXTS]\n    else:\n        files = [path]\n    if not files:\n        print(\"⚠️ 未找到图片文件\")\n    else:\n        print(f\"📁 共 {len(files)} 张图片\")\n        q = int(input(\"质量(1-100,建议75-85):\") or 80)\n        q = max(1, min(q, 100))\n        fmt = input(\"输出格式(JPEG/PNG/WebP/BMP,默认JPEG):\").strip().upper() or \"JPEG\"\n        if fmt not in OUT_FMT:\n            print(f\"❌ 不支持 {fmt}，使用 JPEG\")\n            fmt = \"JPEG\"\n        workers = _auto_workers()\n        print(f\"⚡ 并发数: {workers}（自动硬件适配）\")\n        ok = fail = saved_total = 0\n        for i, f in enumerate(files, 1):\n            try:\n                img = Image.open(f)\n                if img.mode in ('RGBA','PA') and fmt != 'PNG':\n                    img = img.convert('RGB')\n                base = os.path.splitext(os.path.basename(f))[0]\n                out = os.path.join(os.path.dirname(f), f\"{base}_compressed{OUT_FMT[fmt]}\")\n                save_opts = {\"quality\": q, \"optimize\": True} if fmt == \"JPEG\" else {}\n                img.save(out, format=fmt, **save_opts)\n                o, c = os.path.getsize(f)/1024, os.path.getsize(out)/1024\n                saved_total += max(0, o - c)\n                ok += 1\n                if len(files) <= 20 or i % 5 == 0:\n                    print(f\"  [{i}/{len(files)}] {os.path.basename(f)}: {o:.0f}KB→{c:.0f}KB\")\n            except Exception as e:\n                fail += 1\n                print(f\"  [{i}/{len(files)}] {os.path.basename(f)} ❌ {e}\")\n        print(f\"\\n✅ 成功 {ok} / 失败 {fail}\")\n        print(f\"   累计节省 {saved_total/1024:.1f} MB\")\n```\n\n</details>\n\n<details>\n<summary>📋 img.convert（格式转换 JPEG/PNG/WebP/BMP）</summary>\n\n```python\nfrom PIL import Image\nimport os\n\ndef _fix_path(p):\n    return p.strip().strip('\"').strip(\"'\").replace(\"\\\\\",\"/\").replace(\"//\",\"/\")\n\nEXTS = {\".jpg\",\".jpeg\",\".png\",\".webp\",\".bmp\",\".tiff\",\".gif\"}\nOUT_FMT = {\"JPEG\":\".jpg\", \"PNG\":\".png\", \"WEBP\":\".webp\", \"BMP\":\".bmp\"}\n\npath = _fix_path(input(\"图片路径:\").strip() or \"photo.jpg\")\nif not os.path.exists(path):\n    print(f\"❌ 不存在: {path}\")\nelse:\n    fmt = input(\"目标格式(JPEG/PNG/WebP/BMP):\").strip().upper() or \"PNG\"\n    if fmt not in OUT_FMT:\n        print(f\"❌ 不支持 {fmt}\")\n    else:\n        img = Image.open(path)\n        if img.mode in ('RGBA','PA') and fmt != 'PNG':\n            img = img.convert('RGB')\n        base = os.path.splitext(os.path.basename(path))[0]\n        out = os.path.join(os.path.dirname(path), f\"{base}_converted{OUT_FMT[fmt]}\")\n        img.save(out, format=fmt)\n        print(f\"✅ 已转换: {out}\")\n```\n\n</details>\n\n<details>\n<summary>📋 img.removebg（人像抠图）</summary>\n\n```python\nfrom rembg import remove\nimport os\ntry:\n    inp = input(\"图片:\").strip().strip('\"').replace(\"\\\\\",\"/\") or \"portrait.jpg\"\n    if not os.path.exists(inp):\n        print(f\"❌ 文件不存在: {inp}\")\n    else:\n        out = f\"bg_removed_{os.path.basename(inp)}.png\"\n        result = remove(open(inp,\"rb\").read())\n        open(out,\"wb\").write(result)\n        print(f\"✅ 完成: {out}\")\nexcept ImportError: print(\"❌ 缺少 rembg: pip install rembg\")\nexcept Exception as e: print(f\"❌ {e}\")\n```\n\n</details>\n\n<details>\n<summary>📋 img.idphoto（证件照生成）</summary>\n\n```python\nfrom PIL import Image\ntry:\n    f = input(\"照片:\").strip().strip('\"').replace(\"\\\\\",\"/\") or \"face.jpg\"\n    sz = input(\"尺寸(一寸/二寸):\").strip()或 \"一寸\"\n    bg = input(\"背景色(白色/蓝色/红色):\").strip()或 \"白色\"\n    sizes = {\"一寸\":(295,413),\"二寸\":(413,579)}\n    bgs = {\"白色\":(255,255,255),\"蓝色\":(0,0,255),\"红色\":(255,0,0)}\n    w,h = sizes.get(sz, sizes[\"一寸\"])\n    img = Image.open(f).resize((w,h),Image.LANCZOS)\n    canvas = Image.new(\"RGB\",(w,h),bgs.get(bg, (255,255,255)))\n    canvas.paste(img,(0,0),img if img.mode==\"RGBA\" else None)\n    canvas.save(f\"证件照_{sz}_{bg}.png\")\n    print(f\"✅ 证件照_{sz}_{bg}.png ({w}x{h})\")\nexcept Exception as e: print(f\"❌ {e}\")\n```\n\n</details>\n\n<details>\n<summary>📋 img.repair（图片修复）</summary>\n\n```python\nfrom PIL import Image, ImageEnhance\ntry:\n    f = input(\"老照片路径:\").strip().strip('\"').replace(\"\\\\\",\"/\") or \"old.jpg\"\n    img = Image.open(f)\n    img = ImageEnhance.Sharpness(img).enhance(1.5)\n    img = ImageEnhance.Brightness(img).enhance(1.1)\n    img = ImageEnhance.Contrast(img).enhance(1.2)\n    img.save(\"repaired.jpg\")\n    print(\"✅ 完成: repaired.jpg\")\nexcept Exception as e: print(f\"❌ {e}\")\n```\n\n</details>\n\nFile v4.1.0:references/modules/life.md\n\n# 🎮 模块 4：生活娱乐\n\n> ⚠️ v4.1.0 起，本模块移至 `ent`（娱乐）可选包，默认不加载。\n\n---\n\n#### 4.1 娱乐小工具\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport urllib.request, json, random\n\ndef _joke_cn():\n    apis = [\n        (\"https://api.apiopen.top/getJoke?page=1&count=1&type=txt\", lambda d: d.get(\"result\",[{}])[0].get(\"content\",\"\")),\n        (\"https://v1.hitokoto.cn/?c=a\", lambda d: f\"{d.get('hitokoto','')} —— {d.get('from','')}\")),\n        (\"https://api.oioweb.cn/api/common/Hitokoto\", lambda d: f\"{d.get('result',{}).get('content','')} —— {d.get('result',{}).get('from','')}\")),\n    ]\n    for url, extract in apis:\n        try:\n            r = urllib.request.urlopen(urllib.request.Request(url, headers={\"User-Agent\":\"dgngjx/3.5\"}), timeout=5)\n            data = json.loads(r.read())\n            text = extract(data)\n            if text.strip(): return text\n        except: continue\n    return \"\"\n\ntry:\n    c = input(\"选择(1笑话/2一言/3运势):\").strip() or \"1\"\n    if c == \"1\":\n        joke = _joke_cn()\n        if joke: print(f\"😄 {joke}\")\n        else:\n            jokes = [\n                (\"程序员为什么喜欢用暗色主题？\", \"因为光明会引来 bug！\"),\n                (\"老婆问程序员丈夫：你到底爱不爱我？\", \"当然爱。老婆：那你能不能不在'当然爱'后面加分号？感觉像执行完就结束。\"),\n            ]\n            s, p = random.choice(jokes)\n            print(f\"📶 联网失败，给你讲个本地笑话：\\n   {s}\\n   → {p}\")\n    elif c == \"2\":\n        for url, extract in [\n            (\"https://v1.hitokoto.cn/\", lambda d: f\"{d.get('hitokoto','')} —— {d.get('from','')}\"),\n            (\"https://tenapi.cn/v2/yiyan\", lambda d: d.get(\"data\",\"\") or d.get(\"content\",\"\")),\n        ]:\n            try:\n                r = urllib.request.urlopen(urllib.request.Request(url, headers={\"User-Agent\":\"dgngjx/3.5\"}), timeout=5)\n                t = extract(json.loads(r.read()))\n                if t: print(f\"✨ {t}\"); break\n            except: continue\n        else: print(\"📶 一言API不可用，试试 https://hitokoto.cn/\")\n    elif c == \"3\":\n        print(random.choice([\"大吉\",\"中吉\",\"小吉\",\"吉\",\"末吉\",\"凶\",\"大凶\"]))\n    else: print(\"❌ 请输入 1、2 或 3\")\nexcept Exception as e: print(f\"❌ {e}\")\n```\n\n</details>\n\n---\n\n#### 4.2 壁纸中心\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport urllib.request, urllib.parse, json\n\ndef _wallpaper_cn():\n    for url, src in [\n        (\"https://imgapi.cn/api.php?zd=pc&fl=fengjing&gs=jpg\", \"风景随机\"),\n        (\"https://picsum.photos/1920/1080\", \"随机摄影\"),\n    ]:\n        try:\n            r = urllib.request.urlopen(urllib.request.Request(url, headers={\"User-Agent\":\"dgngjx/3.5\"}), timeout=8)\n            final = r.geturl()\n            if final and final != url: return (final, src)\n        except: continue\n    return None\n\ntry:\n    _ = input(\"关键词:\").strip() or \"nature\"\n    result = _wallpaper_cn()\n    if result:\n        print(f\"📷 来源: {result[1]}\\n   下载: {result[0]}\")\n        print(\"   💡 右键→图片另存为\")\n    else:\n        print(\"❌ 壁纸源不可用\")\n        print(\"   • https://unsplash.com/s/photos/nature\")\n        print(\"   • https://pixabay.com/zh/images/search/nature/\")\nexcept Exception as e: print(f\"❌ {e}\")\n```\n\n</details>\n\n---\n\n#### 4.3 BMI 计算器 ⭐ v3.7.0 新增\n\n**✅ 开箱即用**（纯数学运算，零依赖）\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\ntry:\n    w = float(input(\"体重(kg):\"))\n    h = float(input(\"身高(cm):\"))\n    if w <= 0 or h <= 0:\n        print(\"❌ 体重和身高必须大于0\")\n    elif h > 300 or w > 700:\n        print(\"❌ 请输入合理的体重(kg)和身高(cm)\")\n    else:\n        bmi = w / ((h/100) ** 2)\n        if bmi < 18.5:\n            label = \"偏瘦\"; advice = \"适当增加营养摄入\"\n        elif bmi < 24:\n            label = \"正常\"; advice = \"保持良好生活习惯\"\n        elif bmi < 28:\n            label = \"偏胖\"; advice = \"建议控制饮食+增加运动\"\n        else:\n            label = \"肥胖\"; advice = \"建议就医评估+制定减重计划\"\n        ideal_low = 18.5 * (h/100)**2\n        ideal_high = 24 * (h/100)**2\n        print(f\"✅ BMI: {bmi:.1f}  [{label}]\")\n        print(f\"   理想体重范围: {ideal_low:.1f} - {ideal_high:.1f} kg\")\n        print(f\"   建议: {advice}\")\n        print(f\"   ⚠️ BMI 仅供筛查，不替代医学诊断\")\nexcept ValueError:\n    print(\"❌ 请输入数字，如 70 和 175\")\n```\n\n</details>\n\n---\n\n#### 4.4 番茄钟 ⭐ v3.7.0 新增\n\n**✅ 开箱即用**（纯 Python 标准库 time，零依赖）\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport time, sys\n\nprint(\"🍅 番茄工作法：专注25分钟 + 休息5分钟\")\nprint(\"   Ctrl+C 可随时退出\")\ntry:\n    work_min = int(input(\"专注时长(分钟,默认25):\") or 25)\n    rest_min = int(input(\"休息时长(分钟,默认5):\") or 5)\n    rounds = int(input(\"轮数(默认4):\") or 4)\nexcept ValueError:\n    print(\"❌ 请输入整数\"); sys.exit()\n\nwork_sec = max(1, work_min) * 60\nrest_sec = max(1, rest_min) * 60\n\ndef _countdown(seconds, label):\n    for remaining in range(seconds, 0, -1):\n        m, s = divmod(remaining, 60)\n        print(f\"\\r   {label}: {m:02d}:{s:02d}\", end=\"\", flush=True)\n        time.sleep(1)\n    print()\n\ntry:\n    for r in range(1, rounds + 1):\n        print(f\"\\n=== 第 {r}/{rounds} 轮 ===\")\n        _countdown(work_sec, \"⏳ 专注中\")\n        print(\"🔔 时间到！休息一下吧~\")\n        try:\n            print(\"\\a\", end=\"\", flush=True)\n        except Exception:\n            pass\n        if r < rounds:\n            _countdown(rest_sec, \"☕ 休息中\")\n            print(\"✅ 休息结束，准备下一轮\")\n        else:\n            print(f\"🎉 全部完成！共专注 {rounds * work_min} 分钟\")\nexcept KeyboardInterrupt:\n    print(\"\\n\\n⏹️ 已手动退出\")\n```\n\n</details>\n\nFile v4.1.0:references/modules/pdf.md\n\n# 📄 模块 7：PDF 转换\n\n---\n\n#### 7.1 PDF 合并\n\n📦 **需 PyPDF2**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nfrom PyPDF2 import PdfMerger\nimport os\nfiles = []\ntotal_mb = 0\ntry:\n    while True:\n        f = input(f\"文件{len(files)+1}(回车结束):\").strip().strip('\"').replace(\"\\\\\",\"/\")\n        if not f: break\n        if not os.path.exists(f): print(f\"❌ 跳过: {f}\"); continue\n        size_mb = os.path.getsize(f)/1024/1024\n        total_mb += size_mb\n        if size_mb > 500: print(f\"⚠️ {size_mb:.0f}MB 大文件，合并可能较慢\")\n        files.append(f)\n    if len(files) < 2: print(\"❌ 至少2个PDF\")\n    else:\n        if total_mb > 100: print(f\"⚠️ 总大小 {total_mb:.0f}MB，需1-5分钟...\")\n        m = PdfMerger()\n        ok = 0\n        for f in files:\n            try: m.append(f); ok+=1\n            except: print(f\"⚠️ 跳过损坏文件: {f}\")\n        if len(m.pages)==0: print(\"❌ 全部损坏\")\n        else:\n            m.write(\"merged.pdf\"); m.close()\n            print(f\"✅ merged.pdf ({ok}/{len(files)} 文件, {os.path.getsize('merged.pdf')/1024/1024:.1f}MB)\")\nexcept Exception as e: print(f\"❌ {e}\")\n```\n\n</details>\n\n---\n\n#### 7.2 PDF 拆分\n\n📦 **需 PyPDF2**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nfrom PyPDF2 import PdfReader, PdfWriter\nimport os\ntry:\n    inp = input(\"PDF路径:\").strip().strip('\"').replace(\"\\\\\",\"/\") or \"source.pdf\"\n    pgs = input(\"页码(如 1-3):\").strip() or \"1-3\"\n    if not os.path.exists(inp): print(\"❌ 文件不存在\")\n    else:\n        r = PdfReader(inp)\n        if r.is_encrypted: print(\"❌ 已加密，请先解密！\")\n        else:\n            w = PdfWriter(); total = len(r.pages); added = 0\n            for p in pgs.split(\",\"):\n                if \"-\" in p:\n                    s,e = map(int, p.split(\"-\"))\n                    if s>e: s,e = e,s\n                    for i in range(max(0,s-1),min(e,total)):\n                        w.add_page(r.pages[i]); added+=1\n                else:\n                    idx = int(p)-1\n                    if 0<=idx<total: w.add_page(r.pages[idx]); added+=1\n            w.write(\"extracted.pdf\")\n            print(f\"✅ extracted.pdf ({added}页)\")\nexcept Exception as e: print(f\"❌ {e}\")\n```\n\n</details>\n\n---\n\n#### 7.3 PDF 加密 / 7.4 PDF 解密\n\n📦 **需 PyPDF2**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nfrom PyPDF2 import PdfReader, PdfWriter\n# 加密\ninp = input(\"PDF路径:\").strip().strip('\"') or \"source.pdf\"\npwd = input(\"密码:\").strip()\nif len(pwd)<4: print(\"⚠️ 密码太短\")\nelse:\n    r=PdfReader(inp); w=PdfWriter()\n    for p in r.pages: w.add_page(p)\n    w.encrypt(pwd); w.write(\"encrypted.pdf\")\n    print(\"✅ encrypted.pdf (密码丢失无法恢复!)\")\n\n# 解密\ninp = input(\"加密PDF:\").strip().strip('\"') or \"encrypted.pdf\"\npwd = input(\"密码:\").strip()\nr=PdfReader(inp)\nif r.is_encrypted: r.decrypt(pwd)\nw=PdfWriter()\nfor p in r.pages: w.add_page(p)\nw.write(\"decrypted.pdf\"); print(\"✅ decrypted.pdf\")\n```\n\n</details>\n\nFile v4.1.0:references/modules/sys.md\n\n# 🖥️ 模块 10：系统工具 ⭐ v3.5 新增模块\n\n---\n\n#### 10.1 系统资源监控 ⭐ v3.8.0 优化（wmic → PowerShell CIM）\n\n**✅ 开箱即用**\n\n实时监控 CPU、内存、磁盘使用率，返回中文信息。v3.8.0 将 wmic 迁移到 PowerShell CIM cmdlet，兼容 Windows Server 2025+。\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport os, platform, subprocess, time\n\ndef _ps_cim(cmd):\n    \"\"\"执行 PowerShell CIM 命令并返回 stdout\"\"\"\n    r = subprocess.run(\n        [\"powershell\", \"-NoProfile\", \"-Command\", cmd],\n        capture_output=True, text=True, timeout=10\n    )\n    return r.stdout.strip()\n\ndef get_system_resources():\n    \"\"\"获取系统资源信息（跨Windows/macOS/Linux）\"\"\"\n    info = {'cpu_usage': 0, 'ram_total_mb': 0, 'ram_used_mb': 0, 'ram_percent': 0,\n            'disk_total_gb': 0, 'disk_used_gb': 0, 'disk_percent': 0,\n            'cpu_count': os.cpu_count() or 2, 'platform': platform.system()}\n    \n    # CPU 使用率\n    try:\n        if info['platform'] == 'Windows':\n            out = _ps_cim(\"Get-CimInstance Win32_Processor | Select-Object -ExpandProperty LoadPercentage\")\n            lines = [l.strip() for l in out.split('\\n') if l.strip().isdigit()]\n            info['cpu_usage'] = int(lines[0]) if lines else 0\n        else:\n            r = subprocess.run(['grep','-c','processor','/proc/cpuinfo'], capture_output=True, text=True, timeout=5)\n            info['cpu_usage'] = 0  # Linux\n    except: pass\n    \n    # 内存信息\n    try:\n        if info['platform'] == 'Windows':\n            out = _ps_cim(\"Get-CimInstance Win32_OperatingSystem | Select-Object FreePhysicalMemory,TotalVisibleMemorySize | Format-List\")\n            for line in out.split('\\n'):\n                line = line.strip()\n                if line.startswith('TotalVisibleMemorySize'):\n                    info['ram_total_mb'] = int(line.split(':')[1].strip()) // 1024\n                elif line.startswith('FreePhysicalMemory'):\n                    free = int(line.split(':')[1].strip()) // 1024\n                    info['ram_used_mb'] = info['ram_total_mb'] - free\n            if info['ram_total_mb'] > 0:\n                info['ram_percent'] = int(info['ram_used_mb'] / info['ram_total_mb'] * 100)\n        else:\n            with open('/proc/meminfo') as f:\n                mem = {}\n                for line in f:\n                    parts = line.split()\n                    if parts[0] in ('MemTotal:','MemAvailable:','MemFree:'):\n                        mem[parts[0]] = int(parts[1])\n            total = mem.get('MemTotal:',0)\n            avail = mem.get('MemAvailable:', mem.get('MemFree:',0))\n            info['ram_total_mb'] = total // 1024\n            info['ram_used_mb'] = (total - avail) // 1024\n            if total > 0: info['ram_percent'] = int((total-avail)/total*100)\n    except: pass\n    \n    # 磁盘信息\n    try:\n        if info['platform'] == 'Windows':\n            out = _ps_cim(\"Get-CimInstance Win32_LogicalDisk | Select-Object Size,FreeSpace,DeviceID | Format-List\")\n            total = free = 0\n            for block in out.split('\\n\\n'):\n                for line in block.split('\\n'):\n                    line = line.strip()\n                    if line.startswith('Size') and ':' in line:\n                        val = line.split(':')[1].strip()\n                        if val.isdigit(): total += int(val)\n                    elif line.startswith('FreeSpace') and ':' in line:\n                        val = line.split(':')[1].strip()\n                        if val.isdigit(): free += int(val)\n            info['disk_total_gb'] = total // (1024**3)\n            info['disk_used_gb'] = (total - free) // (1024**3)\n            if total > 0: info['disk_percent'] = int((total-free)/total*100)\n        else:\n            r = subprocess.run(['df','/'], capture_output=True, text=True, timeout=5)\n            lines = r.stdout.strip().split('\\n')\n            if len(lines)>1:\n                parts = lines[1].split()\n                if len(parts)>=4:\n                    total = int(parts[1])\n                    used = int(parts[2])\n                    info['disk_total_gb'] = total // (1024**2)\n                    info['disk_used_gb'] = used // (1024**2)\n                    info['disk_percent'] = int(parts[4].replace('%',''))\n    except: pass\n    \n    return info\n\nres = get_system_resources()\nprint(\"=\" * 40)\nprint(\"  📊 dgngjx-skill 系统资源监控\")\nprint(\"=\" * 40)\nprint(f\"🖥️ 系统: {res['platform']}\")\nprint(f\"⚡ CPU: {res['cpu_usage']}% 占用 | {res['cpu_count']} 核心\")\nprint(f\"🧠 内存: {res['ram_used_mb']}/{res['ram_total_mb']} MB ({res['ram_percent']}%)\")\nprint(f\"💾 磁盘: {res['disk_used_gb']}/{res['disk_total_gb']} GB ({res['disk_percent']}%)\")\nprint()\n\n# 预警\nif res['ram_percent'] > 90: print(\"🔴 内存严重不足！建议关闭部分程序\")\nelif res['ram_percent'] > 75: print(\"🟡 内存偏高，考虑释放\")\nif res['disk_percent'] > 90: print(\"🔴 磁盘空间告急！清理空间\")\nelif res['disk_percent'] > 80: print(\"🟡 磁盘空间偏低\")\nif res['cpu_usage'] > 90: print(\"🔴 CPU 负载过高\")\nelif res['cpu_usage'] > 75: print(\"🟡 CPU 使用率较高\")\n```\n\n</details>\n\n**⚠️ 可能遇到的坑：**\n\n| 情况 | 原因 | 解决 |\n|------|------|------|\n| 获取失败返回 0 | 权限不足 | 以管理员权限运行 |\n| wmic 被禁用 | Windows 精简版 | v3.8.0 已迁移到 PowerShell CIM，无需 wmic |\n| macOS 内存显示异常 | macOS 内存管理机制 | 正常行为，macOS 积极利用内存 |\n| PowerShell 执行策略限制 | Restricted 策略 | 运行 `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned` |\n\n---\n\n#### 10.2 批量文件重命名 ⭐ v3.5.0 新增\n\n**✅ 开箱即用**（零依赖，纯 os 模块）\n\n批量预览后才能执行，支持正则替换、序号前缀、大小写转换。\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport os, re\n\ndef _fix_path(p):\n    return p.strip().strip('\"').strip(\"'\").replace(\"\\\\\",\"/\").replace(\"//\",\"/\")\n\nBLOCKED_EXTS = {'.exe','.dll','.bat','.cmd','.ps1','.vbs','.sh','.com','.scr','.hta','.reg','.msi','.apk','.jar'}\n\ndir_path = _fix_path(input(\"目录路径:\").strip() or \".\")\nif not os.path.isdir(dir_path):\n    print(f\"❌ 目录不存在: {dir_path}\")\nelse:\n    files = [f for f in os.listdir(dir_path) if os.path.isfile(os.path.join(dir_path,f))]\n    safe_files = [f for f in files if os.path.splitext(f)[1].lower() not in BLOCKED_EXTS]\n    if len(safe_files) != len(files):\n        print(f\"⚠️ 已跳过 {len(files)-len(safe_files)} 个系统/可执行文件\")\n    files = safe_files\n    \n    if not files:\n        print(\"❌ 目录为空或无安全文件\")\n    else:\n        print(f\"\\n📁 {dir_path} 中找到 {len(files)} 个文件\")\n        print()\n        print(\"重命名模式:\")\n        print(\"  1. 添加前缀 (如: photo_001.jpg)\")\n        print(\"  2. 添加后缀 (如: 001_终版.jpg)\")\n        print(\"  3. 正则替换 (正则表达式匹配替换)\")\n        print(\"  4. 大小写转换 (全部小写/大写)\")\n        print(\"  5. 序号重命名 (统一前缀+递增数字)\")\n        mode = input(\"选择模式(1-5):\").strip() or \"1\"\n        \n        previews = []\n        \n        if mode == \"1\":\n            prefix = input(\"前缀:\").strip() or \"file_\"\n            for i, fn in enumerate(files):\n                new = prefix + fn\n                previews.append((fn, new))\n                \n        elif mode == \"2\":\n            suffix = input(\"后缀:\").strip() or \"_ok\"\n            for fn in files:\n                name, ext = os.path.splitext(fn)\n                new = name + suffix + ext\n                previews.append((fn, new))\n                \n        elif mode == \"3\":\n            pattern = input(\"匹配正则:\").strip()\n            repl = input(\"替换为:\").strip()\n            if not pattern:\n                print(\"❌ 正则表达式不能为空\")\n                previews = []\n            else:\n                for fn in files:\n                    new = re.sub(pattern, repl, fn)\n                    previews.append((fn, new))\n                    \n        elif mode == \"4\":\n            upper = input(\"转大写?(Y/n):\").strip().lower() != 'n'\n            for fn in files:\n                new = fn.upper() if upper else fn.lower()\n                previews.append((fn, new))\n                \n        elif mode == \"5\":\n            prefix = input(\"统一前缀:\").strip() or \"file\"\n            start = int(input(\"起始序号(1):\") or 1)\n            width = int(input(\"序号位数(3):\") or 3)\n            for i, fn in enumerate(files):\n                _, ext = os.path.splitext(fn)\n                new = f\"{prefix}_{str(start+i).zfill(width)}{ext}\"\n                previews.append((fn, new))\n        \n        if previews:\n            print(f\"\\n📋 预览（共 {len(previews)} 个文件）:\")\n            print(\"-\" * 60)\n            for old, new in previews[:10]:\n                if old != new:\n                    print(f\"  {old}\")\n                    print(f\"    → {new}\")\n                else:\n                    print(f\"  {old} （无变化）\")\n            if len(previews) > 10:\n                print(f\"  ... 还有 {len(previews)-10} 个文件\")\n            print(\"-\" * 60)\n            \n            new_names = [new for _, new in previews]\n            if len(new_names) != len(set(new_names)):\n                print(\"⚠️ 警告：新文件名存在冲突！\")\n                from collections import Counter\n                for name, cnt in Counter(new_names).items():\n                    if cnt > 1:\n                        print(f\"   冲突: {name} ({cnt}次)\")\n                print(\"   请修改规则避免冲突\")\n            else:\n                confirm = input(f\"\\n确认执行? (输入 YES 确认): \").strip()\n                if confirm == \"YES\":\n                    success = 0\n                    for old, new in previews:\n                        if old != new:\n                            old_path = os.path.join(dir_path, old)\n                            new_path = os.path.join(dir_path, new)\n                            if not os.path.exists(new_path):\n                                os.rename(old_path, new_path)\n                                success += 1\n                            else:\n                                print(f\"⚠️ 已存在，跳过: {new}\")\n                    print(f\"✅ 已重命名 {success} 个文件\")\n                else:\n                    print(\"❎ 已取消\")\n```\n\n</details>\n\n**⚠️ 可能遇到的坑：**\n\n| 情况 | 原因 | 解决 |\n|------|------|------|\n| 输入 YES 仍被拒绝 | 必须大写的 YES | 输入 `YES` |\n| 文件名冲突 | 替换规则导致重名 | 修改正则或前缀 |\n| 无变化 | 规则未匹配 | 检查正则或前缀 |\n| 跳过系统文件 | 安全规则拦截 | 正常行为，不可执行文件不参与 |\n\n---\n\n#### 10.3 Markdown 转 HTML ⭐ v3.5.0 新增\n\n**✅ 开箱即用**（零依赖纯 Python，将 Markdown 文件转为带样式的独立 HTML）\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport os, re, html\n\ndef _fix_path(p):\n    return p.strip().strip('\"').strip(\"'\").replace(\"\\\\\",\"/\").replace(\"//\",\"/\")\n\nBLOCKED_EXTS = {'.exe','.dll','.bat','.cmd','.ps1','.vbs','.sh','.com','.scr','.hta','.reg','.msi','.apk','.jar'}\n\nf = _fix_path(input(\"Markdown文件路径:\").strip() or \"README.md\")\nif not os.path.exists(f):\n    print(f\"❌ 文件不存在: {f}\")\nelse:\n    ext = os.path.splitext(f)[1].lower()\n    if ext in BLOCKED_EXTS:\n        print(f\"🚫 安全规则禁止处理 {ext} 文件\")\n    else:\n        with open(f, 'r', encoding='utf-8') as md_file:\n            md_content = md_file.read()\n        \n        lines = md_content.split('\\n')\n        html_lines = []\n        in_code_block = False\n        in_list = False\n        \n        for line in lines:\n            stripped = line.strip()\n            \n            if stripped.startswith('```'):\n                lang = stripped[3:].strip()\n                if in_code_block:\n                    html_lines.append('</code></pre>')\n                    in_code_block = False\n                else:\n                    html_lines.append(f'<pre><code class=\"language-{lang}\">' if lang else '<pre><code>')\n                    in_code_block = True\n                continue\n            \n            if in_code_block:\n                html_lines.append(html.escape(line))\n                continue\n            \n            if not stripped:\n                if in_list:\n                    html_lines.append('</ul>')\n                    in_list = False\n                html_lines.append('')\n                continue\n            \n            if stripped.startswith('# '):\n                html_lines.append(f'<h1>{html.escape(stripped[2:])}</h1>')\n                continue\n            elif stripped.startswith('## '):\n                html_lines.append(f'<h2>{html.escape(stripped[3:])}</h2>')\n                continue\n            elif stripped.startswith('### '):\n                html_lines.append(f'<h3>{html.escape(stripped[4:])}</h3>')\n                continue\n            \n            if stripped.startswith('- ') or stripped.startswith('* '):\n                if not in_list:\n                    html_lines.append('<ul>')\n                    in_list = True\n                item = stripped[2:]\n                item = re.sub(r'\\*\\*(.+?)\\*\\*', r'<strong>\\1</strong>', item)\n                html_lines.append(f'<li>{item}</li>')\n                continue\n            \n            if stripped.startswith('> '):\n                html_lines.append(f'<blockquote>{html.escape(stripped[2:])}</blockquote>')\n                continue\n            \n            if stripped == '---':\n                html_lines.append('<hr/>')\n                continue\n            \n            paragraph = html.escape(stripped)\n            paragraph = re.sub(r'\\*\\*(.+?)\\*\\*', r'<strong>\\1</strong>', paragraph)\n            paragraph = re.sub(r'`(.+?)`', r'<code>\\1</code>', paragraph)\n            html_lines.append(f'<p>{paragraph}</p>')\n        \n        if in_list:\n            html_lines.append('</ul>')\n        \n        body_content = '\\n'.join(html_lines)\n        \n        full_html = f'''<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>{os.path.basename(f)}</title>\n<style>\nbody {{ font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 40px 20px; color: #333; line-height: 1.8; }}\nh1 {{ color: #1a1a1a; border-bottom: 2px solid #eee; padding-bottom: 10px; }}\nh2 {{ color: #2c3e50; margin-top: 30px; }}\nh3 {{ color: #34495e; }}\ncode {{ background: #f4f4f4; padding: 2px 6px; border-radius: 3px; font-size: 0.9em; }}\npre {{ background: #2d2d2d; color: #f8f8f2; padding: 16px; border-radius: 6px; overflow-x: auto; }}\nblockquote {{ border-left: 4px solid #ddd; margin: 0; padding-left: 16px; color: #666; }}\nul {{ padding-left: 24px; }}\nli {{ margin-bottom: 4px; }}\nhr {{ border: none; border-top: 1px solid #eee; margin: 20px 0; }}\nstrong {{ color: #1a1a1a; }}\n</style>\n</head>\n<body>\n{body_content}\n</body>\n</html>'''\n        \n        out_name = os.path.splitext(f)[0] + '.html'\n        with open(out_name, 'w', encoding='utf-8') as out:\n            out.write(full_html)\n        \n        print(f\"✅ 已生成: {out_name}\")\n        print(f\"   行数: {len(lines)} → HTML大小: {os.path.getsize(out_name)//1024}KB\")\n        print(f\"   💡 双击打开浏览器预览\")\n```\n\n</details>\n\n**⚠️ 可能遇到的坑：**\n\n| 情况 | 原因 | 解决 |\n|------|------|------|\n| 复杂表格未转换 | 极简解析器不支持 | 使用专业工具如 pandoc |\n| 图片未显示 | 本地路径未转绝对路径 | 手动替换为绝对路径或URL |\n| 含HTML标签的MD | 被转义 | 正常行为，安全考虑 |\n| 大文件慢 | 逐行解析 | 10MB+ 文件建议用 pandoc |\n\n---\n\n#### 10.4 历史记录系统 ⭐ v3.7.0 新增\n\n**✅ 开箱即用**（纯 Python 标准库 json，零依赖）\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport json, os, datetime\n\nHISTORY_FILE = os.path.join(os.path.expanduser(\"~\"), \".workbuddy\", \"dgngjx_history.json\")\nos.makedirs(os.path.dirname(HISTORY_FILE), exist_ok=True)\n\ndef _load():\n    if os.path.exists(HISTORY_FILE):\n        with open(HISTORY_FILE, encoding=\"utf-8\") as f:\n            return json.load(f)\n    return []\n\ndef _save(records):\n    with open(HISTORY_FILE, \"w\", encoding=\"utf-8\") as f:\n        json.dump(records[-200:], f, ensure_ascii=False, indent=2)\n\nmode = input(\"操作(记录/查看/搜索/清空):\").strip() or \"查看\"\nif mode == \"记录\":\n    tool = input(\"工具名:\").strip()\n    inp = input(\"输入摘要:\").strip()\n    out = input(\"输出摘要:\").strip()\n    rec = {\"time\": datetime.datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\"),\n           \"tool\": tool, \"input\": inp[:200], \"output\": out[:200]}\n    records = _load()\n    records.append(rec)\n    _save(records)\n    print(f\"✅ 已记录（共 {len(records)} 条）\")\nelif mode == \"查看\":\n    records = _load()\n    if not records:\n        print(\"📭 暂无历史记录\")\n    else:\n        for i, r in enumerate(records[-20:], 1):\n            print(f\"{i}. [{r['time']}] {r['tool']}\")\n            print(f\"   输入: {r.get('input','')[:60]}\")\n            print(f\"   输出: {r.get('output','')[:60]}\")\n        print(f\"\\n共 {len(records)} 条（显示最近 20 条）\")\nelif mode == \"搜索\":\n    kw = input(\"搜索关键词:\").strip().lower()\n    records = _load()\n    matched = [r for r in records if kw in r.get(\"tool\",\"\").lower() or kw in r.get(\"input\",\"\").lower()]\n    print(f\"找到 {len(matched)} 条匹配:\")\n    for r in matched[-10:]:\n        print(f\"  [{r['time']}] {r['tool']} - {r.get('input','')[:50]}\")\nelif mode == \"清空\":\n    confirm = input(\"确认清空？输入 YES:\").strip()\n    if confirm == \"YES\":\n        _save([])\n        print(\"✅ 历史记录已清空\")\n    else:\n        print(\"已取消\")\nelse:\n    print(f\"❌ 不支持 {mode}。可选: 记录/查看/搜索/清空\")\n```\n\n</details>\n\n---\n\n#### 10.5 配置持久化 ⭐ v3.7.0 新增\n\n**✅ 开箱即用**（纯 Python 标准库 json，零依赖）\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport json, os\n\nCONFIG_FILE = os.path.join(os.path.expanduser(\"~\"), \".workbuddy\", \"dgngjx_config.json\")\nDEFAULTS = {\"image_quality\": 80, \"output_format\": \"JPEG\", \"theme\": \"auto\",\n            \"recent_tools\": [], \"auto_save_history\": True}\nos.makedirs(os.path.dirname(CONFIG_FILE), exist_ok=True)\n\ndef _load():\n    if os.path.exists(CONFIG_FILE):\n        with open(CONFIG_FILE, encoding=\"utf-8\") as f:\n            cfg = json.load(f)\n        for k, v in DEFAULTS.items():\n            if k not in cfg:\n                cfg[k] = v\n        return cfg\n    return dict(DEFAULTS)\n\ndef _save(cfg):\n    with open(CONFIG_FILE, \"w\", encoding=\"utf-8\") as f:\n        json.dump(cfg, f, ensure_ascii=False, indent=2)\n\nmode = input(\"操作(查看/设置/重置):\").strip() or \"查看\"\nif mode == \"查看\":\n    cfg = _load()\n    print(\"=== 当前配置 ===\")\n    for k, v in cfg.items():\n        print(f\"  {k}: {v}\")\n    print(f\"\\n配置文件: {CONFIG_FILE}\")\nelif mode == \"设置\":\n    cfg = _load()\n    key = input(f\"设置项({'/'.join(DEFAULTS.keys())}):\").strip()\n    if key not in DEFAULTS:\n        print(f\"❌ 未知设置项。可选: {', '.join(DEFAULTS.keys())}\")\n    else:\n        val = input(f\"当前 {key}={cfg[key]}，新值:\").strip()\n        if val:\n            if isinstance(DEFAULTS[key], int):\n                try: val = int(val)\n                except ValueError: print(\"❌ 需要整数\"); raise SystemExit\n            elif isinstance(DEFAULTS[key], bool):\n                val = val.lower() in (\"true\",\"yes\",\"1\",\"y\")\n            cfg[key] = val\n            _save(cfg)\n            print(f\"✅ {key} = {val}\")\nelif mode == \"重置\":\n    confirm = input(\"确认重置为默认值？输入 YES:\").strip()\n    if confirm == \"YES\":\n        _save(DEFAULTS)\n        print(\"✅ 已重置\")\n    else:\n        print(\"已取消\")\nelse:\n    print(f\"❌ 不支持 {mode}。可选: 查看/设置/重置\")\n```\n\n</details>\n\n---\n\n#### 10.6 周报/月报自动生成 ⭐ v3.8.0 新增\n\n**✅ 开箱即用**（纯 Python 标准库 json/datetime，零依赖）\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport json, os, datetime\n\nHISTORY_FILE = os.path.join(os.path.expanduser(\"~\"), \".workbuddy\", \"dgngjx_history.json\")\n\ndef _load():\n    if os.path.exists(HISTORY_FILE):\n        with open(HISTORY_FILE, encoding=\"utf-8\") as f:\n            return json.load(f)\n    return []\n\nmode = input(\"生成周报(w)/月报(m)，默认w:\").strip() or \"w\"\nrecords = _load()\nif not records:\n    print(\"📭 暂无历史记录。先使用工具自动记录工作。\")\nelse:\n    today = datetime.date.today()\n    if mode == \"m\":\n        start = today.replace(day=1)\n        period_label = f\"{today.year}年{today.month}月\"\n        title = f\"# 📋 {period_label} 月度工作报告\"\n    else:\n        start = today - datetime.timedelta(days=today.weekday())\n        period_label = f\"{start.strftime('%m/%d')} - {today.strftime('%m/%d')}\"\n        title = f\"# 📋 本周工作报告（{period_label}）\"\n    \n    filtered = []\n    for r in records:\n        try:\n            t = datetime.datetime.strptime(r.get(\"time\",\"\"), \"%Y-%m-%d %H:%M:%S\").date()\n            if t >= start and t <= today:\n                filtered.append(r)\n        except ValueError:\n            continue\n    \n    print(title)\n    print(f\"\\n> 生成时间：{today:%Y-%m-%d} | 数据源：{len(filtered)} 条记录\\n\")\n    \n    if not filtered:\n        print(\"本周期内暂无记录。\")\n    else:\n        tool_counts = {}\n        for r in filtered:\n            tool = r.get(\"tool\",\"未知\")\n            tool_counts[tool] = tool_counts.get(tool, 0) + 1\n        print(\"## 📊 工具使用统计\\n\")\n        print(\"| 工具 | 使用次数 |\")\n        print(\"|------|---------:|\")\n        for tool, cnt in sorted(tool_counts.items(), key=lambda x: -x[1]):\n            print(f\"| {tool} | {cnt} |\")\n        print(\"\\n## 📝 工作记录明细\\n\")\n        for i, r in enumerate(filtered, 1):\n            inp = r.get(\"input\",\"\")[:80]\n            out = r.get(\"output\",\"\")[:80]\n            print(f\"**{i}. [{r['time']}] {r['tool']}**\")\n            print(f\"   - 输入: {inp}\")\n            print(f\"   - 输出: {out}\")\n            print()\n        print(\"---\\n*本报告由 dgngjx-skill 历史记录系统自动生成*\")\n```\n\n</details>\n\n---\n\n#### 🔗 模块间管道联动 ⭐ v3.7.0 新增架构特性\n\n**✅ 开箱即用**（纯标准库，零依赖）\n\n<details>\n<summary>📋 管道使用示例</summary>\n\n```python\nimport csv, json, io\n\ndef csv_to_markdown_table(csv_path):\n    \"\"\"CSV 文件 → Markdown 表格\"\"\"\n    with open(csv_path, encoding=\"utf-8-sig\", newline=\"\") as f:\n        reader = csv.reader(f)\n        rows = list(reader)\n    if len(rows) < 2:\n        return \"CSV 至少需要 1 行表头 + 1 行数据\"\n    header = rows[0]\n    lines = []\n    lines.append(\"| \" + \" | \".join(header) + \" |\")\n    lines.append(\"| \" + \" | \".join([\"---\"] * len(header)) + \" |\")\n    for row in rows[1:]:\n        while len(row) < len(header):\n            row.append(\"\")\n        lines.append(\"| \" + \" | \".join(row[:len(header)]) + \" |\")\n    return \"\\n\".join(lines)\n\ndef csv_to_json(csv_path):\n    \"\"\"CSV 文件 → JSON 数组\"\"\"\n    with open(csv_path, encoding=\"utf-8-sig\", newline=\"\") as f:\n        reader = csv.DictReader(f)\n        return json.dumps(list(reader), ensure_ascii=False, indent=2)\n\npath = input(\"CSV文件路径:\").strip().strip('\"').strip(\"'\")\nfmt = input(\"输出格式(json/md):\").strip() or \"md\"\nif fmt == \"json\":\n    print(csv_to_json(path))\nelse:\n    print(csv_to_markdown_table(path))\n```\n\n</details>\n\nFile v4.1.0:references/modules/text.md\n\n# 📝 模块 2：文本工具\n\n---\n\n#### 2.1 文本统计\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nt = input(\"输入文本:\")\nif not t or not t.strip():\n    print(\"❌ 没有检测到输入。请程序运行时输入一段文字\")\nelse:\n    total = len(t)\n    cn = sum(1 for c in t if '一' <= c <= '鿿')\n    en = len(t.split())\n    lines = t.split(chr(10))\n    print(f\"总字符: {total} | 中文: {cn} | 英文词: {en}\")\n    print(f\"非空行: {len([l for l in lines if l.strip()])}\")\n    print(f\"去重后: {len(list(dict.fromkeys(lines)))}行 (原{len(lines)}行)\")\n```\n\n</details>\n\n---\n\n#### 2.2 编码转换 ⭐ v3.9.0 拆分为 encode + hash\n\n> v3.9.0 将编码转换拆分为两个独立工具：`text.encode`（Base64/URL/Unicode/进制）和 `text.hash`（MD5/SHA1/SHA256）。\n\n<details>\n<summary>📋 text.encode（Base64/URL/Unicode/进制）</summary>\n\n```python\nimport base64, urllib.parse\ntry:\n    t = input(\"文本:\") or \"Hello\"\n    m = input(\"方法(Base64/URL/Unicode/进制):\").strip() or \"Base64\"\n    op = input(\"编码/解码:\").strip() or \"编码\"\n    if m == \"Base64\":\n        print(base64.b64encode(t.encode()).decode() if op == \"编码\" else base64.b64decode(t.encode()).decode())\n    elif m == \"URL\":\n        print(urllib.parse.quote(t) if op == \"编码\" else urllib.parse.unquote(t))\n    elif m == \"Unicode\":\n        print(\" \".join(f\"\\\\u{ord(c):04x}\" for c in t))\n    elif m == \"进制\":\n        base_from = input(\"原进制(2/8/10/16):\").strip() or \"10\"\n        try:\n            n = int(t, int(base_from))\n            print(f\"✅ {t} (原{base_from}进制) =\")\n            print(f\"   二进制: {bin(n)[2:]}\")\n            print(f\"   八进制: {oct(n)[2:]}\")\n            print(f\"   十进制: {n}\")\n            print(f\"   十六进制: {hex(n)[2:].upper()}\")\n        except ValueError:\n            print(f\"❌ '{t}' 不是有效的{base_from}进制数字\")\n    else:\n        print(f\"❌ 不支持 {m}。可选: Base64 / URL / Unicode / 进制\")\nexcept Exception as e:\n    print(f\"❌ 处理失败: {e}\")\n```\n\n</details>\n\n<details>\n<summary>📋 text.hash（MD5/SHA1/SHA256）</summary>\n\n```python\nimport hashlib\ntry:\n    t = input(\"文本:\") or \"Hello\"\n    algos = {\"md5\": hashlib.md5(), \"sha1\": hashlib.sha1(), \"sha256\": hashlib.sha256()}\n    for name, h in algos.items():\n        h.update(t.encode())\n        print(f\"  {name.upper():7}: {h.hexdigest()}\")\nexcept Exception as e:\n    print(f\"❌ {e}\")\n```\n\n</details>\n\n---\n\n#### 2.3 词频统计\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nfrom collections import Counter\nimport re\n\nSTOPWORDS = {'的','了','和','是','在','我','有','也','不','就','都','而','及','与','着','或','一个','没有',\n    '我们','你们','他们','她们','但是','然而','因为','所以','如果','虽然','不过','而且','或者','还是',\n    '既','又','并','等','把','被','让','往','从','对','于','以','为','比','此','那','这','她','他','它',\n    '们','些','什么','哪','怎么','多','很','最','更','太','非常','每','当','起','已','将','能','会','应',\n    '可','得','过','给','来','去','说','看','知道','做','想','问','请','好','再','还','只','如','真的',\n    '自己','人','事','时','地','今天','现在','一些','这样','那样','怎么样','可以','这个','那个','不是',\n    '可能','已经','之后','之前','然后','不过','比较','其实','其他','其中','所有','虽然','由于','因此'}\n\ndef _smart_segment(text):\n    try:\n        import jieba\n        return list(jieba.lcut(text, cut_all=False))\n    except ImportError:\n        return re.findall(r'[一-鿿\\w]+', text)\n\ntry:\n    t = input(\"输入文本:\")\n    if not t.strip():\n        print(\"❌ 没有检测到输入\")\n    else:\n        raw = _smart_segment(t)\n        words = []\n        for w in raw:\n            w = w.strip()\n            if len(w) <= 1: continue\n            if re.match(r'^[a-zA-Z]+$', w): w = w.lower()\n            if w in STOPWORDS: continue\n            words.append(w)\n        if not words:\n            print(\"❌ 没有找到有效词\")\n        else:\n            for i,(word,cnt) in enumerate(Counter(words).most_common(15),1):\n                print(f\"  {i:2d}. {word}: {cnt} {'█'*min(cnt*2,30)}\")\n            unique = len(set(words))\n            print(f\"\\n📊 共 {len(words)} 个有效词，{unique} 个不重复词\")\n            d = unique/len(words)*100\n            print(f\"   词汇丰富度: {d:.1f}%\", end=\"\")\n            print(\"（用词多样）\" if d>80 else \"（正常范围）\" if d>50 else \"（同词重复较多）\")\n            try:\n                import jieba; print(\"   ✅ 已使用 jieba 精准分词\")\n            except: print(\"   💡 安装 jieba 可获得更精准分词: pip install jieba\")\nexcept Exception as e:\n    print(f\"❌ {e}\")\n```\n\n</details>\n\n---\n\n#### 2.4 文本差异对比 ⭐ v3.7.0 新增\n\n**✅ 开箱即用**（纯 Python 标准库 difflib，零依赖）\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport difflib, os\n\ndef _read(src):\n    \"\"\"支持文件路径或直接文本\"\"\"\n    if os.path.isfile(src):\n        with open(src, encoding=\"utf-8\") as f:\n            return f.read().splitlines()\n    return src.split(\"\\n\")\n\nsrc_a = input(\"文本A(内容或文件路径):\").strip()\nsrc_b = input(\"文本B(内容或文件路径):\").strip()\nif not src_a or not src_b:\n    print(\"❌ 文本A和B均不能为空\")\nelse:\n    lines_a = _read(src_a)\n    lines_b = _read(src_b)\n    diff = list(difflib.unified_diff(lines_a, lines_b, lineterm=\"\", fromfile=\"A\", tofile=\"B\"))\n    if not diff:\n        print(\"✅ 两段文本完全相同，无差异\")\n    else:\n        for line in diff:\n            if line.startswith(\"-\") and not line.startswith(\"---\"):\n                print(f\"🔴 {line}\")\n            elif line.startswith(\"+\") and not line.startswith(\"+++\"):\n                print(f\"🟢 {line}\")\n            elif line.startswith(\"@@\"):\n                print(f\"🔵 {line}\")\n            else:\n                print(f\"   {line}\")\n        adds = sum(1 for l in diff if l.startswith(\"+\") and not l.startswith(\"+++\"))\n        dels = sum(1 for l in diff if l.startswith(\"-\") and not l.startswith(\"---\"))\n        print(f\"\\n📊 新增 {adds} 行，删除 {dels} 行\")\n```\n\n</details>\n\nFile v4.1.0:references/modules/util.md\n\n# 🛠️ 模块 9：实用小工具\n\n---\n\n#### 9.1 二维码生成 / 9.2 密码生成 / 9.3 正则测试\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 二维码生成</summary>\n\n```python\nimport urllib.parse\ntext = input(\"内容/URL:\") or \"https://www.example.com\"\nurl = f\"https://api.qrserver.com/v1/create-qr-code/?size=300x300&data={urllib.parse.quote(text,safe='')}\"\nprint(f\"📱 复制到浏览器下载: {url}\")\n```\n\n</details>\n\n<details>\n<summary>📋 密码生成器</summary>\n\n```python\nimport random, string, math\nlength = int(input(\"密码长度(16):\") or 16)\nchars = string.ascii_letters + string.digits + \"!@#$%^&*\"\npwd = ''.join(random.choice(chars) for _ in range(length))\nentropy = length * math.log2(len(chars))\nprint(f\"🔐 {pwd}\")\nprint(f\"强度: {'★★★★★' if entropy>=80 else '★★★★☆' if entropy>=60 else '★★★☆☆'} ({entropy:.1f} bits)\")\n```\n\n</details>\n\n<details>\n<summary>📋 正则测试器</summary>\n\n```python\nimport re\np = input(\"正则:\") or r\"\\b\\w+@\\w+\\.\\w+\\b\"\nt = input(\"文本:\") or \"test@example.com\"\ntry:\n    m = re.findall(p, t)\n    print(f\"✅ 找到 {len(m)} 个: {m}\" if m else \"❌ 无匹配\")\nexcept re.error as e: print(f\"❌ 语法错误: {e}\")\n```\n\n</details>\n\n---\n\n#### 9.4 文件哈希校验 ⭐ v3.6.0 新增\n\n**✅ 开箱即用**（纯 Python 标准库 hashlib，零依赖）\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport hashlib, os\n\npath = input(\"文件路径:\").strip().strip('\"').strip(\"'\")\nif not path or not os.path.isfile(path):\n    print(f\"❌ 文件不存在: {path}\")\nelse:\n    algos = {\"md5\": hashlib.md5(), \"sha1\": hashlib.sha1(), \"sha256\": hashlib.sha256()}\n    size = os.path.getsize(path)\n    chunk = 8 * 1024 * 1024\n    with open(path, \"rb\") as f:\n        while True:\n            data = f.read(chunk)\n            if not data:\n                break\n            for h in algos.values():\n                h.update(data)\n    print(f\"📄 文件: {os.path.basename(path)}  ({size/1024:.1f} KB)\")\n    for name, h in algos.items():\n        print(f\"  {name.upper():7}: {h.hexdigest()}\")\n    expect = input(\"粘贴期望哈希值比对(可留空回车跳过):\").strip().lower()\n    if expect:\n        matched = [n for n, h in algos.items() if h.hexdigest().lower() == expect]\n        print(f\"✅ 校验通过（{matched[0].upper()}）\" if matched else \"❌ 校验失败：哈希值不匹配，文件可能已损坏或被篡改\")\n```\n\n</details>\n\n---\n\n#### 9.5 UUID 生成器 ⭐ v3.6.0 新增\n\n**✅ 开箱即用**（纯 Python 标准库 uuid，零依赖）\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport uuid\n\nmode = input(\"类型(uuid4随机/uuid1时间/nodash无横线):\").strip() or \"uuid4\"\ntry:\n    count = int(input(\"生成数量(默认1):\") or 1)\nexcept ValueError:\n    count = 1\ncount = max(1, min(count, 100))\n\nfor _ in range(count):\n    if mode == \"uuid1\":\n        u = str(uuid.uuid1())\n    elif mode == \"nodash\":\n        u = uuid.uuid4().hex\n    else:\n        u = str(uuid.uuid4())\n    print(u)\nprint(f\"✅ 已生成 {count} 个 UUID（{mode}）\")\n```\n\n</details>\n\n---\n\n#### 9.6 时间戳转换 ⭐ v3.6.0 新增\n\n**✅ 开箱即用**（纯 Python 标准库 datetime，零依赖）\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport datetime, time\n\ns = input(\"输入时间戳 或 日期(YYYY-MM-DD HH:MM:SS)，留空取当前:\").strip()\nif not s:\n    now = time.time()\n    print(f\"⏰ 当前时间: {datetime.datetime.now():%Y-%m-%d %H:%M:%S}\")\n    print(f\"   秒级时间戳: {int(now)}\")\n    print(f\"   毫秒级时间戳: {int(now*1000)}\")\nelif s.replace(\".\", \"\").isdigit():\n    ts = float(s)\n    if ts > 1e12:\n        ts /= 1000\n    try:\n        dt = datetime.datetime.fromtimestamp(ts)\n        print(f\"✅ 时间戳 {s} →\")\n        print(f\"   本地时间: {dt:%Y-%m-%d %H:%M:%S}\")\n        print(f\"   星期: {'一二三四五六日'[dt.weekday()]}\")\n    except (ValueError, OSError):\n        print(\"❌ 时间戳超出有效范围\")\nelse:\n    parsed = None\n    for fmt in (\"%Y-%m-%d %H:%M:%S\", \"%Y-%m-%d %H:%M\", \"%Y-%m-%d\", \"%Y/%m/%d %H:%M:%S\", \"%Y/%m/%d\"):\n        try:\n            parsed = datetime.datetime.strptime(s, fmt)\n            break\n        except ValueError:\n            continue\n    if parsed:\n        print(f\"✅ 日期 {s} →\")\n        print(f\"   秒级时间戳: {int(parsed.timestamp())}\")\n        print(f\"   毫秒级时间戳: {int(parsed.timestamp()*1000)}\")\n    else:\n        print(\"❌ 无法识别，请用 时间戳 或 YYYY-MM-DD HH:MM:SS 格式\")\n```\n\n</details>\n\n---\n\n#### 9.7 IP 工具 ⭐ v3.6.0 新增\n\n**✅ 开箱即用**（纯 Python 标准库 ipaddress/socket，零依赖）\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport ipaddress, socket\n\ns = input(\"输入IP或网段(如192.168.1.0/24)，留空查本机:\").strip()\nif not s:\n    try:\n        hostname = socket.gethostname()\n        local_ip = socket.gethostbyname(hostname)\n        print(f\"🖥️ 主机名: {hostname}\")\n        print(f\"   本机IP: {local_ip}\")\n    except socket.error as e:\n        print(f\"❌ 获取本机IP失败: {e}\")\nelse:\n    try:\n        if \"/\" in s:\n            net = ipaddress.ip_network(s, strict=False)\n            print(f\"✅ 网段: {net}\")\n            print(f\"   网络地址: {net.network_address}\")\n            print(f\"   广播地址: {net.broadcast_address}\")\n            print(f\"   子网掩码: {net.netmask}\")\n            print(f\"   可用主机数: {net.num_addresses}\")\n        else:\n            ip = ipaddress.ip_address(s)\n            print(f\"✅ IP: {ip}  (IPv{ip.version})\")\n            print(f\"   私有地址: {'是' if ip.is_private else '否'}\")\n            print(f\"   回环地址: {'是' if ip.is_loopback else '否'}\")\n            print(f\"   多播地址: {'是' if ip.is_multicast else '否'}\")\n    except ValueError as e:\n        print(f\"❌ 无效的IP或网段: {e}\")\n```\n\n</details>\n\n---\n\n#### 9.8 CSV 查看器 ⭐ v3.7.0 新增\n\n**✅ 开箱即用**（纯 Python 标准库 csv，零依赖）\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport csv, os\n\npath = input(\"CSV文件路径:\").strip().strip('\"').strip(\"'\")\nif not path or not os.path.isfile(path):\n    print(f\"❌ 文件不存在: {path}\")\nelse:\n    enc = \"utf-8\"\n    for try_enc in (\"utf-8-sig\", \"utf-8\", \"gbk\", \"gb18030\"):\n        try:\n            with open(path, encoding=try_enc) as f:\n                f.read(4096)\n            enc = try_enc; break\n        except (UnicodeDecodeError, UnicodeError):\n            continue\n    with open(path, encoding=enc) as f:\n        head = f.read(2048)\n    try:\n        dialect = csv.Sniffer().sniff(head, delimiters=\",\\t;|\")\n    except csv.Error:\n        dialect = csv.excel\n    with open(path, encoding=enc, newline=\"\") as f:\n        reader = csv.reader(f, dialect)\n        rows = list(reader)\n    if not rows:\n        print(\"⚠️ 文件为空\")\n    else:\n        max_cols = max(len(r) for r in rows)\n        widths = [0] * max_cols\n        for r in rows:\n            for i, c in enumerate(r):\n                widths[i] = max(widths[i], len(c))\n        widths = [min(w, 30) for w in widths]\n        total = len(rows)\n        page = 20\n        for start in range(0, total, page):\n            print(f\"\\n--- 第 {start+1}-{min(start+page, total)} 行 / 共 {total} 行 ---\")\n            for r in rows[start:start+page]:\n                line = \" | \".join((c if len(c)<=w else c[:w-1]+\"…\").ljust(w) for c, w in zip(r, widths))\n                print(line)\n            if start + page < total:\n                more = input(\"按回车继续 / q 退出: \").strip().lower()\n                if more == \"q\":\n                    break\n```\n\n</details>\n\n---\n\n#### 9.9 密码强度检测 ⭐ v3.7.0 新增\n\n**✅ 开箱即用**（纯 Python 标准库，零依赖）\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport re, hashlib\n\nCOMMON_PASSWORDS = {\n    \"123456\",\"password\",\"12345678\",\"qwerty\",\"123456789\",\"letmein\",\"1234567\",\"football\",\"iloveyou\",\n    \"admin\",\"welcome\",\"monkey\",\"login\",\"abc123\",\"111111\",\"123123\",\"password123\",\"1234\",\"000000\",\n    \"1qaz2wsx\",\"qwerty123\",\"123qwe\",\"password1\",\"sunshine\",\"princess\",\"dragon\",\"flower\",\"shadow\",\n    \"superman\",\"michael\",\"master\",\"photoshop\",\"112233\",\"654321\",\"trustno1\",\"batman\",\"passw0rd\"\n}\n\npwd = input(\"待检测密码:\").strip()\nif not pwd:\n    print(\"❌ 请输入密码\")\nelse:\n    score = 0\n    tips = []\n    if len(pwd) >= 8: score += 1\n    else: tips.append(\"长度不足8位\")\n    if len(pwd) >= 12: score += 1\n    if len(pwd) >= 16: score += 1\n    has_lower = bool(re.search(r\"[a-z]\", pwd))\n    has_upper = bool(re.search(r\"[A-Z]\", pwd))\n    has_digit = bool(re.search(r\"\\d\", pwd))\n    has_symbol = bool(re.search(r\"[^a-zA-Z\\d]\", pwd))\n    diversity = sum([has_lower, has_upper, has_digit, has_symbol])\n    score += diversity\n    if not has_lower: tips.append(\"缺少小写字母\")\n    if not has_upper: tips.append(\"缺少大写字母\")\n    if not has_digit: tips.append(\"缺少数字\")\n    if not has_symbol: tips.append(\"缺少特殊符号\")\n    if re.search(r\"(.)\\1{2,}\", pwd):\n        score -= 2; tips.append(\"含3+连续重复字符(如aaa)\")\n    if re.search(r\"(012|123|234|345|456|567|678|789|890)\", pwd):\n        score -= 2; tips.append(\"含连续数字序列(如123)\")\n    if re.search(r\"(abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz)\", pwd.lower()):\n        score -= 2; tips.append(\"含连续字母序列(如abc)\")\n    if pwd.lower() in COMMON_PASSWORDS:\n        score = 0; tips.append(\"⚠️ 在常见弱密码字典中！\")\n    score = max(0, min(score, 10))\n    stars = \"★\" * (score // 2) + \"☆\" * (5 - score // 2)\n    labels = {0:\"极弱\",1:\"极弱\",2:\"极弱\",3:\"弱\",4:\"弱\",5:\"中等\",6:\"中等\",7:\"强\",8:\"强\",9:\"很强\",10:\"极强\"}\n    print(f\"长度: {len(pwd)} | 字符类: {diversity}/4\")\n    print(f\"评分: {score}/10  {stars}  [{labels[score]}]\")\n    if tips:\n        print(\"建议:\")\n        for t in tips[:5]:\n            print(f\"  • {t}\")\n    if score >= 8:\n        print(\"✅ 密码强度优秀\")\n    elif score >= 5:\n        print(\"💡 尚可，但有提升空间\")\n    else:\n        print(\"❌ 建议立即修改\")\n```\n\n</details>\n\n---\n\n#### 9.10 颜色值转换 ⭐ v3.7.0 新增\n\n**✅ 开箱即用**（纯数学运算，零依赖）\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport re\n\ns = input(\"输入颜色值(如#FF0000 / rgb(255,0,0) / 255,0,0):\").strip()\nif not s:\n    print(\"❌ 请输入颜色值\")\nelse:\n    r = g = b = None\n    s_clean = s.strip()\n    hex_m = re.match(r\"^#?([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$\", s_clean)\n    if hex_m:\n        hx = hex_m.group(1)\n        if len(hx) == 3:\n            r, g, b = int(hx[0]*2,16), int(hx[1]*2,16), int(hx[2]*2,16)\n        else:\n            r, g, b = int(hx[0:2],16), int(hx[2:4],16), int(hx[4:6],16)\n    rgb_m = re.match(r\"rgb\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)\", s_clean, re.I)\n    if rgb_m and r is None:\n        r, g, b = int(rgb_m.group(1)), int(rgb_m.group(2)), int(rgb_m.group(3))\n    num_m = re.match(r\"^(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})$\", s_clean)\n    if num_m and r is None:\n        r, g, b = int(num_m.group(1)), int(num_m.group(2)), int(num_m.group(3))\n    if r is None or not all(0 <= v <= 255 for v in (r, g, b)):\n        print(\"❌ 无法识别。支持格式: #FF0000 / #F00 / rgb(255,0,0) / 255,0,0\")\n    else:\n        hex_out = f\"#{r:02X}{g:02X}{b:02X}\"\n        rn, gn, bn = r/255, g/255, b/255\n        mx, mn = max(rn, gn, bn), min(rn, gn, bn)\n        l = (mx + mn) / 2\n        if mx == mn:\n            h = s = 0\n        else:\n            d = mx - mn\n            s = d / (2 - mx - mn) if l > 0.5 else d / (mx + mn)\n            if mx == rn:\n                h = (gn - bn) / d + (6 if gn < bn else 0)\n            elif mx == gn:\n                h = (bn - rn) / d + 2\n            else:\n                h = (rn - gn) / d + 4\n            h /= 6\n        h_deg = round(h * 360)\n        s_pct = round(s * 100)\n        l_pct = round(l * 100)\n        print(f\"✅ {s_clean} →\")\n        print(f\"   HEX : {hex_out}  (小写: {hex_out.lower()})\")\n        print(f\"   RGB : rgb({r}, {g}, {b})\")\n        print(f\"   HSL : hsl({h_deg}, {s_pct}%, {l_pct}%)\")\n```\n\n</details>\n\n---\n\n#### 9.11 随机数生成器 ⭐ v3.7.0 新增\n\n**✅ 开箱即用**（纯 Python 标准库 random，零依赖）\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport random, string\n\nmode = input(\"模式(整数/小数/硬币/骰子/打乱/抽样/字符串):\").strip() or \"整数\"\nif mode == \"整数\":\n    try:\n        a = int(input(\"最小值(默认1):\") or 1)\n        b = int(input(\"最大值(默认100):\") or 100)\n        n = int(input(\"数量(默认1):\") or 1)\n        if a > b: a, b = b, a\n        res = [random.randint(a, b) for _ in range(max(1, min(n, 100)))]\n        print(f\"✅ {res}\")\n    except ValueError: print(\"❌ 请输入整数\")\nelif mode == \"小数\":\n    try:\n        a = float(input(\"最小值(默认0):\") or 0)\n        b = float(input(\"最大值(默认1):\") or 1)\n        if a > b: a, b = b, a\n        res = [round(random.uniform(a, b), 6) for _ in range(3)]\n        print(f\"✅ {res}\")\n    except ValueError: print(\"❌ 请输入数字\")\nelif mode == \"硬币\":\n    n = int(input(\"次数(默认1):\") or 1)\n    res = [\"正面\" if random.random() < 0.5 else \"反面\" for _ in range(max(1, min(n, 100)))]\n    h = res.count(\"正面\")\n    print(f\"✅ {res}\")\n    print(f\"   正面{h}次 / 反面{len(res)-h}次\")\nelif mode == \"骰子\":\n    n = int(input(\"骰子数(默认1):\") or 1)\n    res = [random.randint(1, 6) for _ in range(max(1, min(n, 20)))]\n    print(f\"✅ {res}  合计: {sum(res)}\")\nelif mode == \"打乱\":\n    items = input(\"输入列表(逗号分隔):\").split(\",\")\n    items = [x.strip() for x in items if x.strip()]\n    random.shuffle(items)\n    print(f\"✅ {items}\")\nelif mode == \"抽样\":\n    items = input(\"列表(逗号分隔):\").split(\",\")\n    items = [x.strip() for x in items if x.strip()]\n    try:\n        k = int(input(f\"抽取数(1-{len(items)}):\") or 1)\n        k = max(1, min(k, len(items)))\n    except ValueError: k = 1\n    print(f\"✅ {random.sample(items, k)}\")\nelif mode == \"字符串\":\n    length = int(input(\"长度(默认16):\") or 16)\n    chars = \"\"\n    if input(\"字母(y/n 默认y):\").strip().lower() != \"n\": chars += string.ascii_letters\n    if input(\"数字(y/n 默认y):\").strip().lower() != \"n\": chars += string.digits\n    if input(\"符号(y/n 默认n):\").strip().lower() == \"y\": chars += \"!@#$%^&*\"\n    if not chars: chars = string.ascii_letters + string.digits\n    print(f\"✅ {''.join(random.choice(chars) for _ in range(max(1, min(length, 256))))}\")\nelse:\n    print(f\"❌ 不支持 {mode}。可选: 整数/小数/硬币/骰子/打乱/抽样/字符串\")\n```\n\n</details>\n\nArchive v4.0.0: 4 files, 48566 bytes\n\nFiles: references/tax_2026.yaml (8588b), skill-card.md (2745b), SKILL.md (129699b), _meta.json (131b)\n\nFile v4.0.0:SKILL.md\n\n---\nname: dgngjx-skill\nslug: dgngjx-skill\ndisplayName: \"多功能工具箱 v4.0\"\ndescription: \"多功能免费工具箱 - 图片处理、PDF转换、数据换算、文本工具、开发工具、视频工具、教育、生活娱乐、实用小工具、系统工具、AI办公。11大模块49个工具。v4.0 数据准确性：个税五险全国化（tax_2026.yaml 查表累进引擎）+ 汇率缓存时效治理（72h TTL + 陈旧标注）。\"\ndescription_zh: \"多功能免费工具箱 - 11大模块49个工具。v4.0 数据准确性：个税五险全国化（13城市查表累进引擎）+ 汇率缓存时效治理（72h TTL + 陈旧标注）。\"\nversion: 4.0.0\ncategory: office-efficiency\nplatforms:\n  - windows\n  - macos\n  - linux\ntags:\n  - toolbox\n  - pdf\n  - image\n  - video\n  - developer\n  - converter\n  - calculator\n  - text\n  - reliable\n  - qrcode\n  - password\n  - regex\n  - system\n  - hardware\n  - monitor\n  - hash\n  - uuid\n  - timestamp\n  - ip\n  - csv\n  - color\n  - password-strength\n  - random\n  - diff\n  - bmi\n  - pomodoro\n  - exchange-rate\n  - history\n  - config\n  - meeting\n  - minutes\n  - weekly-report\n  - cli\n  - registry\n  - argparse\n  - data-accuracy\n  - tax-calculation\n  - social-insurance\n  - exchange-rate-ttl\nrequires_api_key: false\n---\n\n# 多功能工具箱 dgngjx-skill v4.0.0\n\n## 🚀 dgngjx CLI 统一入口 ⭐ v3.9.0 新增\n\n**✅ 开箱即用**（纯 Python 标准库 argparse，零依赖）\n\n> v3.9.0 起提供统一命令行入口 `dgngjx`，49 个工具均可通过 `模块.工具` 子命令直接调用，无需进入交互模式。无参数启动则进入交互向导，保留旧体验。\n\n<details>\n<summary>📋 CLI 使用方式</summary>\n\n```bash\n# 直接调用工具（参数化，非交互）\ndgngjx calc.mortgage --amount 1000000 --years 30 --rate 4.2\ndgngjx text.encode --method base64 --text \"Hello World\"\ndgngjx img.compress --path ./photos/ --quality 80 --format JPEG\ndgngjx hash.md5 --file document.pdf\ndgngjx exchange --amount 100 --from USD --to CNY\n\n# 机器可解析输出（供 CI/计划任务使用）\ndgngjx sys.monitor --json\n\n# 无人值守模式（跳过非危险确认点）\ndgngjx file.rename --dir ./photos/ --prefix \"vacation_\" --yes\n# 或设置环境变量\nexport DGNGJX_ASSUME_YES=1\ndgngjx img.convert --path ./photos/ --format PNG\n\n# 无参数启动 → 进入交互向导（兼容旧体验）\ndgngjx\n```\n\n</details>\n\n<details>\n<summary>📋 CLI 入口代码（dgngjx 命令）</summary>\n\n```python\n#!/usr/bin/env python3\n\"\"\"dgngjx CLI - 多功能工具箱统一命令行入口\"\"\"\nimport argparse, json, os, sys\n\ndef load_registry():\n    \"\"\"加载工具注册表\"\"\"\n    reg_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), \"registry.json\")\n    with open(reg_path, encoding=\"utf-8\") as f:\n        return json.load(f)\n\ndef build_tool_index(registry):\n    \"\"\"将 modules[].tools 嵌套结构扁平化为 tool_id → info 索引\"\"\"\n    index = {}\n    for mod in registry.get(\"modules\", []):\n        for tool_id, tool_info in mod.get(\"tools\", {}).items():\n            tool_info[\"module_id\"] = mod[\"id\"]\n            tool_info[\"module_name\"] = mod[\"name\"]\n            index[tool_id] = tool_info\n    return index\n\ndef main():\n    parser = argparse.ArgumentParser(prog=\"dgngjx\", description=\"多功能工具箱 dgngjx-skill\")\n    parser.add_argument(\"tool\", nargs=\"?\", help=\"工具路径，如 calc.mortgage / text.encode / img.compress\")\n    parser.add_argument(\"--json\", action=\"store_true\", help=\"输出机器可解析 JSON\")\n    parser.add_argument(\"--yes\", \"-y\", action=\"store_true\", help=\"跳过非危险确认点\")\n    parser.add_argument(\"--help-tools\", action=\"store_true\", help=\"列出所有可用工具\")\n    \n    # 解析已知参数，剩余参数传递给工具\n    args, remaining = parser.parse_known_args()\n    \n    # 环境变量支持\n    if os.environ.get(\"DGNGJX_ASSUME_YES\"):\n        args.yes = True\n    \n    registry = load_registry()\n    tool_index = build_tool_index(registry)\n    \n    if args.help_tools or not args.tool:\n        # 列出所有工具或进入交互向导\n        if args.tool:\n            # 显示特定工具帮助\n            tool_info = tool_index.get(args.tool)\n            if tool_info:\n                print(f\"工具: {args.tool}\")\n                print(f\"描述: {tool_info.get('description','')}\")\n                print(f\"参数: {json.dumps(tool_info.get('params',{}), ensure_ascii=False, indent=2)}\")\n            else:\n                print(f\"❌ 未找到工具: {args.tool}\")\n        else:\n            # 交互向导模式\n            print(\"🔧 dgngjx-skill 交互向导\")\n            print(\"=\" * 40)\n            for mod in registry.get(\"modules\", []):\n                print(f\"\\n{mod['name']}:\")\n                for tool_id, tool_info in mod.get(\"tools\", {}).items():\n                    print(f\"  {tool_id}: {tool_info.get('description','')}\")\n        return\n    \n    # 查找工具\n    tool_info = tool_index.get(args.tool)\n    if not tool_info:\n        print(f\"❌ 未找到工具: {args.tool}\")\n        print(f\"可用工具: {', '.join(list(tool_index.keys())[:10])}...\")\n        sys.exit(1)\n    \n    # 构建工具参数\n    tool_args = {}\n    i = 0\n    while i < len(remaining):\n        if remaining[i].startswith(\"--\"):\n            key = remaining[i][2:].replace(\"-\",\"_\")\n            if i + 1 < len(remaining) and not remaining[i+1].startswith(\"--\"):\n                tool_args[key] = remaining[i+1]\n                i += 2\n            else:\n                tool_args[key] = True\n                i += 1\n        else:\n            i += 1\n    \n    # 注入全局标志\n    tool_args[\"_json\"] = args.json\n    tool_args[\"_yes\"] = args.yes\n    \n    # 执行工具（延迟加载）\n    module = tool_info.get(\"module_id\")\n    print(f\"▶ 运行: {args.tool} (模块 {module})\")\n    # 实际工具执行由 SKILL.md 中的代码块完成\n    # 此处仅做路由，AI 根据 tool_id 执行对应代码块\n\nif __name__ == \"__main__\":\n    main()\n```\n\n</details>\n\n> **无人值守支持**：设置环境变量 `DGNGJX_ASSUME_YES=1` 或传递 `--yes` 参数，可跳过所有非危险确认点。危险操作（删除/覆盖）仍强制交互确认。`--json` 输出机器可解析 JSON，供计划任务与 CI 场景使用。\n\n---\n\n## 30 秒速查表\n\n| 我想做 | 直接说 |\n|--------|--------|\n| 算房贷 / 五险一金 | `\"算房贷\"` `\"算五险一金\"` |\n| 日期 / 单位换算 | `\"日期计算\"` `\"公里转英里\"` `\"MB转GB\"` `\"100美元换人民币\"` |\n| 统计字数 / 编码 | `\"字数统计\"` `\"Base64编码\"` `\"算MD5\"` `\"进制转换\"` |\n| 词频分析 / 差异 | `\"词频统计\"` `\"对比两段文本\"` |\n| 词频分析 | `\"词频统计\"` |\n| JSON / HTTP / Token | `\"JSON格式化\"` `\"测试接口\"` |\n| 查知识 / 下壁纸 | `\"查勾股定理\"` `\"下壁纸\"` |\n| 压缩 / 修复图片 | `\"帮我压缩 D:\\photo.jpg\"` `\"修复老照片\"` |\n| 合并 / 拆分 PDF | `\"合并这几个PDF\"` `\"拆分第5-10页\"` |\n| 视频格式转换 | `\"MP4转GIF\"` |\n| 视频编辑 | `\"给视频加水印\"` `\"提取视频帧\"` |\n| 二维码 / 密码 / 正则 | `\"生成二维码\"` `\"生成随机密码\"` `\"测试正则 \\d+\"` `\"检测密码强度\"` |\n| 文件哈希 / UUID | `\"校验文件哈希\"` `\"生成UUID\"` |\n| 时间戳 / IP工具 | `\"时间戳转换\"` `\"查本机IP\"` `\"算子网\"` |\n| CSV / 颜色 / 随机数 | `\"看CSV文件\"` `\"#FF0000转HSL\"` `\"随机数\"` |\n| BMI / 番茄钟 | `\"算BMI\"` `\"开始番茄钟\"` |\n| 历史 / 配置 / 周报 | `\"查看历史记录\"` `\"修改配置\"` `\"生成本周周报\"` |\n| 会议纪要 | `\"生成会议纪要\"` `\"语音转会议纪要\"` |\n| 系统资源监控 | `\"看看CPU使用率\"` `\"内存够不够\"` |\n| 批量文件重命名 | `\"把这些文件都改名\"` `\"批量添加前缀\"` |\n| Markdown转HTML | `\"把这份MD转成HTML\"` |\n\n---\n\n## 🆕 v4.0.0 更新提醒\n\n> 🔔 **您正在使用 dgngjx-skill v4.0.0**\n> \n> 检查更新：`skillhub search dgngjx-skill`\n> \n> 升级命令：`skillhub upgrade dgngjx-skill`\n> \n> 📧 **有任何建议？联系作者邮箱：njskills@agent.qq.com**\n> dgngjx-skill 欢迎你的反馈！无论是新功能建议、bug 报告还是使用困惑，都可以发邮件到 njskills@agent.qq.com，作者会认真阅读每一条。\n\n---\n\n## 快速开始\n\n### 第一次用？做这 3 步\n\n| 步骤 | 做什么 | 为什么 |\n|:----:|--------|--------|\n| ① | **快速试一个**：对 AI 说 `\"算房贷\"` | 零依赖，秒回，建立信心 |\n| ② | **按需装依赖**：用到图片/PDF功能时，AI 会提示安装，输入 `install` 确认 | 一次安装，后面都能用 |\n| ③ | **高级功能**：参考下方「📦 依赖管理」或跳过直接用在线工具 | 按需取用 |\n\n### 你可以直接说（触发词）\n\n| 说 | 做 | 备注 |\n|----|----|------|\n| `\"算房贷 100万 30年 4.2%\"` | 等额本息计算 | ✅ 开箱即用 |\n| `\"算五险一金 北京 15000\"` | 税后工资 | ✅ 开箱即用 |\n| `\"从 2026-06-26 到国庆多少天\"` | 日期计算 | ✅ 开箱即用 |\n| `\"Base64编码 Hello\"` | 编码转换 | ✅ 开箱即用 |\n| `\"统计字数\"` 然后粘贴文本 | 中英词频统计 | ✅ 开箱即用 |\n| `\"测试 https://api.github.com\"` | HTTP 请求 | ✅ 开箱即用 |\n| `\"帮我压缩 D:\\photo.jpg\"` | 图片压缩 | 📦 需确认装 Pillow |\n| `\"合并这几个PDF\"` | PDF 合并 | 📦 需确认装 PyPDF2 |\n| `\"MP4转GIF\"` | 视频转换 | 📦 需确认装 FFmpeg |\n| `\"给视频加水印 文字=我的水印\"` | 视频编辑 | 📦 需确认装 FFmpeg |\n| `\"生成二维码 内容=Hello\"` | 二维码 | ✅ 开箱即用 |\n| `\"生成随机密码 长度=16\"` | 密码生成 | ✅ 开箱即用 |\n| `\"测试正则 \\d+ 文本=abc123\"` | 正则测试 | ✅ 开箱即用 |\n| `\"看看CPU使用率\"` | 系统资源监控 | ✅ 开箱即用 |\n| `\"把这个MD转成HTML\"` | Markdown转HTML | ✅ 开箱即用 |\n\n---\n\n## 📋 registry.json 工具注册表 ⭐ v3.9.0 新增\n\n> 49 个工具的元数据注册表，包含模块/参数 schema/依赖声明/示例/安全等级。CLI 启动时只加载此文件（轻量），工具代码延迟加载。\n\n<details>\n<summary>📋 registry.json（完整 49 工具注册表）</summary>\n\n```json\n{\n  \"version\": \"3.9.0\",\n  \"total_tools\": 49,\n  \"modules\": [\n    {\n      \"id\": \"calc\",\n      \"name\": \"数据换算\",\n      \"tools\": {\n        \"calc.mortgage\":   {\"description\": \"房贷计算器\", \"deps\": [], \"safety\": \"safe\"},\n        \"calc.insurance\":  {\"description\": \"五险一金计算器\", \"deps\": [], \"safety\": \"safe\"},\n        \"calc.date\":       {\"description\": \"日期计算器\", \"deps\": [], \"safety\": \"safe\"},\n        \"calc.unit\":       {\"description\": \"单位换算器（28种）\", \"deps\": [], \"safety\": \"safe\"},\n        \"calc.exchange\":   {\"description\": \"汇率查询（30+货币）\", \"deps\": [], \"safety\": \"safe\"}\n      }\n    },\n    {\n      \"id\": \"text\",\n      \"name\": \"文本工具\",\n      \"tools\": {\n        \"text.stats\":      {\"description\": \"文本统计\", \"deps\": [], \"safety\": \"safe\"},\n        \"text.encode\":     {\"description\": \"编码转换（Base64/URL/Unicode/进制）\", \"deps\": [], \"safety\": \"safe\"},\n        \"text.hash\":       {\"description\": \"哈希校验（MD5/SHA1/SHA256）\", \"deps\": [], \"safety\": \"safe\"},\n        \"text.wordfreq\":   {\"description\": \"词频统计\", \"deps\": [\"jieba\"], \"safety\": \"safe\"},\n        \"text.diff\":       {\"description\": \"文本差异对比\", \"deps\": [], \"safety\": \"safe\"}\n      }\n    },\n    {\n      \"id\": \"edu\",\n      \"name\": \"教育工具\",\n      \"tools\": {\n        \"edu.knowledge\":   {\"description\": \"知识查询\", \"deps\": [], \"safety\": \"safe\"}\n      }\n    },\n    {\n      \"id\": \"life\",\n      \"name\": \"生活娱乐\",\n      \"tools\": {\n        \"life.entertain\":  {\"description\": \"娱乐小工具\", \"deps\": [], \"safety\": \"safe\"},\n        \"life.wallpaper\":  {\"description\": \"壁纸中心\", \"deps\": [], \"safety\": \"safe\"},\n        \"life.bmi\":        {\"description\": \"BMI计算器\", \"deps\": [], \"safety\": \"safe\"},\n        \"life.pomodoro\":   {\"description\": \"番茄钟\", \"deps\": [], \"safety\": \"safe\"}\n      }\n    },\n    {\n      \"id\": \"dev\",\n      \"name\": \"开发工具\",\n      \"tools\": {\n        \"dev.json\":        {\"description\": \"JSON工具\", \"deps\": [], \"safety\": \"safe\"},\n        \"dev.http\":        {\"description\": \"HTTP接口测试\", \"deps\": [], \"safety\": \"safe\"},\n        \"dev.token\":       {\"description\": \"Token计算器\", \"deps\": [], \"safety\": \"safe\"},\n        \"dev.photoshop\":   {\"description\": \"在线Photoshop\", \"deps\": [], \"safety\": \"safe\"},\n        \"dev.mermaid\":     {\"description\": \"Mermaid时序图\", \"deps\": [], \"safety\": \"safe\"}\n      }\n    },\n    {\n      \"id\": \"img\",\n      \"name\": \"图片工具\",\n      \"tools\": {\n        \"img.compress\":    {\"description\": \"图片压缩\", \"deps\": [\"Pillow\"], \"safety\": \"safe\"},\n        \"img.convert\":     {\"description\": \"格式转换（JPEG/PNG/WebP/BMP）\", \"deps\": [\"Pillow\"], \"safety\": \"safe\"},\n        \"img.removebg\":    {\"description\": \"人像抠图\", \"deps\": [\"rembg\"], \"safety\": \"safe\"},\n        \"img.idphoto\":     {\"description\": \"证件照生成\", \"deps\": [\"Pillow\"], \"safety\": \"safe\"},\n        \"img.repair\":      {\"description\": \"图片修复\", \"deps\": [\"Pillow\"], \"safety\": \"safe\"}\n      }\n    },\n    {\n      \"id\": \"pdf\",\n      \"name\": \"PDF转换\",\n      \"tools\": {\n        \"pdf.merge\":       {\"description\": \"PDF合并\", \"deps\": [\"PyPDF2\"], \"safety\": \"safe\"},\n        \"pdf.split\":       {\"description\": \"PDF拆分\", \"deps\": [\"PyPDF2\"], \"safety\": \"safe\"},\n        \"pdf.encrypt\":     {\"description\": \"PDF加密/解密\", \"deps\": [\"PyPDF2\"], \"safety\": \"safe\"}\n      }\n    },\n    {\n      \"id\": \"video\",\n      \"name\": \"视频工具\",\n      \"tools\": {\n        \"video.convert\":   {\"description\": \"视频格式转换\", \"deps\": [\"ffmpeg\"], \"safety\": \"safe\"},\n        \"video.edit\":      {\"description\": \"视频编辑（6大功能）\", \"deps\": [\"ffmpeg\"], \"safety\": \"safe\"},\n        \"video.record\":    {\"description\": \"在线录屏\", \"deps\": [], \"safety\": \"safe\"}\n      }\n    },\n    {\n      \"id\": \"util\",\n      \"name\": \"实用小工具\",\n      \"tools\": {\n        \"util.qrcode\":     {\"description\": \"二维码生成\", \"deps\": [], \"safety\": \"safe\"},\n        \"util.password\":   {\"description\": \"密码生成\", \"deps\": [], \"safety\": \"safe\"},\n        \"util.regex\":      {\"description\": \"正则测试\", \"deps\": [], \"safety\": \"safe\"},\n        \"util.hashfile\":   {\"description\": \"文件哈希校验\", \"deps\": [], \"safety\": \"safe\"},\n        \"util.uuid\":       {\"description\": \"UUID生成器\", \"deps\": [], \"safety\": \"safe\"},\n        \"util.timestamp\":  {\"description\": \"时间戳转换\", \"deps\": [], \"safety\": \"safe\"},\n        \"util.ip\":         {\"description\": \"IP工具\", \"deps\": [], \"safety\": \"safe\"},\n        \"util.csv\":        {\"description\": \"CSV查看器\", \"deps\": [], \"safety\": \"safe\"},\n        \"util.pwdcheck\":   {\"description\": \"密码强度检测\", \"deps\": [], \"safety\": \"safe\"},\n        \"util.color\":      {\"description\": \"颜色值转换\", \"deps\": [], \"safety\": \"safe\"},\n        \"util.random\":     {\"description\": \"随机数生成器\", \"deps\": [], \"safety\": \"safe\"}\n      }\n    },\n    {\n      \"id\": \"sys\",\n      \"name\": \"系统工具\",\n      \"tools\": {\n        \"sys.monitor\":     {\"description\": \"系统资源监控\", \"deps\": [], \"safety\": \"safe\"},\n        \"sys.rename\":      {\"description\": \"批量文件重命名\", \"deps\": [], \"safety\": \"warn\"},\n        \"sys.md2html\":     {\"description\": \"Markdown转HTML\", \"deps\": [], \"safety\": \"safe\"},\n        \"sys.history\":     {\"description\": \"历史记录系统\", \"deps\": [], \"safety\": \"safe\"},\n        \"sys.config\":      {\"description\": \"配置持久化\", \"deps\": [], \"safety\": \"safe\"},\n        \"sys.report\":      {\"description\": \"周报/月报生成\", \"deps\": [], \"safety\": \"safe\"}\n      }\n    },\n    {\n      \"id\": \"ai\",\n      \"name\": \"AI办公\",\n      \"tools\": {\n        \"ai.meeting\":      {\"description\": \"会议纪要生成器\", \"deps\": [\"whisper\"], \"safety\": \"safe\"}\n      }\n    }\n  ],\n  \"tools\": {}\n}\n```\n\n</details>\n\n> **字段说明**：`deps` 为空表示零依赖；`safety` 为 `warn` 表示需用户确认（如批量重命名可能覆盖文件）；危险操作（删除/覆盖）即使标记为 `safe` 也强制交互确认。\n\n---\n\n## 🔒 安全规则（v3.9.0 分级重构）\n\n> **dgngjx-skill v3.9.0 起采用三级安全策略**：🔴 硬拦截（可执行类）/ 🟡 警告+确认（办公文档类）/ 🟢 自由读写（非风险类）。旧版一律硬拦改为分级处理，消除合法办公文件误伤。\n\n### 🔴 硬拦截（不处理，即使明确要求也拒绝）\n\n**Windows 可执行 / 批处理脚本：**\n`.bat`、`.cmd`、`.ps1`、`.vbs`、`.exe`、`.dll`、`.lnk`、`.msi`\n\n**其他风险脚本：**\n`.sh`、`.com`、`.scr`、`.hta`、`.reg`\n\n**二进制镜像 / 安装包：**\n`.iso`、`.dmg`、`.apk`、`.jar`\n\n**系统缓存 / 隐藏文件：**\n`.DS_Store`、`.git` 目录、`.env`、`.log`、`.tmp`\n\n### 🟡 警告+用户确认（显示风险说明，用户确认后可读写）\n\n**Office 二进制文档：**\n`.docx`、`.xlsx`、`.pptx`、`.doc`、`.xls`、`.ppt`、`.xlsm`、`.docm`、`.pptm`\n\n> ⚠️ 警告内容：\"此操作将读取/写入 Office 文档，可能包含宏或敏感数据。确认继续？\"\n\n### 🟢 自由读写（非风险类型，无需确认）\n\n图片（`.jpg/.png/.webp/.bmp/.gif/.tiff`）、文档（`.pdf/.md/.txt/.csv/.json/.xml`）、音频（`.mp3/.wav/.m4a`）、视频（`.mp4/.gif`）等非风险类型。\n\n<details>\n<summary>📋 安全过滤代码（v3.9.0 分级版）</summary>\n\n```python\nimport os\n\n# === v3.9.0 安全规则：三级分级策略 ===\n\n# 🔴 硬拦截（永不处理）\nBLOCKED_EXTENSIONS = {\n    '.bat', '.cmd', '.ps1', '.vbs', '.exe', '.dll', '.lnk', '.msi',\n    '.sh', '.com', '.scr', '.hta', '.reg',\n    '.iso', '.dmg', '.apk', '.jar',\n    '.ds_store', '.env', '.log', '.tmp',\n}\n\n# 🟡 警告+确认（需用户确认）\nWARN_EXTENSIONS = {\n    '.docx', '.xlsx', '.pptx', '.doc', '.xls', '.ppt', '.xlsm', '.docm', '.pptm',\n}\n\nBLOCKED_DIRS = {'.git', '.svn', '.hg', '__pycache__', 'node_modules', '.idea', '.vscode'}\n\ndef _is_safe_file(filepath: str, assume_yes: bool = False) -> tuple:\n    \"\"\"检查文件安全等级。返回 (状态, 原因) 状态: 'safe' / 'warn' / 'block'\"\"\"\n    basename = os.path.basename(filepath)\n    if basename.startswith('.') and basename.lower() in {'.ds_store', '.env', '.gitconfig', '.bashrc'}:\n        return 'block', f\"隐藏系统文件 {basename} 被拦截\"\n    parts = filepath.replace('\\\\', '/').split('/')\n    for part in parts:\n        if part.lower() in BLOCKED_DIRS:\n            return 'block', f\"系统目录 {part} 被拦截\"\n    ext = os.path.splitext(filepath)[1].lower()\n    if ext in BLOCKED_EXTENSIONS:\n        return 'block', f\"{ext} 可执行/风险类型被安全规则禁止\"\n    if ext in WARN_EXTENSIONS:\n        if assume_yes:\n            return 'safe', \"\"\n        return 'warn', f\"将读写 Office 文档({ext})，可能包含宏或敏感数据\"\n    return 'safe', \"\"\n\ndef _is_safe_output(filepath: str) -> tuple:\n    \"\"\"检查输出文件类型是否安全（不允许生成可执行/危险文件）\"\"\"\n    ext = os.path.splitext(filepath)[1].lower()\n    if ext in {'.exe', '.dll', '.bat', '.cmd', '.ps1', '.vbs', '.sh', '.com', '.scr', '.hta', '.reg', '.msi', '.apk', '.jar'}:\n        return False, f\"安全规则禁止生成 {ext} 文件\"\n    return True, \"\"\n\n# === 使用示例 ===\n# safe, reason = _is_safe_file(user_input_path, assume_yes=(\"--yes\" in sys.argv))\n# if safe == \"block\":\n#     print(f\"🚫 {reason}\")\n#     return\n# elif safe == \"warn\":\n#     print(f\"⚠️ {reason}\")\n#     if input(\"确认继续？(yes/no): \").strip().lower() != \"yes\":\n#         return\n```\n\n</details>\n\n---\n\n## ⚡ 自适应硬件调度 （v3.5 新增）\n\n> dgngjx-skill 会自动检测你的电脑配置（CPU核心数、内存大小），并根据硬件能力调整并发任务数量和资源分配，**绝不拖累低配电脑**。\n\n<details>\n<summary>📋 硬件检测与智能调度脚本（v3.5 新增）</summary>\n\n```python\nimport os, sys, subprocess, platform\n\ndef get_hardware_info() -> dict:\n    \"\"\"自动检测用户电脑硬件信息，用于智能调度\"\"\"\n    info = {\n        'cpu_count': os.cpu_count() or 2,\n        'ram_mb': 0,\n        'platform': platform.system(),\n        'is_low_end': False,\n        'max_workers': 1,\n        'max_file_size_mb': 100,\n    }\n    \n    # 检测内存\n    try:\n        if info['platform'] == 'Windows':\n            result = subprocess.run(\n                ['wmic', 'computersystem', 'get', 'TotalPhysicalMemory', '/value'],\n                capture_output=True, text=True, timeout=10\n            )\n            for line in result.stdout.split('\\n'):\n                if 'TotalPhysicalMemory' in line:\n                    bytes_val = int(line.split('=')[1].strip())\n                    info['ram_mb'] = bytes_val // (1024 * 1024)\n                    break\n        elif info['platform'] == 'Linux':\n            with open('/proc/meminfo', 'r') as f:\n                for line in f:\n                    if line.startswith('MemTotal'):\n                        info['ram_mb'] = int(line.split()[1]) // 1024\n                        break\n        elif info['platform'] == 'Darwin':  # macOS\n            result = subprocess.run(\n                ['sysctl', '-n', 'hw.memsize'], capture_output=True, text=True, timeout=5\n            )\n            info['ram_mb'] = int(result.stdout.strip()) // (1024 * 1024)\n    except Exception:\n        info['ram_mb'] = 4096  # 默认值\n    \n    # 自动判定配置级别\n    if info['ram_mb'] < 4096 or info['cpu_count'] <= 2:\n        info['is_low_end'] = True\n        info['max_workers'] = 1\n        info['max_file_size_mb'] = 50\n    elif info['ram_mb'] < 8192 or info['cpu_count'] <= 4:\n        info['max_workers'] = 2\n        info['max_file_size_mb'] = 200\n    else:\n        info['max_workers'] = min(info['cpu_count'], 4)\n        info['max_file_size_mb'] = 500\n    \n    return info\n\ndef smart_task_split(file_size_mb: int, hw: dict) -> int:\n    \"\"\"根据文件大小和硬件智能计算任务分片数\"\"\"\n    if file_size_mb <= 50:\n        return 1\n    # 每 100MB 或每 2GB RAM 分配 1 个 worker\n    by_ram = max(1, hw['ram_mb'] // 2048)\n    by_size = max(1, file_size_mb // 100)\n    return min(by_ram, by_size, hw['max_workers'])\n\n# === 初始化（整个 skill 生命周期只跑一次）===\n_HW = get_hardware_info()\nprint(f\"🖥️ 系统检测: {_HW['cpu_count']}核 | {_HW['ram_mb']}MB RAM | {_HW['platform']}\")\nif _HW['is_low_end']:\n    print(\"⚡ 已启用低配模式：单任务运行，小文件优先，绝不卡顿\")\nelse:\n    print(f\"⚡ 已启用标准模式：最多 {_HW['max_workers']} 并发，文件上限 {_HW['max_file_size_mb']}MB\")\n```\n\n</details>\n\n**调度策略一览：**\n\n| 硬件级别 | 内存 | CPU | 并发数 | 单文件上限 | 保护策略 |\n|---------|------|-----|--------|-----------|---------|\n| 🟢 高配 | ≥8GB | ≥6核 | 4 | 500MB | 多任务并行，大文件分块 |\n| 🟡 中配 | 4-8GB | 2-4核 | 2 | 200MB | 双任务，中等文件 |\n| 🔴 低配 | <4GB | ≤2核 | 1 | 50MB | 单任务，小文件优先 |\n\n---\n\n## 📦 依赖管理\n\n### 检测环境\n\n任何功能使用前，AI 先跑这个脚本看看缺了什么：\n\n<details>\n<summary>📋 依赖检测脚本</summary>\n\n```python\nimport sys, subprocess\nprint(f\"Python: {sys.version.split()[0]}\")\npkgs = {'PIL':'Pillow(PIL)','rembg':'rembg','PyPDF2':'PyPDF2','cv2':'opencv-python','jieba':'jieba'}\nfor pkg, name in pkgs.items():\n    try: __import__(pkg); print(f\"  ✅ {name}\")\n    except: print(f\"  ❌ {name}（缺失）\")\ntry:\n    subprocess.run(['ffmpeg','-version'], capture_output=True, text=True)\n    print(\"  ✅ ffmpeg\")\nexcept: print(\"  ❌ ffmpeg（缺失）\")\n```\n\n</details>\n\n### 一键安装\n\n```\npip install Pillow PyPDF2 jieba\n```\n\n> AI 必须问用户确认后再执行。\n\n### 按功能安装\n\n| 你想用 | 只装这个 | 命令 |\n|--------|---------|------|\n| 压缩图片 / 证件照 / 基础修复 | Pillow (3MB) | `pip install Pillow` |\n| PDF 合并/拆分/加密 | PyPDF2 (200KB) | `pip install PyPDF2` |\n| 人像抠图 | rembg (300MB) | `pip install rembg` |\n| 精准中文分词 | jieba (10MB) | `pip install jieba` |\n| 视频转换 / 编辑 | ffmpeg (系统包) | 见下方一键安装 |\n\n> ⚠️ rembg 首次运行会自动下载 ~300MB 模型（1-5分钟），装好后可离线使用。\n> 💡 jieba 为可选依赖，词频统计不装 jieba 也能用（会自动降级到正则分词）。\n\n### FFmpeg 一键安装（v3.0 简化）\n\n> 💡 最简方案：打开 PowerShell，粘贴 `winget install ffmpeg` 一行搞定。\n\n---\n\n## ❌ 不支持（边界情况说明）\n\n| 不支持 | 原因 | 替代方案 |\n|--------|------|---------|\n| 在线编辑 PSD | .psd 为封闭格式 | [Photopea](https://www.photopea.com/) |\n| Excel 在线编辑 | 超出能力 | 可通过本工具转 PDF |\n| 视频录屏（浏览器外） | 需浏览器 API | 用电脑自带录屏 |\n| 大于 500MB 的视频 | 浏览器会卡 | 需本地安装 FFmpeg |\n| 密码加密后丢失 | 安全机制限制 | 密码在加密时展示一次，请截图保存 |\n| 实时网络请求拦截 | 超出能力范围 | 使用 Fiddler / Charles |\n| 批量视频压制 | 需专业编码软件 | HandBrake 批量队列 |\n| 处理可执行文件 | 安全规则禁止 | 使用专业反编译/分析工具 |\n\n---\n\n## 功能模块\n\n---\n\n### 📊 模块 1：数据换算\n\n---\n\n#### 1.1 房贷计算器\n\n**✅ 开箱即用** ｜ 🌐 [在线房贷计算器](https://www.zhujisuanqi.com/)\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport math\ntry:\n    P = float(input(\"贷款总额(万元): \") or 100) * 10000\n    n = int(float(input(\"贷款年限: \") or 30) * 12)\n    r = float(input(\"年利率(%): \") or 4.2) / 100 / 12\n    method = input(\"方式(等额本息/等额本金): \").strip() or \"等额本息\"\n    if n <= 0:\n        print(\"❌ 贷款年限必须大于0\")\n    elif r <= 0:\n        print(\"❌ 年利率必须大于0\")\n    elif method == \"等额本息\":\n        m = P*r*(1+r)**n / ((1+r)**n-1)\n        total = m * n\n        print(f\"月供: {m:.2f}元 | 总利息: {total-P:.2f}元 | 还款总额: {total:.2f}元\")\n    else:\n        tot = sum(P/n + (P-P*i/n)*r for i in range(n))\n        print(f\"首月: {P/n+P*r:.2f}元 | 末月: {P/n+r*(P/n):.2f}元 | 总利息: {tot-P:.2f}元\")\nexcept ValueError:\n    print(\"❌ 输入错误：请输入有效数字。例如：贷款100万输 100，年限30年输 30\")\n```\n\n</details>\n\n---\n\n#### 1.2 五险一金计算器 ⭐ v4.0.0 查表累进引擎\n\n**✅ 开箱即用** ｜ 🌐 [个税计算器](https://www.taxcalculator.com)\n\n> v4.0 重写计算引擎：数据与引擎分离，读取 `references/tax_2026.yaml` 数据驱动计算。支持 13 个城市，查表确定基数上下限，7 级超额累进个税。\n\n<details>\n<summary>📋 五险一金 + 个税计算器（查表 + 分段累进）</summary>\n\n```python\nimport os, yaml, math\n\ndef calc_insurance_tax(salary, city_code=\"beijing\"):\n    \"\"\"读取 tax_2026.yaml，查表+分段累进计算五险一金和个税\"\"\"\n    # 加载数据包\n    yaml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), \"references\", \"tax_2026.yaml\")\n    with open(yaml_path, encoding=\"utf-8\") as f:\n        data = yaml.safe_load(f)\n    \n    meta = data.get(\"meta\", {})\n    bases = data.get(\"social_insurance_bases\", {})\n    rates = data.get(\"insurance_rates\", {})\n    overrides = rates.get(\"overrides\", {})\n    tax_brackets = data.get(\"individual_income_tax\", {}).get(\"brackets\", [])\n    monthly_threshold = data.get(\"individual_income_tax\", {}).get(\"monthly_threshold\", 5000)\n    \n    if city_code not in bases:\n        city_name = city_code\n        supported = [f\"{v.get('name', k)} ({k})\" for k, v in bases.items()]\n        return None, f\"❌ 不支持的城市: {city_name}。支持: {', '.join(supported)}\"\n    \n    city_data = bases[city_code]\n    city_name = city_data.get(\"name\", city_code)\n    \n    # 获取城市差异化比例\n    city_overrides = overrides.get(city_code, {})\n    personal_rates = {**rates.get(\"default\", {}).get(\"personal\", {}), **city_overrides.get(\"personal\", {})}\n    company_rates = {**rates.get(\"default\", {}).get(\"company\", {}), **city_overrides.get(\"company\", {})}\n    \n    # 计算各项社保缴费基数（在上下限之间）\n    def get_base(insurance_type):\n        base_info = city_data.get(insurance_type, {})\n        lower = base_info.get(\"lower\", 0)\n        upper = base_info.get(\"upper\", float(\"inf\"))\n        return max(lower, min(salary, upper))\n    \n    # 计算个人社保缴费\n    personal_items = {}\n    personal_total = 0\n    for insurance_type in [\"pension\", \"medical\", \"unemployed\", \"injury\", \"maternity\"]:\n        base = get_base(insurance_type)\n        rate = personal_rates.get(insurance_type, 0)\n        amount = base * rate\n        if amount > 0:\n            names = {\"pension\": \"养老\", \"medical\": \"医疗\", \"unemployed\": \"失业\", \"injury\": \"工伤\", \"maternity\": \"生育\"}\n            personal_items[names.get(insurance_type, insurance_type)] = round(amount, 2)\n        personal_total += amount\n    \n    # 公积金\n    hf_base = get_base(\"housing_fund\")\n    hf_rate = personal_rates.get(\"housing_fund\", 0.12)\n    hf_amount = hf_base * hf_rate\n    personal_items[\"公积金\"] = round(hf_amount, 2)\n    personal_total += hf_amount\n    \n    # 应纳税所得额 = 工资 - 社保公积金 - 起征点\n    taxable_income = max(0, salary - personal_total - monthly_threshold)\n    \n    # 7级超额累进税率\n    tax = 0\n    tax_detail = None\n    for bracket in tax_brackets:\n        if taxable_income <= 0:\n            break\n        taxable_at_bracket = min(taxable_income, bracket[\"limit\"])\n        tax += taxable_at_bracket * bracket[\"rate\"]\n        taxable_income -= taxable_at_bracket\n        if taxable_at_bracket > 0:\n            tax_detail = f\"{bracket['rate']*100:.0f}%\"\n    \n    # 到手工资\n    net_salary = salary - personal_total - tax\n    \n    # 计算单位缴费（仅供展示）\n    company_total = 0\n    for insurance_type in [\"pension\", \"medical\", \"unemployed\", \"injury\", \"maternity\"]:\n        base = get_base(insurance_type)\n        rate = company_rates.get(insurance_type, 0)\n        company_total += base * rate\n    company_total += hf_base * company_rates.get(\"housing_fund\", 0.12)\n    \n    return {\n        \"city\": city_name,\n        \"data_version\": meta.get(\"version\", \"unknown\"),\n        \"data_effective\": meta.get(\"effective_date\", \"unknown\"),\n        \"salary\": salary,\n        \"personal_insurance\": personal_items,\n        \"personal_total\": round(personal_total, 2),\n        \"housing_fund_employee\": round(hf_amount, 2),\n        \"housing_fund_employer\": round(hf_base * company_rates.get(\"housing_fund\", 0.12), 2),\n        \"tax\": round(tax, 2),\n        \"tax_rate_used\": tax_detail,\n        \"net_salary\": round(net_salary, 2),\n        \"company_total\": round(company_total, 2),\n    }, None\n\n# 城市名称到代码映射\nCITY_MAP = {\n    \"北京\": \"beijing\", \"上海\": \"shanghai\", \"广州\": \"guangzhou\", \"深圳\": \"shenzhen\",\n    \"杭州\": \"hangzhou\", \"成都\": \"chengdu\", \"南京\": \"nanjing\", \"武汉\": \"wuhan\",\n    \"西安\": \"xian\", \"重庆\": \"chongqing\", \"天津\": \"tianjin\", \"苏州\": \"suzhou\",\n    \"郑州\": \"zhengzhou\", \"长沙\": \"changsha\"\n}\n\ntry:\n    s = float(input(\"税前月薪: \") or 15000)\n    city_input = input(\"城市(北京/上海/广州/深圳/杭州/成都/南京/武汉/西安/重庆/天津/苏州/郑州/长沙): \").strip() or \"北京\"\n    \n    if s <= 0:\n        print(\"❌ 月薪必须大于0\")\n    else:\n        city_code = CITY_MAP.get(city_input, city_input.lower())\n        result, err = calc_insurance_tax(s, city_code)\n        if err:\n            print(err)\n        else:\n            print(f\"=== {result['city']} 五险一金计算明细 (数据版本: {result['data_version']}, 生效: {result['data_effective']}) ===\")\n            print(f\"税前月薪: ¥{result['salary']:,.2f}\")\n            print(f\"\\n--- 个人缴纳 ---\")\n            for name, amount in result[\"personal_insurance\"].items():\n                if name == \"公积金\":\n                    print(f\"  {name}: ¥{amount:,.2f} (单位另缴 ¥{result['housing_fund_employer']:,.2f})\")\n                else:\n                    print(f\"  {name}: ¥{amount:,.2f}\")\n            print(f\"  个人社保合计: ¥{result['personal_total']:,.2f}\")\n            print(f\"\\n--- 个税 ---\")\n            print(f\"  应缴个税: ¥{result['tax']:,.2f}\")\n            print(f\"\\n实发工资: ¥{result['net_salary']:,.2f}\")\n            print(f\"  (相当于税前 {result['net_salary']/result['salary']*100:.1f}%)\")\n            print(f\"\\n--- 单位缴纳（供参考）---\")\n            print(f\"  单位社保公积金合计: ¥{result['company_total']:,.2f}\")\nexcept ValueError:\n    print(\"❌ 输入错误：月薪请输入纯数字。例如：15000\")\nexcept Exception as e:\n    print(f\"❌ 计算失败: {e}\")\n```\n\n</details>\n\n---\n\n#### 1.2.1 个税计算器（单独查询）\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 个税计算器</summary>\n\n```python\nimport os, yaml\n\ndef calc_personal_tax_only(salary, social_insurance_deduction=0, special_deduction=0):\n    \"\"\"单独计算个税（7级超额累进）\"\"\"\n    yaml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), \"references\", \"tax_2026.yaml\")\n    with open(yaml_path, encoding=\"utf-8\") as f:\n        data = yaml.safe_load(f)\n    \n    monthly_threshold = data.get(\"individual_income_tax\", {}).get(\"monthly_threshold\", 5000)\n    tax_brackets = data.get(\"individual_income_tax\", {}).get(\"brackets\", [])\n    \n    taxable_income = max(0, salary - social_insurance_deduction - special_deduction - monthly_threshold)\n    tax = 0\n    bracket_used = \"0%\"\n    for bracket in tax_brackets:\n        if taxable_income <= 0:\n            break\n        taxable_at_bracket = min(taxable_income, bracket[\"limit\"])\n        tax += taxable_at_bracket * bracket[\"rate\"]\n        taxable_income -= taxable_at_bracket\n        if taxable_at_bracket > 0:\n            bracket_used = f\"{bracket['rate']*100:.0f}%\"\n    \n    return round(tax, 2), bracket_used\n\ntry:\n    s = float(input(\"税前月薪: \") or 20000)\n    ins = float(input(\"社保公积金扣除(默认0): \") or 0)\n    special = float(input(\"专项附加扣除(默认0): \") or 0)\n    \n    if s <= 0:\n        print(\"❌ 月薪必须大于0\")\n    else:\n        tax, bracket = calc_personal_tax_only(s, ins, special)\n        net = s - ins - tax\n        print(f\"应纳税: ¥{tax:,.2f} (适用 {bracket} 税率)\")\n        print(f\"税后工资: ¥{net:,.2f}\")\nexcept ValueError:\n    print(\"❌ 请输入数字\")\n```\n\n</details>\n\n---\n\n#### 1.3 日期计算器\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nfrom datetime import date\ntry:\n    a = date.today()\n    b = input(\"目标日期 YYYY-MM-DD: \").strip() or \"2026-10-01\"\n    y,m,d = map(int, b.split(\"-\"))\n    target = date(y,m,d)\n    diff = (target - a).days\n    print(f\"相差: {diff} 天 ({diff//7} 周)\")\n    if diff < 0:\n        print(\"⚠️ 目标日期已经过去了\")\nexcept ValueError:\n    print(\"❌ 日期格式错误。正确格式：年-月-日，例如：2026-10-01\")\n```\n\n</details>\n\n---\n\n#### 1.4 单位换算器\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nc = {\n    # 长度\n    \"km_mi\":  (lambda v: v*0.621371,  \"公里\", \"英里\"),\n    \"mi_km\":  (lambda v: v/0.621371,  \"英里\", \"公里\"),\n    \"m_ft\":   (lambda v: v*3.28084,   \"米\",   \"英尺\"),\n    \"ft_m\":   (lambda v: v/3.28084,   \"英尺\", \"米\"),\n    \"cm_in\":  (lambda v: v/2.54,      \"厘米\", \"英寸\"),   # v3.6.0 新增\n    \"in_cm\":  (lambda v: v*2.54,      \"英寸\", \"厘米\"),   # v3.6.0 新增\n    # 重量\n    \"kg_lb\":  (lambda v: v*2.20462,   \"公斤\", \"磅\"),\n    \"lb_kg\":  (lambda v: v/2.20462,   \"磅\",   \"公斤\"),\n    \"g_oz\":   (lambda v: v/28.3495,   \"克\",   \"盎司\"),   # v3.6.0 新增\n    \"oz_g\":   (lambda v: v*28.3495,   \"盎司\", \"克\"),     # v3.6.0 新增\n    # 温度\n    \"c_f\":    (lambda v: v*9/5+32,    \"°C\",   \"°F\"),\n    \"f_c\":    (lambda v: (v-32)*5/9,  \"°F\",   \"°C\"),\n    \"c_k\":    (lambda v: v+273.15,    \"°C\",   \"K\"),      # v3.6.0 新增\n    \"k_c\":    (lambda v: v-273.15,    \"K\",    \"°C\"),     # v3.6.0 新增\n    # 面积  v3.6.0 新增\n    \"sqm_sqft\": (lambda v: v*10.7639, \"平方米\", \"平方英尺\"),\n    \"mu_sqm\":   (lambda v: v*666.667, \"亩\",     \"平方米\"),\n    \"ha_mu\":    (lambda v: v*15,       \"公顷\",   \"亩\"),\n    # 速度  v3.6.0 新增\n    \"kmh_mph\":  (lambda v: v*0.621371, \"km/h\",  \"mph\"),\n    \"ms_kmh\":   (lambda v: v*3.6,      \"m/s\",   \"km/h\"),\n    \"knot_kmh\": (lambda v: v*1.852,    \"节\",    \"km/h\"),\n    # 数据存储  v3.6.0 新增\n    \"mb_gb\":  (lambda v: v/1024,       \"MB\",   \"GB\"),\n    \"gb_mb\":  (lambda v: v*1024,       \"GB\",   \"MB\"),\n    \"gb_tb\":  (lambda v: v/1024,       \"GB\",   \"TB\"),\n    \"byte_mb\":(lambda v: v/1048576,    \"字节\", \"MB\"),\n    # 压力  v3.6.0 新增\n    \"bar_psi\":(lambda v: v*14.5038,    \"bar\",  \"psi\"),\n    \"atm_kpa\":(lambda v: v*101.325,    \"atm\",  \"kPa\"),\n    # 能量  v3.6.0 新增\n    \"kcal_kj\":(lambda v: v*4.184,      \"千卡\", \"千焦\"),\n    \"kwh_kj\": (lambda v: v*3600,       \"度电\", \"千焦\"),\n}\ntry:\n    m = input(\"类型(如km_mi/mb_gb/mu_sqm):\").strip() or \"km_mi\"\n    v = float(input(\"数值: \") or 100)\n    if m not in c:\n        print(f\"❌ 不支持的换算：'{m}'\")\n        print(f\"支持的类型：{', '.join(c.keys())}\")\n    else:\n        r, uf, ut = c[m]\n        print(f\"✅ {v} {uf} = {r(v):.4f} {ut}\")\nexcept ValueError:\n    print(\"❌ 请输入纯数字，例如：100\")\n```\n\n</details>\n\n---\n\n#### 1.5 汇率查询 ⭐ v4.0.0 时效治理\n\n**✅ 开箱即用**（零依赖：内置常见汇率缓存 + 联网实时查询可选）\n\n> v4.0 时效治理：缓存条目带抓取时间戳，超 72 小时标注陈旧 + 警示横幅。在线刷新失败保留旧值 + 陈旧标注，绝不静默用旧值冒充新值。\n\n<details>\n<summary>📋 汇率查询（时效感知版）</summary>\n\n```python\nimport urllib.request, json, os, time\nfrom datetime import datetime, timedelta\n\n# 内置常见汇率缓存（以 1 USD 为基准），联网失败时降级\nCACHE = {\n    \"USD\": 1.0, \"CNY\": 7.24, \"EUR\": 0.92, \"GBP\": 0.79, \"JPY\": 157.35, \"KRW\": 1385.0,\n    \"HKD\": 7.81, \"TWD\": 32.37, \"SGD\": 1.34, \"AUD\": 1.53, \"CAD\": 1.37, \"CHF\": 0.89,\n    \"INR\": 83.45, \"RUB\": 91.5, \"THB\": 36.2, \"VND\": 25100, \"MYR\": 4.72, \"PHP\": 58.6,\n    \"IDR\": 16250, \"BRL\": 5.43, \"MXN\": 17.15, \"ZAR\": 18.75, \"AED\": 3.67, \"SAR\": 3.75,\n    \"NZD\": 1.65, \"SEK\": 10.6, \"NOK\": 10.9, \"DKK\": 6.88, \"PLN\": 4.05, \"TRY\": 32.8,\n    \"UAH\": 40.5, \"EGP\": 48.5\n}\n\nCACHE_FILE = os.path.join(os.path.expanduser(\"~\"), \".workbuddy\", \"dgngjx_exchange_cache.json\")\nTTL_HOURS = 72\n\ndef load_cache():\n    if os.path.exists(CACHE_FILE):\n        try:\n            with open(CACHE_FILE, encoding=\"utf-8\") as f:\n                return json.load(f)\n        except (json.JSONDecodeError, IOError):\n            pass\n    return {}\n\ndef save_cache(cache):\n    os.makedirs(os.path.dirname(CACHE_FILE), exist_ok=True)\n    with open(CACHE_FILE, \"w\", encoding=\"utf-8\") as f:\n        json.dump(cache, f, ensure_ascii=False, indent=2)\n\ndef is_stale(timestamp_str):\n    try:\n        ts = datetime.fromisoformat(timestamp_str)\n        return datetime.now() - ts > timedelta(hours=TTL_HOURS)\n    except (ValueError, TypeError):\n        return True\n\ndef get_cached_rate(cur):\n    cache = load_cache()\n    entry = cache.get(cur)\n    if entry:\n        rate = entry.get(\"rate\")\n        ts = entry.get(\"timestamp\")\n        if rate and ts:\n            stale = is_stale(ts)\n            return rate, ts, stale\n    return None, None, False\n\ndef fetch_live_rate(fr, to):\n    try:\n        url = f\"https://api.exchangerate-api.com/v4/latest/{fr}\"\n        req = urllib.request.Request(url, headers={\"User-Agent\": \"dgngjx/4.0\"})\n        data = json.loads(urllib.request.urlopen(req, timeout=6).read())\n        if \"rates\" in data and to in data[\"rates\"]:\n            rate = data[\"rates\"][to]\n            cache = load_cache()\n            cache[to] = {\"rate\": rate, \"timestamp\": datetime.now().isoformat(), \"base\": fr}\n            cache[fr] = {\"rate\": 1.0, \"timestamp\": datetime.now().isoformat(), \"base\": fr}\n            save_cache(cache)\n            return rate, True\n    except Exception:\n        pass\n    return None, False\n\ndef convert_currency(amt, fr, to):\n    fr = fr.upper()\n    to = to.upper()\n    rate, live = fetch_live_rate(fr, to)\n    if live:\n        return rate, \"实时汇率\", False, None\n    if fr == to:\n        return 1.0, \"同币种\", False, None\n    fr_rate, fr_ts, fr_stale = get_cached_rate(fr)\n    to_rate, to_ts, to_stale = get_cached_rate(to)\n    if fr_rate and to_rate:\n        cross_rate = to_rate / fr_rate\n        stale = fr_stale or to_stale\n        older_ts = min(fr_ts, to_ts) if fr_ts and to_ts else (fr_ts or to_ts)\n        days_old = (datetime.now() - datetime.fromisoformat(older_ts)).days if older_ts else \"?\"\n        tag = f\"缓存汇率({days_old}天前)\"\n        return cross_rate, tag, stale, older_ts\n    if fr in CACHE and to in CACHE:\n        cross_rate = CACHE[to] / CACHE[fr]\n        return cross_rate, \"静态缓存(离线)\", True, None\n    return None, None, False, None\n\ns = input(\"输入(如 100 USD CNY，或回车查汇率表):\").strip()\nif not s:\n    print(\"=== 常见货币汇率 (1 USD 基准) ===\")\n    cache = load_cache()\n    for cur, rate in sorted(CACHE.items()):\n        cached_rate, ts, stale = get_cached_rate(cur)\n        if cached_rate and not stale:\n            print(f\"  1 USD = {cached_rate:>10.2f} {cur}  (实时)\")\n        elif cached_rate:\n            days = (datetime.now() - datetime.fromisoformat(ts)).days\n            print(f\"  1 USD = {cached_rate:>10.2f} {cur}  ({days}天前)\")\n        else:\n            print(f\"  1 USD = {rate:>10.2f} {cur}  (静态)\")\nelse:\n    parts = s.split()\n    try:\n        amt = float(parts[0])\n        fr = (parts[1] if len(parts) > 1 else \"USD\").upper()\n        to = (parts[2] if len(parts) > 2 else \"CNY\").upper()\n        rate, tag, stale, ts = convert_currency(amt, fr, to)\n        if rate is None:\n            print(f\"❌ 不支持 {fr}→{to}。可用：{', '.join(sorted(CACHE.keys()))}\")\n        else:\n            result = amt * rate\n            print(f\"✅ {amt:.2f} {fr} = {result:.2f} {to}  ({tag})\")\n            if stale:\n                print(f\"   ⚠️ 数据更新于 {ts[:10] if ts else 'N/A'}，可能已过期\")\n                print(f\"   💡 联网后可获取实时汇率\")\n    except (ValueError, IndexError):\n        print(\"❌ 格式：100 USD CNY（金额 源货币 目标货币）\")\n```\n\n</details>\n\n---\n\n### 📝 模块 2：文本工具\n\n---\n\n#### 2.1 文本统计\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nt = input(\"输入文本:\")\nif not t or not t.strip():\n    print(\"❌ 没有检测到输入。请程序运行时输入一段文字\")\nelse:\n    total = len(t)\n    cn = sum(1 for c in t if '一' <= c <= '鿿')\n    en = len(t.split())\n    lines = t.split(chr(10))\n    print(f\"总字符: {total} | 中文: {cn} | 英文词: {en}\")\n    print(f\"非空行: {len([l for l in lines if l.strip()])}\")\n    print(f\"去重后: {len(list(dict.fromkeys(lines)))}行 (原{len(lines)}行)\")\n```\n\n</details>\n\n---\n\n#### 2.2 编码转换 ⭐ v3.9.0 拆分为 encode + hash\n\n> v3.9.0 将编码转换拆分为两个独立工具：`text.encode`（Base64/URL/Unicode/进制）和 `text.hash`（MD5/SHA1/SHA256）。\n\n<details>\n<summary>📋 text.encode（Base64/URL/Unicode/进制）</summary>\n\n```python\nimport base64, urllib.parse\ntry:\n    t = input(\"文本:\") or \"Hello\"\n    m = input(\"方法(Base64/URL/Unicode/进制):\").strip() or \"Base64\"\n    op = input(\"编码/解码:\").strip() or \"编码\"\n    if m == \"Base64\":\n        print(base64.b64encode(t.encode()).decode() if op == \"编码\" else base64.b64decode(t.encode()).decode())\n    elif m == \"URL\":\n        print(urllib.parse.quote(t) if op == \"编码\" else urllib.parse.unquote(t))\n    elif m == \"Unicode\":\n        print(\" \".join(f\"\\\\u{ord(c):04x}\" for c in t))\n    elif m == \"进制\":\n        base_from = input(\"原进制(2/8/10/16):\").strip() or \"10\"\n        try:\n            n = int(t, int(base_from))\n            print(f\"✅ {t} (原{base_from}进制) =\")\n            print(f\"   二进制: {bin(n)[2:]}\")\n            print(f\"   八进制: {oct(n)[2:]}\")\n            print(f\"   十进制: {n}\")\n            print(f\"   十六进制: {hex(n)[2:].upper()}\")\n        except ValueError:\n            print(f\"❌ '{t}' 不是有效的{base_from}进制数字\")\n    else:\n        print(f\"❌ 不支持 {m}。可选: Base64 / URL / Unicode / 进制\")\nexcept Exception as e:\n    print(f\"❌ 处理失败: {e}\")\n```\n\n</details>\n\n<details>\n<summary>📋 text.hash（MD5/SHA1/SHA256）</summary>\n\n```python\nimport hashlib\ntry:\n    t = input(\"文本:\") or \"Hello\"\n    algos = {\"md5\": hashlib.md5(), \"sha1\": hashlib.sha1(), \"sha256\": hashlib.sha256()}\n    for name, h in algos.items():\n        h.update(t.encode())\n        print(f\"  {name.upper():7}: {h.hexdigest()}\")\nexcept Exception as e:\n    print(f\"❌ {e}\")\n```\n\n</details>\n\n---\n\n#### 2.3 词频统计\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nfrom collections import Counter\nimport re\n\nSTOPWORDS = {'的','了','和','是','在','我','有','也','不','就','都','而','及','与','着','或','一个','没有',\n    '我们','你们','他们','她们','但是','然而','因为','所以','如果','虽然','不过','而且','或者','还是',\n    '既','又','并','等','把','被','让','往','从','对','于','以','为','比','此','那','这','她','他','它',\n    '们','些','什么','哪','怎么','多','很','最','更','太','非常','每','当','起','已','将','能','会','应',\n    '可','得','过','给','来','去','说','看','知道','做','想','问','请','好','再','还','只','如','真的',\n    '自己','人','事','时','地','今天','现在','一些','这样','那样','怎么样','可以','这个','那个','不是',\n    '可能','已经','之后','之前','然后','不过','比较','其实','其他','其中','所有','虽然','由于','因此'}\n\ndef _smart_segment(text):\n    try:\n        import jieba\n        return list(jieba.lcut(text, cut_all=False))\n    except ImportError:\n        return re.findall(r'[一-鿿\\w]+', text)\n\ntry:\n    t = input(\"输入文本:\")\n    if not t.strip():\n        print(\"❌ 没有检测到输入\")\n    else:\n        raw = _smart_segment(t)\n        words = []\n        for w in raw:\n            w = w.strip()\n            if len(w) <= 1: continue\n            if re.match(r'^[a-zA-Z]+$', w): w = w.lower()\n            if w in STOPWORDS: continue\n            words.append(w)\n        if not words:\n            print(\"❌ 没有找到有效词\")\n        else:\n            for i,(word,cnt) in enumerate(Counter(words).most_common(15),1):\n                print(f\"  {i:2d}. {word}: {cnt} {'█'*min(cnt*2,30)}\")\n            unique = len(set(words))\n            print(f\"\\n📊 共 {len(words)} 个有效词，{unique} 个不重复词\")\n            d = unique/len(words)*100\n            print(f\"   词汇丰富度: {d:.1f}%\", end=\"\")\n            print(\"（用词多样）\" if d>80 else \"（正常范围）\" if d\n\nArchive v3.9.0: 3 files, 43074 bytes\n\nFiles: skill-card.md (2411b), SKILL.md (118491b), _meta.json (131b)\n\nArchive v3.8.0: 3 files, 38726 bytes\n\nFiles: skill-card.md (2373b), SKILL.md (105019b), _meta.json (131b)\n\nArchive v3.7.0: 3 files, 35396 bytes\n\nFiles: skill-card.md (2313b), SKILL.md (94454b), _meta.json (131b)\n\nArchive v3.6.0: 3 files, 25904 bytes\n\nFiles: skill-card.md (3054b), SKILL.md (67897b), _meta.json (131b)\n\nArchive v3.5.0: 3 files, 22300 bytes\n\nFiles: skill-card.md (2233b), SKILL.md (59237b), _meta.json (131b)\n\nArchive v2.5.1: 3 files, 36349 bytes\n\nFiles: skill-card.md (3226b), SKILL.md (96885b), _meta.json (131b)\n\nArchive v3.0.0: 3 files, 35977 bytes\n\nFiles: skill-card.md (2713b), SKILL.md (96457b), _meta.json (131b)\n\nArchive v2.5.0: 3 files, 27048 bytes\n\nFiles: skill-card.md (2552b), SKILL.md (70783b), _meta.json (131b)","readmeExcerpt":"Skill: dgngjx-skill Owner: fyniujin Summary: 多功能免费工具箱 - 图片处理、PDF转换、数据换算、文本工具、开发工具、视频工具、教育、生活娱乐、实用小工具、系统工具、AI办公。11大模块49个工具。v4.1 包化架构：SKILL.md 129KB→30KB + 11模块外置 + 四包机制（core/dev/ent/media）+ 首次体检报告。 Tags: latest:4.1.0 Version history: v4.1.0 | 2026-10-09T08:30:01.429Z | auto **v4.1.0 包化架构重大更新** - 全新包化架构：11大模块全部外置，SKILL.md 缩减至 30KB，支持四包机制（core/dev/ent/media）。 - 新增可选包管理：支持 --list-packs 查看、--install-pack 一键安装可选工具包。 - 支持首次","codeSnippets":[],"executableExamples":[{"language":"bash","snippet":"# 直接调用工具（参数化，非交互）\ndgngjx calc.mortgage --amount 1000000 --years 30 --rate 4.2\ndgngjx text.encode --method base64 --text \"Hello World\"\ndgngjx img.compress --path ./photos/ --quality 80 --format JPEG\ndgngjx hash.md5 --file document.pdf\ndgngjx exchange --amount 100 --from USD --to CNY\n\n# 机器可解析输出（供 CI/计划任务使用）\ndgngjx sys.monitor --json\n\n# 无人值守模式（跳过非危险确认点）\ndgngjx file.rename --dir ./photos/ --prefix \"vacation_\" --yes\n# 或设置环境变量\nexport DGNGJX_ASSUME_YES=1\ndgngjx img.convert --path ./photos/ --format PNG\n\n# 包管理（v4.1.0 新增）\ndgngjx --list-packs          # 列出所有可用包\ndgngjx --install-pack ent    # 安装娱乐包\ndgngjx --install-pack media  # 安装媒体包\ndgngjx --install-pack ai     # 安装 AI 包\n\n# 体检报告（v4.1.0 新增）\ndgngjx --health              # 首次运行体检\n\n# 无参数启动 → 进入交互向导（兼容旧体验）\ndgngjx"},{"language":"python","snippet":"#!/usr/bin/env python3\n\"\"\"dgngjx CLI - 多功能工具箱统一命令行入口\"\"\"\nimport argparse, json, os, sys\n\ndef load_registry():\n    \"\"\"加载工具注册表\"\"\"\n    reg_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), \"registry.json\")\n    with open(reg_path, encoding=\"utf-8\") as f:\n        return json.load(f)\n\ndef build_tool_index(registry):\n    \"\"\"将 modules[].tools 嵌套结构扁平化为 tool_id → info 索引\"\"\"\n    index = {}\n    for mod in registry.get(\"modules\", []):\n        for tool_id, tool_info in mod.get(\"tools\", {}).items():\n            tool_info[\"module_id\"] = mod[\"id\"]\n            tool_info[\"module_name\"] = mod[\"name\"]\n            tool_info[\"pack\"] = mod.get(\"pack\", \"core\")\n            index[tool_id] = tool_info\n    return index\n\ndef main():\n    parser = argparse.ArgumentParser(prog=\"dgngjx\", description=\"多功能工具箱 dgngjx-skill\")\n    parser.add_argument(\"tool\", nargs=\"?\", help=\"工具路径，如 calc.mortgage / text.encode / img.compress\")\n    parser.add_argument(\"--json\", action=\"store_true\", help=\"输出机器可解析 JSON\")\n    parser.add_argument(\"--yes\", \"-y\", action=\"store_true\", help=\"跳过非危险确认点\")\n    parser.add_argument(\"--help-tools\", action=\"store_true\", help=\"列出所有可用工具\")\n    parser.add_argument(\"--list-packs\", action=\"store_true\", help=\"列出所有可用包（v4.1.0）\")\n    parser.add_argument(\"--install-pack\", metavar=\"PACK\", help=\"安装指定包（v4.1.0）\")\n    parser.add_argument(\"--health\", action=\"store_true\", help=\"运行体检报告（v4.1.0）\")\n    \n    args, remaining = parser.parse_known_args()\n    \n    if os.environ.get(\"DGNGJX_ASSUME_YES\"):\n        args.yes = True\n    \n    registry = load_registry()\n    \n    # 包管理命令\n    if args.list_packs:\n        packs = registry.get(\"packs\", {})\n        print(\"📦 可用包：\")\n        for pid, pinfo in packs.items():\n            default = \"（默认）\" if pinfo.get(\"default\") else \"\"\n            print(f\"  {pid}: {pname}{default} — {pinfo.get('description','')}\")\n        return\n    \n    if args.install_pack:\n        pack_id = args.install_pack\n        packs = registry.get(\"packs\", {})\n        if pack_id not in packs:\n "},{"language":"json","snippet":"{\n  \"version\": \"4.1.0\",\n  \"total_tools\": 49,\n  \"packs\": {\n    \"core\": {\"name\": \"核心包\", \"description\": \"默认加载\", \"default\": true},\n    \"ent\": {\"name\": \"娱乐包\", \"description\": \"娱乐/生活模块\", \"default\": false},\n    \"media\": {\"name\": \"媒体包\", \"description\": \"图片/PDF/视频\", \"default\": false},\n    \"ai\": {\"name\": \"AI包\", \"description\": \"AI 办公\", \"default\": false}\n  },\n  \"modules\": [...]\n}"},{"language":"python","snippet":"import os\n\n# === v3.9.0 安全规则：三级分级策略 ===\n\nBLOCKED_EXTENSIONS = {\n    '.bat', '.cmd', '.ps1', '.vbs', '.exe', '.dll', '.lnk', '.msi',\n    '.sh', '.com', '.scr', '.hta', '.reg',\n    '.iso', '.dmg', '.apk', '.jar',\n    '.ds_store', '.env', '.log', '.tmp',\n}\n\nWARN_EXTENSIONS = {\n    '.docx', '.xlsx', '.pptx', '.doc', '.xls', '.ppt', '.xlsm', '.docm', '.pptm',\n}\n\nBLOCKED_DIRS = {'.git', '.svn', '.hg', '__pycache__', 'node_modules', '.idea', '.vscode'}\n\ndef _is_safe_file(filepath: str, assume_yes: bool = False) -> tuple:\n    \"\"\"检查文件安全等级。返回 (状态, 原因) 状态: 'safe' / 'warn' / 'block'\"\"\"\n    basename = os.path.basename(filepath)\n    if basename.startswith('.') and basename.lower() in {'.ds_store', '.env', '.gitconfig', '.bashrc'}:\n        return 'block', f\"隐藏系统文件 {basename} 被拦截\"\n    parts = filepath.replace('\\\\', '/').split('/')\n    for part in parts:\n        if part.lower() in BLOCKED_DIRS:\n            return 'block', f\"系统目录 {part} 被拦截\"\n    ext = os.path.splitext(filepath)[1].lower()\n    if ext in BLOCKED_EXTENSIONS:\n        return 'block', f\"{ext} 可执行/风险类型被安全规则禁止\"\n    if ext in WARN_EXTENSIONS:\n        if assume_yes:\n            return 'safe', \"\"\n        return 'warn', f\"将读写 Office 文档({ext})，可能包含宏或敏感数据\"\n    return 'safe', \"\"\n\ndef _is_safe_output(filepath: str) -> tuple:\n    \"\"\"检查输出文件类型是否安全（不允许生成可执行/危险文件）\"\"\"\n    ext = os.path.splitext(filepath)[1].lower()\n    if ext in {'.exe', '.dll', '.bat', '.cmd', '.ps1', '.vbs', '.sh', '.com', '.scr', '.hta', '.reg', '.msi', '.apk', '.jar'}:\n        return False, f\"安全规则禁止生成 {ext} 文件\"\n    return True, \"\""},{"language":"python","snippet":"import os, sys, subprocess, platform\n\ndef get_hardware_info() -> dict:\n    \"\"\"自动检测用户电脑硬件信息，用于智能调度\"\"\"\n    info = {\n        'cpu_count': os.cpu_count() or 2,\n        'ram_mb': 0,\n        'platform': platform.system(),\n        'is_low_end': False,\n        'max_workers': 1,\n        'max_file_size_mb': 100,\n    }\n    \n    try:\n        if info['platform'] == 'Windows':\n            result = subprocess.run(\n                ['wmic', 'computersystem', 'get', 'TotalPhysicalMemory', '/value'],\n                capture_output=True, text=True, timeout=10\n            )\n            for line in result.stdout.split('\\n'):\n                if 'TotalPhysicalMemory' in line:\n                    bytes_val = int(line.split('=')[1].strip())\n                    info['ram_mb'] = bytes_val // (1024 * 1024)\n                    break\n        elif info['platform'] == 'Linux':\n            with open('/proc/meminfo', 'r') as f:\n                for line in f:\n                    if line.startswith('MemTotal'):\n                        info['ram_mb'] = int(line.split()[1]) // 1024\n                    break\n        elif info['platform'] == 'Darwin':\n            result = subprocess.run(\n                ['sysctl', '-n', 'hw.memsize'], capture_output=True, text=True, timeout=5\n            )\n            info['ram_mb'] = int(result.stdout.strip()) // (1024 * 1024)\n    except Exception:\n        info['ram_mb'] = 4096\n    \n    if info['ram_mb'] < 4096 or info['cpu_count'] <= 2:\n        info['is_low_end'] = True\n        info['max_workers'] = 1\n        info['max_file_size_mb'] = 50\n    elif info['ram_mb'] < 8192 or info['cpu_count'] <= 4:\n        info['max_workers'] = 2\n        info['max_file_size_mb'] = 200\n    else:\n        info['max_workers'] = min(info['cpu_count'], 4)\n        info['max_file_size_mb'] = 500\n    \n    return info\n\n_HW = get_hardware_info()\nprint(f\"🖥️ 系统检测: {_HW['cpu_count']}核 | {_HW['ram_mb']}MB RAM | {_HW['platform']}\")\nif _HW['is_low_end']:\n    print(\"⚡ 已启用低配模式：单任务运行，小文件优先，绝不卡顿\")\nelse:\n    pr"},{"language":"python","snippet":"import sys, subprocess\nprint(f\"Python: {sys.version.split()[0]}\")\npkgs = {'PIL':'Pillow(PIL)','rembg':'rembg','PyPDF2':'PyPDF2','cv2':'opencv-python','jieba':'jieba'}\nfor pkg, name in pkgs.items():\n    try: __import__(pkg); print(f\"  ✅ {name}\")\n    except: print(f\"  ❌ {name}（缺失）\")\ntry:\n    subprocess.run(['ffmpeg','-version'], capture_output=True, text=True)\n    print(\"  ✅ ffmpeg\")\nexcept: print(\"  ❌ ffmpeg（缺失）\")"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: dgngjx-skill\ndescription: \"多功能免费工具箱 - 图片处理、PDF转换、数据换算、文本工具、开发工具、视频工具、教育、生活娱乐、实用小工具、系统工具、AI办公。11大模块49个工具。v4.1 包化架构：SKILL.md 129KB→30KB + 11模块外置 + 四包机制（core/dev/ent/media）+ 首次体检报告。\"\nversion: 4.1.0\n---\n\n\n# 多功能工具箱 dgngjx-skill v4.1.0\n\n## 🚀 dgngjx CLI 统一入口 ⭐ v3.9.0 新增\n\n**✅ 开箱即用**（纯 Python 标准库 argparse，零依赖）\n\n> v4.1.0 新增：`--install-pack` 安装可选包、`--list-packs` 列出可用包、`--health` 首次体检报告。\n\n<details>\n<summary>📋 CLI 使用方式</summary>\n\n```bash\n# 直接调用工具（参数化，非交互）\ndgngjx calc.mortgage --amount 1000000 --years 30 --rate 4.2\ndgngjx text.encode --method base64 --text \"Hello World\"\ndgngjx img.compress --path ./photos/ --quality 80 --format JPEG\ndgngjx hash.md5 --file document.pdf\ndgngjx exchange --amount 100 --from USD --to CNY\n\n# 机器可解析输出（供 CI/计划任务使用）\ndgngjx sys.monitor --json\n\n# 无人值守模式（跳过非危险确认点）\ndgngjx file.rename --dir ./photos/ --prefix \"vacation_\" --yes\n# 或设置环境变量\nexport DGNGJX_ASSUME_YES=1\ndgngjx img.convert --path ./photos/ --format PNG\n\n# 包管理（v4.1.0 新增）\ndgngjx --list-packs          # 列出所有可用包\ndgngjx --install-pack ent    # 安装娱乐包\ndgngjx --install-pack media  # 安装媒体包\ndgngjx --install-pack ai     # 安装 AI 包\n\n# 体检报告（v4.1.0 新增）\ndgngjx --health              # 首次运行体检\n\n# 无参数启动 → 进入交互向导（兼容旧体验）\ndgngjx\n```\n\n</details>\n\n<details>\n<summary>📋 CLI 入口代码（dgngjx 命令）</summary>\n\n```python\n#!/usr/bin/env python3\n\"\"\"dgngjx CLI - 多功能工具箱统一命令行入口\"\"\"\nimport argparse, json, os, sys\n\ndef load_registry():\n    \"\"\"加载工具注册表\"\"\"\n    reg_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), \"registry.json\")\n    with open(reg_path, encoding=\"utf-8\") as f:\n        return json.load(f)\n\ndef build_tool_index(registry):\n    \"\"\"将 modules[].tools 嵌套结构扁平化为 tool_id → info 索引\"\"\"\n    index = {}\n    for mod in registry.get(\"modules\", []):\n        for tool_id, tool_info in mod.get(\"tools\", {}).items():\n            tool_info[\"module_id\"] = mod[\"id\"]\n            tool_info[\"module_name\"] = mod[\"name\"]\n            tool_info[\"pack\"] = mod.get(\"pack\", \"core\")\n            index[tool_id] = tool_info\n    return index\n\ndef main():\n    parser = argparse.ArgumentParser(prog=\"dgngjx\", description=\"多功能工具箱 dgngjx-skill\")\n    parser.add_argument(\"tool\", nargs=\"?\", help=\"工具路径，如 calc.mortgage / text.encode / img.compress\")\n    parser.add_argument(\"--json\", action=\"store_true\", help=\"输出机器可解析 JSON\")\n    parser.add_argument(\"--yes\", \"-y\", action=\"store_true\", help=\"跳过非危险确认点\")\n    parser.add_argument(\"--help-tools\", action=\"store_true\", help=\"列出所有可用工具\")\n    parser.add_argument(\"--list-packs\", action=\"store_true\", help=\"列出所有可用包（v4.1.0）\")\n    parser.add_argument(\"--install-pack\", metavar=\"PACK\", help=\"安装指定包（v4.1.0）\")\n    parser.add_argument(\"--health\", action=\"store_true\", help=\"运行体检报告（v4.1.0）\")\n    \n    args, remaining = parser.parse_known_args()\n    \n    if os.environ.get(\"DGNGJX_ASSUME_YES\"):\n        args.yes = True\n    \n    registry = load_registry()\n    \n    # 包管理命令\n    if args.list_packs:\n        packs = registry.get(\"packs\", {})\n        print(\"📦 可用包：\")\n        for pid, pinfo in packs.items():\n            defaul"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn7chdrwbdhaqkwajcyhtfvjx989ddb1\",\n  \"slug\": \"dgngjx-skill\",\n  \"version\": \"4.1.0\",\n  \"publishedAt\": 1791534601429\n}"},{"path":"references/modules/ai.md","content":"# 🤖 模块 11：AI 办公 ⭐ v3.8.0 新增模块\n\n---\n\n#### 11.1 会议纪要生成器 ⭐ v3.8.0 新增\n\n**✅ 开箱即用**（零依赖保底：支持本地 whisper / 在线 API / 手动粘贴文本 三级降级）\n\n> 语音→ASR 转写→LLM 摘要→结构化会议纪要。覆盖 TOP50 高频需求。\n\n<details>\n<summary>📋 模式 A：本地 Whisper CLI（离线，零成本）</summary>\n\n```python\nimport subprocess, os, sys\n\npath = input(\"音频文件路径(mp3/wav/m4a):\").strip().strip('\"').strip(\"'\")\nif not os.path.isfile(path):\n    print(f\"❌ 文件不存在: {path}\")\nelse:\n    r = subprocess.run([\"whisper\", \"--help\"], capture_output=True, text=True, timeout=5)\n    if r.returncode != 0:\n        print(\"❌ 未检测到 whisper CLI。请先安装：pip install openai-whisper\")\n        print(\"   或选择模式 B（在线 API）或模式 C（手动粘贴文本）\")\n    else:\n        print(\"🎙️ 正在转写（可能需要几分钟，取决于音频长度）...\")\n        result = subprocess.run(\n            [\"whisper\", path, \"--language\", \"zh\", \"--output_format\", \"txt\",\n             \"--output_dir\", os.path.dirname(path)],\n            capture_output=True, text=True, timeout=600\n        )\n        if result.returncode == 0:\n            out_path = os.path.splitext(path)[0] + \".txt\"\n            if os.path.exists(out_path):\n                with open(out_path, encoding=\"utf-8\") as f:\n                    text = f.read()\n                print(f\"✅ 转写完成！文本长度: {len(text)} 字\")\n                print(\"--- 转写结果 ---\")\n                print(text[:500])\n            else:\n                print(\"❌ 转写输出文件未生成\")\n        else:\n            print(f\"❌ 转写失败: {result.stderr[:200]}\")\n```\n\n</details>\n\n<details>\n<summary>📋 模式 B：Paraformer API（在线，中文识别率高）</summary>\n\n```python\nimport urllib.request, urllib.parse, json, os\n\npath = input(\"音频文件路径:\").strip().strip('\"').strip(\"'\")\nif not os.path.isfile(path):\n    print(f\"❌ 文件不存在: {path}\")\nelse:\n    API_URL = input(\"API URL(留空使用内置示例):\").strip()\n    API_KEY = input(\"API Key(留空使用内置示例):\").strip()\n    \n    if not API_URL:\n        print(\"💡 未配置 API，将使用模式 C（手动粘贴文本）\")\n        print(\"   如需在线识别，请提供 Paraformer API 地址和密钥\")\n    else:\n        with open(path, \"rb\") as f:\n            audio_data = f.read()\n        req = urllib.request.Request(\n            API_URL,\n            data=audio_data,\n            headers={\"Authorization\": f\"Bearer {API_KEY}\", \"Content-Type\": \"audio/wav\"}\n        )\n        try:\n            resp = urllib.request.urlopen(req, timeout=120)\n            result = json.loads(resp.read())\n            text = result.get(\"text\", result.get(\"result\", \"\"))\n            if text:\n                print(f\"✅ 转写完成！文本长度: {len(text)} 字\")\n                print(\"--- 转写结果 ---\")\n                print(text[:500])\n            else:\n                print(\"❌ API 返回空文本\")\n        except Exception as e:\n            print(f\"❌ API 调用失败: {e}\")\n            print(\"   请检查网络连接和 API 配置\")\n```\n\n</details>\n\n<details>\n<summary>📋 模式 C：手动粘贴文本（零依赖保底，推荐）</summary>\n\n```python\nimport datetime\n\nprint(\"📋 模式 C：手动粘贴会议录音转写文本\")\nprint(\"   （如无 ASR 工具，可手动复制会议录音转写内容）\")\nprint(\"   输入文本后输入 END 结束：\")\nlines = []\nwhile True:\n    line = input()\n    line = line.strip() if line else \"\"\n    if line.upper() == \"END\":\n        break\n    lines.append(line)\ntext = \"\\n\".join(lines)\n\nif not"},{"path":"references/modules/calc.md","content":"# 📊 模块 1：数据换算\n\n---\n\n#### 1.1 房贷计算器\n\n**✅ 开箱即用** ｜ 🌐 [在线房贷计算器](https://www.zhujisuanqi.com/)\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport math\ntry:\n    P = float(input(\"贷款总额(万元): \") or 100) * 10000\n    n = int(float(input(\"贷款年限: \") or 30) * 12)\n    r = float(input(\"年利率(%): \") or 4.2) / 100 / 12\n    method = input(\"方式(等额本息/等额本金): \").strip() or \"等额本息\"\n    if n <= 0:\n        print(\"❌ 贷款年限必须大于0\")\n    elif r <= 0:\n        print(\"❌ 年利率必须大于0\")\n    elif method == \"等额本息\":\n        m = P*r*(1+r)**n / ((1+r)**n-1)\n        total = m * n\n        print(f\"月供: {m:.2f}元 | 总利息: {total-P:.2f}元 | 还款总额: {total:.2f}元\")\n    else:\n        tot = sum(P/n + (P-P*i/n)*r for i in range(n))\n        print(f\"首月: {P/n+P*r:.2f}元 | 末月: {P/n+r*(P/n):.2f}元 | 总利息: {tot-P:.2f}元\")\nexcept ValueError:\n    print(\"❌ 输入错误：请输入有效数字。例如：贷款100万输 100，年限30年输 30\")\n```\n\n</details>\n\n---\n\n#### 1.2 五险一金计算器 ⭐ v4.0.0 查表累进引擎\n\n**✅ 开箱即用** ｜ 🌐 [个税计算器](https://www.taxcalculator.com)\n\n> v4.0 重写计算引擎：数据与引擎分离，读取 `references/tax_2026.yaml` 数据驱动计算。支持 13 个城市，查表确定基数上下限，7 级超额累进个税。\n\n<details>\n<summary>📋 五险一金 + 个税计算器（查表 + 分段累进）</summary>\n\n```python\nimport os, yaml, math\n\ndef calc_insurance_tax(salary, city_code=\"beijing\"):\n    \"\"\"读取 tax_2026.yaml，查表+分段累进计算五险一金和个税\"\"\"\n    yaml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), \"references\", \"tax_2026.yaml\")\n    with open(yaml_path, encoding=\"utf-8\") as f:\n        data = yaml.safe_load(f)\n    \n    meta = data.get(\"meta\", {})\n    bases = data.get(\"social_insurance_bases\", {})\n    rates = data.get(\"insurance_rates\", {})\n    overrides = rates.get(\"overrides\", {})\n    tax_brackets = data.get(\"individual_income_tax\", {}).get(\"brackets\", [])\n    monthly_threshold = data.get(\"individual_income_tax\", {}).get(\"monthly_threshold\", 5000)\n    \n    if city_code not in bases:\n        city_name = city_code\n        supported = [f\"{v.get('name', k)} ({k})\" for k, v in bases.items()]\n        return None, f\"❌ 不支持的城市: {city_name}。支持: {', '.join(supported)}\"\n    \n    city_data = bases[city_code]\n    city_name = city_data.get(\"name\", city_code)\n    \n    city_overrides = overrides.get(city_code, {})\n    personal_rates = {**rates.get(\"default\", {}).get(\"personal\", {}), **city_overrides.get(\"personal\", {})}\n    company_rates = {**rates.get(\"default\", {}).get(\"company\", {}), **city_overrides.get(\"company\", {})}\n    \n    def get_base(insurance_type):\n        base_info = city_data.get(insurance_type, {})\n        lower = base_info.get(\"lower\", 0)\n        upper = base_info.get(\"upper\", float(\"inf\"))\n        return max(lower, min(salary, upper))\n    \n    personal_items = {}\n    personal_total = 0\n    for insurance_type in [\"pension\", \"medical\", \"unemployed\", \"injury\", \"maternity\"]:\n        base = get_base(insurance_type)\n        rate = personal_rates.get(insurance_type, 0)\n        amount = base * rate\n        if amount > 0:\n            names = {\"pension\": \"养老\", \"medical\": \"医疗\", \"unemployed\": \"失业\", \"injury\": \"工伤\", \"maternity\": \"生育\"}\n            personal_items[names.get(insuranc"},{"path":"references/modules/dev.md","content":"# 💻 模块 5：开发工具\n\n---\n\n#### 5.1 JSON 工具\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport json\ntry:\n    r = input(\"输入JSON:\").strip() or '{\"key\":\"value\"}'\n    if not r: print(\"❌ 没有输入\")\n    else:\n        p = json.loads(r)\n        print(f\"✅ 合法\\n格式化: {json.dumps(p,ensure_ascii=False,indent=2)}\")\n        print(f\"压缩: {json.dumps(p,ensure_ascii=False,separators=(',',':'))}\")\nexcept json.JSONDecodeError as e:\n    print(f\"❌ JSON错误: {e}\")\n```\n\n</details>\n\n---\n\n#### 5.2 HTTP 接口测试\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nimport urllib.request, socket, ssl\ntry:\n    u = input(\"URL:\").strip() or \"https://api.github.com\"\n    if not u: print(\"❌ URL不能为空\")\n    elif not u.startswith(\"http\"): print(\"❌ 应以 http:// 或 https:// 开头\")\n    else:\n        req = urllib.request.Request(u, headers={\"User-Agent\":\"dgngjx/3.5\"})\n        ctx = ssl.create_default_context()\n        ctx.check_hostname = False\n        resp = urllib.request.urlopen(req, timeout=15, context=ctx)\n        body = resp.read()\n        print(f\"✅ 状态: {resp.status} ({len(body)} bytes)\")\nexcept urllib.error.HTTPError as e:\n    print(f\"❌ HTTP {e.code}: {e.reason}\")\nexcept urllib.error.URLError as e:\n    print(f\"❌ 网络错误: {e.reason}\")\nexcept Exception as e:\n    print(f\"❌ {e}\")\n```\n\n</details>\n\n---\n\n#### 5.3 Token 计算器\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nt = input(\"文本:\") or \"计算Token数\"\nif not t.strip(): print(\"❌ 请输入文字\")\nelse:\n    cn = sum(1 for c in t if '一' <= c <= '鿿')\n    en = len(t) - cn\n    print(f\"中文: {cn} ≈{int(cn/1.5)} | 英文: {en} ≈{int(en/4)} | 总计 ≈{int(cn/1.5+en/4)}\")\n```\n\n</details>\n\n---\n\n#### 5.4 在线 Photoshop\n\n📦 **需 Pillow**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nfrom PIL import Image, ImageEnhance\ntry:\n    f = input(\"图片路径:\").strip().strip('\"').replace(\"\\\\\",\"/\") or \"photo.jpg\"\n    img = Image.open(f)\n    w,h = map(int, input(\"裁剪 宽,高:\").strip() or \"200,200\").split(\",\"))\n    if w > img.width or h > img.height: w,h = min(w,img.width), min(h,img.height)\n    img2 = img.crop(((img.width-w)//2,(img.height-h)//2,w+img.width//2,h+img.height//2))\n    img2.save(\"cropped.jpg\")\n    e = ImageEnhance.Brightness(img).enhance(float(input(\"亮度(1.2):\") or 1.2))\n    e.save(\"bright.jpg\")\n    print(\"✅ 已保存 cropped.jpg, bright.jpg\")\nexcept Exception as e: print(f\"❌ {e}\")\n```\n\n</details>\n\n---\n\n#### 5.5 Mermaid 时序图\n\n**✅ 开箱即用**\n\n<details>\n<summary>📋 展开查看命令</summary>\n\n```python\nseq = input(\"时序图代码:\").strip() or \"A->B: Hello\"\nif seq: print(f\"sequenceDiagram\\n{seq}\")\nelse: print(\"❌ 代码不能为空\")\n```\n\n</details>"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"多功能免费工具箱 - 图片处理、PDF转换、数据换算、文本工具、开发工具、视频工具、教育、生活娱乐、实用小工具、系统工具、AI办公。11大模块49个工具。v4.1 包化架构：SKILL.md 129KB→30KB + 11模块外置 + 四包机制（core/dev/ent/media）+ 首次体检报告。 Skill: dgngjx-skill Owner: fyniujin Summary: 多功能免费工具箱 - 图片处理、PDF转换、数据换算、文本工具、开发工具、视频工具、教育、生活娱乐、实用小工具、系统工具、AI办公。11大模块49个工具。v4.1 包化架构：SKILL.md 129KB→30KB + 11模块外置 + 四包机制（core/dev/ent/media）+ 首次体检报告。 Tags: latest:4.1.0 Version history: v4.1.0 | 2026-10-09T08:30:01.429Z | auto **v4.1.0 包化架构重大更新** - 全新包化架构：11大模块全部外置，SKILL.md 缩减至 30KB，支持四包机制（core/dev/ent/media）。 - 新增可选包管理：支持 --list-packs 查看、--install-pack 一键安装可选工具包。 - 支持首次","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1017,"uniquenessScore":47,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T17:17:30.035Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-10T17:17:30.035Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T21:55:34.569Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}