diff --git a/skills/auto-daily-summary/SKILL.md b/skills/auto-daily-summary/SKILL.md deleted file mode 100644 index bf61688..0000000 --- a/skills/auto-daily-summary/SKILL.md +++ /dev/null @@ -1,218 +0,0 @@ ---- -name: auto-daily-summary -description: Generate recurring summaries, daily reports, content digests, and concise action-oriented briefs from multiple inputs. Use when the user asks for daily summaries, periodic briefings, meeting digests, content condensation, or automated recurring report generation. 中文触发词包括:日报、周报、摘要、会议纪要、内容浓缩、自动汇总、每天发我一份总结。 ---- - -# Auto Daily Summary - -## Overview - -This skill converts scattered information into concise, structured summaries for recurring or one-off use. - -Typical scenarios: -- daily or weekly report generation -- long content condensation -- meeting or conversation summary -- multi-source digest -- action-item extraction - -This skill focuses on **organization and summarization**, not source retrieval itself. - -## Quick Start - -When the user asks for a summary or report: - -1. Identify the sources to summarize -2. Clarify the audience and desired level of detail -3. Determine whether the output is one-time or recurring -4. Summarize by theme, not by raw chronological dump unless requested -5. Extract action items and watch items when useful - -### 中文任务映射 -- “帮我整理成日报” → `daily_report` -- “做个周报/周总结” → `digest` 或 `daily_report` -- “把这段会议内容整理一下” → `meeting_digest` -- “浓缩成 3-5 条重点” → `digest` + `short` -- “每天早上发我一份总结” → `plan-recurring` + `schedule-job` - - -## Input Requirements - -| Field | Required | Description | -|-------|----------|-------------| -| source content | yes | Text, notes, messages, links, reports, logs, or mixed content | -| summary objective | yes | Inform, decide, archive, handoff, or monitor | -| audience | no | Self, team, manager, executive, customer | -| time scope | no | Today, this week, meeting duration, selected period | -| desired length | no | TL;DR, short, standard, detailed | -| output style | no | Daily report, digest, executive summary, bullet list | -| action extraction | no | Whether to extract todos, risks, blockers | - -## Workflow Decision Tree - -### Content Summary -Use when the user wants a concise summary of a long input. - -### Daily / Weekly Report -Use when the user wants a periodic report with sections and status updates. - -### Meeting Digest -Use when the user wants decisions, action items, and blockers from a discussion. - -### Recurring Summary Workflow -Use when the user wants this to happen on a schedule. In that case, pair with `schedule-job`. - -## Instructions - -### Step 1: Identify source boundaries -Clarify what should and should not be included in the summary. - -### Step 2: Determine the correct abstraction level -Choose the right level for the audience: -- executive audience -> implications and decisions -- working team -> concrete tasks and blockers -- archive -> structured factual recap - -### Step 3: Group by theme -Prefer grouping by: -- progress -- decisions -- blockers -- risks -- next steps - -Avoid copying source order unless chronology itself matters. - -### Step 4: Extract action items -When appropriate, identify: -- owner -- task -- due timing -- dependency or blocker - -If ownership is unclear, say so. - -### Step 5: Prepare for automation if needed -If the user wants recurring output: -- use `schedule-job` for cadence -- use `imap-smtp-email` or other enabled notification skills for delivery - -## Scripts - -### CLI Usage - -Use the following commands when you need stable structured outputs: - -```bash -poetry run python skills/auto-daily-summary/scripts/summary_cli.py validate --input-json '' -poetry run python skills/auto-daily-summary/scripts/summary_cli.py run --input-json '' --output json -poetry run python skills/auto-daily-summary/scripts/summary_cli.py plan-recurring --input-json '' -``` - -### Recommended Uses -- `validate` - check whether the summary request payload is complete -- `run` - generate summary JSON and markdown -- `plan-recurring` - generate a schedule-ready message payload for `schedule-job` - - -### Daily Report -```markdown -# Daily Report - -## Summary -[Short summary] - -## Key Updates -- [Update] - -## Decisions -- [Decision] - -## Risks / Blockers -- [Risk or blocker] - -## Next Actions -- [Action] -``` - -### Content Digest -```markdown -# Content Digest - -## TL;DR -[Very short summary] - -## Main Themes -### 1. [Theme] -- [Key point] - -## Notable Details -- [Detail] - -## Follow-up -- [Suggested follow-up] -``` - -## Quality Checklist - -Before finalizing, verify: -- the summary matches the audience level -- repetition and noise are removed -- key decisions are not buried -- action items are explicit when relevant -- uncertainty is preserved rather than flattened away -- the result is shorter and clearer than the source material - -## Fallback Strategy - -If the input is too fragmented: -- produce a partial summary by theme -- list gaps or unclear areas -- ask for additional source material only if needed for the user’s stated goal - -## Related Skills - -- `skills/schedule-job/SKILL.md` - automate recurring execution -- `skills/imap-smtp-email/SKILL.md` - send summaries via email -- `skills/market-academic-insight/SKILL.md` - use when the task is deeper research synthesis rather than pure summarization -- `skills/competitor-news-intel/SKILL.md` - use when competitor monitoring and intelligence is the real task - -## Examples - -**User**: "帮我把今天的工作内容整理成日报" - -Expected output: -- summary -- key updates -- blockers -- next actions - -**User**: "把这篇长文浓缩成 5 条重点" - -Expected output: -- TL;DR -- 5 concise points -- optional follow-up note - -**User**: "每天早上自动给我发新闻摘要" - -Expected output: -- summary format definition -- recommendation to combine with `schedule-job` -- delivery method confirmation - -**User**: "把这段会议记录整理成会议纪要" - -Expected output: -- summary -- decisions -- action items -- blockers if any - -**User**: "给我做个今天的三段式总结" - -Expected output: -- summary -- key updates -- next actions - diff --git a/skills/auto-daily-summary/scripts/summary_cli.py b/skills/auto-daily-summary/scripts/summary_cli.py deleted file mode 100644 index c055af9..0000000 --- a/skills/auto-daily-summary/scripts/summary_cli.py +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import json -import sys -from datetime import datetime, UTC - -from summary_core import build_summary, validate_payload - - -ERROR_TEMPLATE = { - "success": False, - "code": "invalid_input", - "message": "", - "data": {}, - "meta": {}, - "errors": [], -} - - -def _now_iso() -> str: - return datetime.now(UTC).isoformat() - - -def _emit_json(data: dict, pretty: bool, stream=None): - print(json.dumps(data, ensure_ascii=False, indent=2 if pretty else None), file=stream or sys.stdout) - - -def _error_response(code: str, message: str, errors: list[str] | None = None) -> dict: - return { - **ERROR_TEMPLATE, - "code": code, - "message": message, - "meta": {"generated_at": _now_iso()}, - "errors": errors or [], - } - - -def _parse_bool(value: str | None) -> bool | None: - if value is None: - return None - lowered = value.lower() - if lowered in {"1", "true", "yes", "y"}: - return True - if lowered in {"0", "false", "no", "n"}: - return False - raise ValueError(f"invalid boolean value: {value}") - - -def _load_payload(raw: str) -> dict: - return json.loads(raw) - - -def _apply_overrides(payload: dict, args: argparse.Namespace) -> dict: - payload.setdefault("data", {}) - if args.lang: - payload["language"] = args.lang - if args.style: - payload["data"]["style"] = args.style - if args.length: - payload["data"]["length"] = args.length - if hasattr(args, "extract_actions"): - extract_actions = _parse_bool(getattr(args, "extract_actions", None)) - if extract_actions is not None: - payload["data"]["extract_actions"] = extract_actions - if hasattr(args, "extract_risks"): - extract_risks = _parse_bool(getattr(args, "extract_risks", None)) - if extract_risks is not None: - payload["data"]["extract_risks"] = extract_risks - return payload - - -def cmd_validate(args: argparse.Namespace): - payload = _apply_overrides(_load_payload(args.input_json), args) - errors = validate_payload(payload) - result = { - "success": not errors, - "code": "ok" if not errors else "invalid_input", - "message": "payload valid" if not errors else "payload invalid", - "data": {"valid": not errors}, - "meta": {"generated_at": _now_iso()}, - "errors": errors, - } - target_stream = sys.stdout if not errors else sys.stderr - _emit_json(result, args.pretty, target_stream) - if errors: - raise SystemExit(1) - - -def cmd_run(args: argparse.Namespace): - payload = _apply_overrides(_load_payload(args.input_json), args) - errors = validate_payload(payload) - if errors: - _emit_json(_error_response("invalid_input", "payload invalid", errors), args.pretty, sys.stderr) - raise SystemExit(1) - data = build_summary(payload) - if args.output == "markdown": - print(data["markdown"]) - return - result = { - "success": True, - "code": "ok", - "message": "summary generated", - "data": data, - "meta": { - "generated_at": _now_iso(), - "source_count": len(payload.get("data", {}).get("sources", [])), - }, - "errors": [], - } - _emit_json(result, args.pretty) - - -def cmd_plan_recurring(args: argparse.Namespace): - payload = _apply_overrides(_load_payload(args.input_json), args) - errors = validate_payload(payload) - if errors: - _emit_json(_error_response("invalid_input", "payload invalid", errors), args.pretty, sys.stderr) - raise SystemExit(1) - data = build_summary(payload) - result = { - "success": True, - "code": "ok", - "message": "recurring plan generated", - "data": {"schedule_payload": data["schedule_payload"]}, - "meta": {"generated_at": _now_iso()}, - "errors": [], - } - _emit_json(result, args.pretty) - - -def build_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser(description="Generate structured summaries") - subparsers = parser.add_subparsers(dest="command", required=True) - for name in ["validate", "run", "plan-recurring"]: - sub = subparsers.add_parser(name) - sub.add_argument("--input-json", required=True) - sub.add_argument("--lang") - sub.add_argument("--style") - sub.add_argument("--length") - sub.add_argument("--extract-actions") - sub.add_argument("--extract-risks") - sub.add_argument("--pretty", action="store_true") - if name == "run": - sub.add_argument("--output", choices=["json", "markdown"], default="json") - subparsers.choices["validate"].set_defaults(func=cmd_validate) - subparsers.choices["run"].set_defaults(func=cmd_run) - subparsers.choices["plan-recurring"].set_defaults(func=cmd_plan_recurring) - return parser - - -if __name__ == "__main__": - parser = build_parser() - args = parser.parse_args() - try: - args.func(args) - except json.JSONDecodeError as exc: - _emit_json(_error_response("invalid_input", f"invalid json: {exc}", [str(exc)]), getattr(args, "pretty", False), sys.stderr) - raise SystemExit(1) - except ValueError as exc: - _emit_json(_error_response("invalid_input", str(exc), [str(exc)]), getattr(args, "pretty", False), sys.stderr) - raise SystemExit(1) - except Exception as exc: - _emit_json(_error_response("internal_error", "unexpected error", [str(exc)]), getattr(args, "pretty", False), sys.stderr) - raise SystemExit(1) diff --git a/skills/auto-daily-summary/scripts/summary_core.py b/skills/auto-daily-summary/scripts/summary_core.py deleted file mode 100644 index ddc2186..0000000 --- a/skills/auto-daily-summary/scripts/summary_core.py +++ /dev/null @@ -1,228 +0,0 @@ -import re -from collections import Counter -from typing import Any - -SUMMARY_LENGTH_LIMITS = { - "tldr": 2, - "short": 4, - "standard": 6, - "detailed": 10, -} - -ACTION_PREFIX_PATTERNS = [ - r"^(?:TODO|待办|action)[::]?\s*(.+)$", - r"^(?:需要|跟进)\s*(.+)$", -] - -RISK_KEYWORDS = { - "high": ["阻塞", "blocker", "故障", "失败", "严重", "不可用"], - "medium": ["风险", "延迟", "异常", "超时", "报错"], - "low": ["提醒", "注意", "观察", "待确认"], -} - -BOOL_FIELDS = ["extract_actions", "extract_risks"] -VALID_STYLES = {"daily_report", "digest", "meeting_digest", "executive"} -VALID_LENGTHS = {"tldr", "short", "standard", "detailed"} - - -def _clean_text(text: str) -> str: - return re.sub(r"\s+", " ", (text or "").strip()) - - -def _normalize_key(text: str) -> str: - text = _clean_text(text).lower() - return re.sub(r"[^\w\u4e00-\u9fff]+", "", text) - - -def _split_sentences(text: str) -> list[str]: - raw_parts = re.split(r"[。!?;.!?;]+|\n+", text) - return [_clean_text(part) for part in raw_parts if _clean_text(part)] - - -def _sentence_tokens(sentence: str) -> list[str]: - return re.findall(r"[A-Za-z0-9_-]+|[\u4e00-\u9fff]{2,}", sentence.lower()) - - -def _top_sentences(texts: list[str], limit: int) -> list[str]: - sentences: list[str] = [] - for text in texts: - sentences.extend(_split_sentences(text)) - - if not sentences: - return [] - - token_counter = Counter() - for sentence in sentences: - token_counter.update(_sentence_tokens(sentence)) - - scored: list[tuple[int, int, str]] = [] - for index, sentence in enumerate(sentences): - score = sum(token_counter[token] for token in _sentence_tokens(sentence)) - scored.append((score, -index, sentence)) - - ranked = [sentence for _, _, sentence in sorted(scored, reverse=True)] - unique_ranked = [] - seen = set() - for sentence in ranked: - key = _normalize_key(sentence) - if key and key not in seen: - seen.add(key) - unique_ranked.append(sentence) - if len(unique_ranked) >= limit: - break - return unique_ranked - - -def _trim_fragment(text: str, max_length: int = 80) -> str: - fragment = re.split(r"[,,。;;!?]", text, maxsplit=1)[0] - fragment = _clean_text(fragment) - return fragment[:max_length].strip() - - -def _extract_actions(texts: list[str]) -> list[dict[str, Any]]: - items: list[dict[str, Any]] = [] - for text in texts: - for sentence in _split_sentences(text): - for pattern in ACTION_PREFIX_PATTERNS: - match = re.match(pattern, sentence, re.IGNORECASE) - if not match: - continue - task = _trim_fragment(match.group(1)) - if len(task) < 2: - continue - items.append({"task": task, "owner": None, "due_at": None, "blocker": None}) - break - dedup = [] - seen = set() - for item in items: - key = _normalize_key(item["task"]) - if key and key not in seen: - seen.add(key) - dedup.append(item) - return dedup[:10] - - -def _extract_risks(texts: list[str]) -> list[dict[str, Any]]: - risks: list[dict[str, Any]] = [] - for text in texts: - for sentence in _split_sentences(text): - lowered = sentence.lower() - for impact, keywords in RISK_KEYWORDS.items(): - matched = next((keyword for keyword in keywords if keyword.lower() in lowered), None) - if not matched: - continue - start = max(0, lowered.find(matched.lower()) - 18) - end = min(len(sentence), lowered.find(matched.lower()) + len(matched) + 30) - fragment = _clean_text(sentence[start:end]) - fragment = fragment[:120] - if len(fragment) < 2: - continue - risks.append({"risk": fragment, "impact": impact, "mitigation": None}) - break - dedup = [] - seen = set() - for item in risks: - key = _normalize_key(item["risk"]) - if key and key not in seen: - seen.add(key) - dedup.append(item) - return dedup[:10] - - -def _build_summary_line(sentences: list[str]) -> str: - if not sentences: - return "暂无可提炼的关键信息。" - selected = sentences[:2] - return ";".join(selected) - - -def build_summary(payload: dict[str, Any]) -> dict[str, Any]: - data = payload.get("data", {}) - sources = data.get("sources", []) - texts = [_clean_text(source.get("content", "")) for source in sources if _clean_text(source.get("content", ""))] - length = data.get("length", "standard") - style = data.get("style", "daily_report") - limit = SUMMARY_LENGTH_LIMITS.get(length, SUMMARY_LENGTH_LIMITS["standard"]) - top_sentences = _top_sentences(texts, limit) - - summary_line = _build_summary_line(top_sentences) - summary_keys = {_normalize_key(sentence) for sentence in top_sentences[:2]} - detail_sentences = [sentence for sentence in top_sentences if _normalize_key(sentence) not in summary_keys] - - sections = [] - if detail_sentences: - if len(detail_sentences) == 1: - sections = [{"title": "Key Updates", "bullets": detail_sentences}] - else: - midpoint = max(1, len(detail_sentences) // 2) - sections = [ - {"title": "Key Updates", "bullets": detail_sentences[:midpoint]}, - {"title": "Notable Details", "bullets": detail_sentences[midpoint:]}, - ] - - action_items = _extract_actions(texts) if data.get("extract_actions") else [] - risk_items = _extract_risks(texts) if data.get("extract_risks") else [] - - markdown_lines = ["# Summary", "", "## Summary", f"- {summary_line}"] - for section in sections: - if not section["bullets"]: - continue - markdown_lines.extend(["", f"## {section['title']}"]) - markdown_lines.extend(f"- {bullet}" for bullet in section["bullets"]) - if action_items: - markdown_lines.extend(["", "## Action Items"]) - markdown_lines.extend(f"- {item['task']}" for item in action_items) - if risk_items: - markdown_lines.extend(["", "## Risks"]) - markdown_lines.extend(f"- [{item['impact']}] {item['risk']}" for item in risk_items) - - schedule_payload = { - "suggested_name": "Daily Summary", - "message": "[Scheduled Task Triggered] 请立即汇总最新内容并输出结构化摘要,如有行动项和风险请一并列出,然后选择合适的通知方式发送给用户。", - } - - return { - "summary": summary_line, - "sections": sections, - "action_items": action_items, - "risk_items": risk_items, - "markdown": "\n".join(markdown_lines), - "schedule_payload": schedule_payload, - "style": style, - } - - -def _validate_source(source: Any, index: int) -> list[str]: - errors = [] - if not isinstance(source, dict): - return [f"data.sources[{index}] must be an object"] - if not _clean_text(str(source.get("content", ""))): - errors.append(f"data.sources[{index}].content is required") - return errors - - -def validate_payload(payload: dict[str, Any]) -> list[str]: - errors = [] - data = payload.get("data") - if not isinstance(data, dict): - return ["data must be an object"] - sources = data.get("sources") - if not isinstance(sources, list) or not sources: - errors.append("data.sources must be a non-empty array") - else: - for index, source in enumerate(sources): - errors.extend(_validate_source(source, index)) - objective = data.get("objective") - if not isinstance(objective, str) or not objective.strip(): - errors.append("data.objective is required") - style = data.get("style") - if style is not None and style not in VALID_STYLES: - errors.append(f"data.style must be one of {sorted(VALID_STYLES)}") - length = data.get("length") - if length is not None and length not in VALID_LENGTHS: - errors.append(f"data.length must be one of {sorted(VALID_LENGTHS)}") - for field in BOOL_FIELDS: - value = data.get(field) - if value is not None and not isinstance(value, bool): - errors.append(f"data.{field} must be a boolean") - return errors diff --git a/skills/baidu-search/.claude-plugin/plugin.json b/skills/linggan/baidu-search/.claude-plugin/plugin.json similarity index 100% rename from skills/baidu-search/.claude-plugin/plugin.json rename to skills/linggan/baidu-search/.claude-plugin/plugin.json diff --git a/skills/baidu-search/SKILL.md b/skills/linggan/baidu-search/SKILL.md similarity index 100% rename from skills/baidu-search/SKILL.md rename to skills/linggan/baidu-search/SKILL.md diff --git a/skills/baidu-search/scripts/search.py b/skills/linggan/baidu-search/scripts/search.py similarity index 100% rename from skills/baidu-search/scripts/search.py rename to skills/linggan/baidu-search/scripts/search.py diff --git a/skills/bot-self-modifier/SKILL.md b/skills/linggan/bot-self-modifier/SKILL.md similarity index 100% rename from skills/bot-self-modifier/SKILL.md rename to skills/linggan/bot-self-modifier/SKILL.md diff --git a/skills/bot-self-modifier/scripts/bot_modifier.py b/skills/linggan/bot-self-modifier/scripts/bot_modifier.py similarity index 100% rename from skills/bot-self-modifier/scripts/bot_modifier.py rename to skills/linggan/bot-self-modifier/scripts/bot_modifier.py diff --git a/skills/bot-self-modifier/skill.yaml b/skills/linggan/bot-self-modifier/skill.yaml similarity index 100% rename from skills/bot-self-modifier/skill.yaml rename to skills/linggan/bot-self-modifier/skill.yaml diff --git a/skills/caiyun-weather/SKILL.md b/skills/linggan/caiyun-weather/SKILL.md similarity index 100% rename from skills/caiyun-weather/SKILL.md rename to skills/linggan/caiyun-weather/SKILL.md diff --git a/skills/caiyun-weather/_meta.json b/skills/linggan/caiyun-weather/_meta.json similarity index 100% rename from skills/caiyun-weather/_meta.json rename to skills/linggan/caiyun-weather/_meta.json diff --git a/skills/caiyun-weather/scripts/caiyun_weather.py b/skills/linggan/caiyun-weather/scripts/caiyun_weather.py similarity index 100% rename from skills/caiyun-weather/scripts/caiyun_weather.py rename to skills/linggan/caiyun-weather/scripts/caiyun_weather.py diff --git a/skills/competitor-news-intel/SKILL.md b/skills/linggan/competitor-news-intel/SKILL.md similarity index 100% rename from skills/competitor-news-intel/SKILL.md rename to skills/linggan/competitor-news-intel/SKILL.md diff --git a/skills/competitor-news-intel/scripts/intel_cli.py b/skills/linggan/competitor-news-intel/scripts/intel_cli.py similarity index 100% rename from skills/competitor-news-intel/scripts/intel_cli.py rename to skills/linggan/competitor-news-intel/scripts/intel_cli.py diff --git a/skills/competitor-news-intel/scripts/intel_core.py b/skills/linggan/competitor-news-intel/scripts/intel_core.py similarity index 100% rename from skills/competitor-news-intel/scripts/intel_core.py rename to skills/linggan/competitor-news-intel/scripts/intel_core.py diff --git a/skills/competitor-news-intel/scripts/search_provider.py b/skills/linggan/competitor-news-intel/scripts/search_provider.py similarity index 100% rename from skills/competitor-news-intel/scripts/search_provider.py rename to skills/linggan/competitor-news-intel/scripts/search_provider.py diff --git a/skills/contract-document-generator/SKILL.md b/skills/linggan/contract-document-generator/SKILL.md similarity index 100% rename from skills/contract-document-generator/SKILL.md rename to skills/linggan/contract-document-generator/SKILL.md diff --git a/skills/financial-report-generator/SKILL.md b/skills/linggan/financial-report-generator/SKILL.md similarity index 100% rename from skills/financial-report-generator/SKILL.md rename to skills/linggan/financial-report-generator/SKILL.md diff --git a/skills/market-academic-insight/SKILL.md b/skills/linggan/market-academic-insight/SKILL.md similarity index 100% rename from skills/market-academic-insight/SKILL.md rename to skills/linggan/market-academic-insight/SKILL.md diff --git a/skills/ragflow-loader/.claude-plugin/plugin.json b/skills/linggan/ragflow-loader/.claude-plugin/plugin.json similarity index 100% rename from skills/ragflow-loader/.claude-plugin/plugin.json rename to skills/linggan/ragflow-loader/.claude-plugin/plugin.json diff --git a/skills/ragflow-loader/hooks/pre_prompt.py b/skills/linggan/ragflow-loader/hooks/pre_prompt.py similarity index 100% rename from skills/ragflow-loader/hooks/pre_prompt.py rename to skills/linggan/ragflow-loader/hooks/pre_prompt.py diff --git a/skills/sales-decision-report/SKILL.md b/skills/linggan/sales-decision-report/SKILL.md similarity index 100% rename from skills/sales-decision-report/SKILL.md rename to skills/linggan/sales-decision-report/SKILL.md diff --git a/skills/seedream/SKILL.md b/skills/linggan/seedream/SKILL.md similarity index 100% rename from skills/seedream/SKILL.md rename to skills/linggan/seedream/SKILL.md diff --git a/skills/seedream/scripts/generate_image.py b/skills/linggan/seedream/scripts/generate_image.py similarity index 100% rename from skills/seedream/scripts/generate_image.py rename to skills/linggan/seedream/scripts/generate_image.py diff --git a/skills/seedream/scripts/generate_video.py b/skills/linggan/seedream/scripts/generate_video.py similarity index 100% rename from skills/seedream/scripts/generate_video.py rename to skills/linggan/seedream/scripts/generate_video.py diff --git a/skills/static-site-deploy/SKILL.md b/skills/linggan/static-site-deploy/SKILL.md similarity index 100% rename from skills/static-site-deploy/SKILL.md rename to skills/linggan/static-site-deploy/SKILL.md diff --git a/skills/static-site-deploy/scripts/deploy.sh b/skills/linggan/static-site-deploy/scripts/deploy.sh similarity index 100% rename from skills/static-site-deploy/scripts/deploy.sh rename to skills/linggan/static-site-deploy/scripts/deploy.sh diff --git a/skills/static-site-deploy/scripts/download.sh b/skills/linggan/static-site-deploy/scripts/download.sh similarity index 100% rename from skills/static-site-deploy/scripts/download.sh rename to skills/linggan/static-site-deploy/scripts/download.sh diff --git a/skills/static-site-deploy/scripts/list.sh b/skills/linggan/static-site-deploy/scripts/list.sh similarity index 100% rename from skills/static-site-deploy/scripts/list.sh rename to skills/linggan/static-site-deploy/scripts/list.sh diff --git a/skills/static-site-deploy/scripts/read.sh b/skills/linggan/static-site-deploy/scripts/read.sh similarity index 100% rename from skills/static-site-deploy/scripts/read.sh rename to skills/linggan/static-site-deploy/scripts/read.sh diff --git a/skills/static-site-deploy/scripts/static-site-deploy.yml b/skills/linggan/static-site-deploy/scripts/static-site-deploy.yml similarity index 100% rename from skills/static-site-deploy/scripts/static-site-deploy.yml rename to skills/linggan/static-site-deploy/scripts/static-site-deploy.yml diff --git a/skills/voice-notification/SKILL.md b/skills/linggan/voice-notification/SKILL.md similarity index 100% rename from skills/voice-notification/SKILL.md rename to skills/linggan/voice-notification/SKILL.md diff --git a/skills/voice-notification/scripts/voice_notify.py b/skills/linggan/voice-notification/scripts/voice_notify.py similarity index 100% rename from skills/voice-notification/scripts/voice_notify.py rename to skills/linggan/voice-notification/scripts/voice_notify.py diff --git a/skills/weather-china/SKILL.md b/skills/linggan/weather-china/SKILL.md similarity index 100% rename from skills/weather-china/SKILL.md rename to skills/linggan/weather-china/SKILL.md diff --git a/skills/weather-china/_meta.json b/skills/linggan/weather-china/_meta.json similarity index 100% rename from skills/weather-china/_meta.json rename to skills/linggan/weather-china/_meta.json diff --git a/skills/weather-china/lib/weather_cn.py b/skills/linggan/weather-china/lib/weather_cn.py similarity index 100% rename from skills/weather-china/lib/weather_cn.py rename to skills/linggan/weather-china/lib/weather_cn.py