Skill: sunxq1017-hash/scheduled-voice-briefing (version 1.0.4)

--- name: scheduled-voice-briefing description: General-purpose skill for turning natural language requests into scheduled voice notifications and structured briefings. Use when the user wants to create, update, pause, resume, or remove recurring or one-time voice briefing tasks; configure timing, modules, language, voice, or tone in natural language; or manage scheduled spoken summaries such as reminders and daily briefings. This public package focuses on configuration, structure, runtime integration guidance, and production-like examples. It does not bundle provider-specific TTS implementations, does not include built-in API credentials, and does not access local files, system resources, or user data without explicit input. / 通用型 Skill，用于将自然语言请求转换为定时语音通知与结构化播报。适用于创建、修改、暂停、恢复或删除一次性或周期性语音播报任务，并支持通过自然语言配置时间、模块、语言、声音与语气。本公开包聚焦于配置结构、运行时集成说明和更接近真实使用场景的示例，不内置厂商特定的 TTS 实现，不包含内置 API 凭证，也不会在未明确输入的情况下访问本地文件、系统资源或用户数据。 --- # Scheduled Voice Briefing / 定时语音播报 Turn natural language requests into structured scheduled voice notifications and briefings. 将自然语言请求转换为结构化的定时语音通知与播报内容。 ## Overview / 概述 Scheduled Voice Briefing is a general-purpose Skill for scheduled spoken notifications and information briefings. Users can define when to run, what content to include, and how it should sound through natural language instructions. Scheduled Voice Briefing 是一个通用 Skill，用于定时语音通知与信息播报。用户可以通过自然语言设置执行时间、播报内容，以及语言、声音与语气。 Morning briefings are one example use case. This skill can also be used for scheduled reminders, status summaries, and other spoken notifications. 晨间播报只是示例场景之一，本 Skill 同样可用于定时提醒、状态摘要和其他语音通知场景。 ## Core capabilities / 核心能力 This skill supports: - natural language configuration of scheduled tasks - creation and management of recurring and one-time tasks - modular content generation such as environment or schedule summaries - language, voice, and tone customization - clear and explainable configuration updates - optional staged reminder or notification sequences 本 Skill 支持： - 自然语言配置定时任务 - 创建与管理周期性或一次性任务 - 模块化内容生成，例如环境摘要或日程摘要 - 语言、声音与语气控制 - 对配置变更给出清晰且可解释的说明 - 可选的分阶段提醒 / 通知序列设计 ## Runtime integration / 运行时集成 This skill is environment-agnostic and does not depend on a specific operating system. 本 Skill 不依赖特定操作系统，对运行环境保持兼容。 Recommended runtime fields: - `provider` - `voice` - `rate` - `volume` - `tone` - `contextText` - `fallback` - `playbackMode` 建议的运行时字段： - `provider` - `voice` - `rate` - `volume` - `tone` - `contextText` - `fallback` - `playbackMode` The public package does not bundle provider-specific credential handling or vendor-specific playback logic. Any local voice engine, external TTS, or playback runtime must be provided by the runtime environment. 本公开包不内置厂商特定的凭证处理或播放逻辑。任何本地语音引擎、外接 TTS 或播放运行时都应由运行环境自行提供。 ## Safety and execution boundaries / 安全与执行边界 - This skill does not execute arbitrary system commands. - This skill does not include or require built-in API credentials. - Any external services, such as text-to-speech, must be provided and configured by the runtime environment. - This skill does not access local files, system resources, or user data without explicit input. - Any external data, such as calendar or weather information, must be explicitly provided by the user or the runtime environment. - 本 Skill 不执行任意系统命令。 - 本 Skill 不内置或要求提供任何 API 凭证。 - 外部服务（如语音合成）必须由运行环境显式提供和配置。 - 本 Skill 不会在未明确输入的情况下访问本地文件、系统资源或用户数据。 - 所有外部数据（如日历、天气）必须由用户或运行环境显式提供。 ## Emotional style truth / 情绪表达说明 This skill can describe tone, pacing, and emotional intent at the configuration level. However, actual expressive quality depends on the runtime voice engine. Rich emotional playback is best achieved with stronger TTS providers. 本 Skill 可以在配置层描述语气、节奏与情绪意图，但实际表达效果取决于运行环境中的语音引擎。更丰富的情绪表现通常需要更强的 TTS 提供方支持。 ## Content model / 内容模型 Suggested module types: - `opening` - `transition` - `environment_brief` - `schedule_brief` - `closing` 建议模块类型： - `opening`（开场） - `transition`（过渡） - `environment_brief`（环境摘要） - `schedule_brief`（日程摘要） - `closing`（收尾） Rules: - Keep output bounded and relevant to enabled modules. - Prefer short, structured summaries. - Degrade gracefully if optional data sources are unavailable. - Ask for clarification when timing or scope is ambiguous. 规则： - 输出应受约束，并与启用模块强相关。 - 优先使用简短、结构化摘要。 - 当可选数据源不可用时应平稳降级。 - 当时间或作用范围存在歧义时，应请求澄清。 ## Production-like examples / 更接近真实使用的示例 Examples of supported use cases: - weekday morning voice briefing - one-time reminder for tomorrow - evening wrap-up reminder - staged reminder flow before a daily briefing 支持的使用场景示例： - 工作日晨间语音播报 - 明天的一次性提醒 - 晚间收尾提醒 - 在正式播报前增加分阶段提醒序列 ## Explainability requirement / 可解释性要求 Whenever configuration changes, return a short explanation such as: ```text Updated configuration: - Time: weekdays 07:30 - Modules: environment_brief, schedule_brief - Tone: calm - Override: none ``` 每当配置发生变化时，应返回简短且可解释的说明，例如： ```text 已更新配置： - 时间：工作日 07:30 - 模块：environment_brief, schedule_brief - 语气：平静 - 覆盖：无 ``` ## Design boundaries / 设计边界 This skill should not: - hard-code a fixed persona - bundle third-party music or audio assets - assume a specific operating system - rely on undeclared secrets or hidden runtime requirements - generate long unbounded content unrelated to configured modules - ship private wake lines or user-specific language in the public package 本 Skill 不应： - 写死固定人格设定 - 打包第三方音乐或音频资源 - 假定特定操作系统 - 依赖未声明的密钥或隐藏的运行条件 - 生成与模块无关的长篇无限制文本 - 在公共包中携带用户私有台词或个性化表达 ## Resources / 资源说明 - `references/config-schema.md` describes the suggested config structure and runtime contract. - `references/briefing-templates.md` provides higher-quality wording and pacing guidance. - `scripts/update_schedule.py` shows one way to convert natural language updates into config changes. - `scripts/build_briefing.py` shows one way to assemble bounded briefing text from configured modules. - `references/config-schema.md`：建议配置结构与运行时集成说明。 - `references/briefing-templates.md`：更接近真实使用的文案与节奏建议。 - `scripts/update_schedule.py`：展示一种将自然语言更新转换为配置修改的方法。 - `scripts/build_briefing.py`：展示一种基于模块配置生成受约束播报文本的方法。

path: _meta.json
content: ownerId: kn73r8yfzp9wde6ctshrbe8e71838d66
slug: scheduled-voice-briefing
version: 1.0.2
publishedAt: 1774700050302

path: references/briefing-templates.md
content: # Briefing Templates / 播报模板建议

## Goal / 目标

Generate spoken summaries that feel structured, natural, and useful in real usage.  
生成结构清晰、自然顺耳、在真实场景中真正有用的播报内容。

## Shared style rules / 共通风格规则

- Keep the content bounded.
- Prefer short spoken sentences.
- Keep transitions smooth.
- Avoid robotic parameter readouts.
- If data is unavailable, degrade gracefully.

- 保持内容受约束。
- 优先使用适合口播的短句。
- 过渡尽量自然。
- 避免把内部参数直接念出来。
- 当数据不可用时平稳降级。

---

## 1. Scheduled notification mode / 定时通知模式

### English example
> Here is your scheduled notification. I will begin in a calm and clear way. Let me first share today’s weather, and then I will walk you through the key things ahead.

### 中文示例
> 现在开始为你播报定时通知。我会用平稳清晰的方式开始。先跟你说说今天的天气，再把接下来重要的事项讲给你听。

### Notes / 说明
- Suitable for scheduled notifications with a gentle tone.
- Can be combined with staged reminder sequences.

---

## 2. Daily briefing mode / 每日播报模式

### English example
> Good morning. Here is your short briefing for today. First, the weather. Then, the most important items on your schedule.

### 中文示例
> 早上好，给你一个简短的今日播报。先说天气，再说今天最重要的几项安排。

### Notes / 说明
- Best for structured weekday briefings.
- Should sound efficient but not cold.

---

## 3. Reminder mode / 提醒模式

### English example
> Just a reminder: you have an important task coming up soon. Please take a quick look when you are ready.

### 中文示例
> 提醒你一下，接下来有一件比较重要的事情要处理。方便的时候看一眼就好。

### Notes / 说明
- Keep it short.
- Prioritize clarity over richness.

---

## 4. Gentle reminder mode / 柔和提醒模式

### English example
> Whenever you are ready, I can walk you through the next step.

### 中文示例
> 你准备好了的话，我就陪你把下一步慢慢过一遍。

### Notes / 说明
- Useful for softer reminder-style interactions.
- Better with gentler tone and slower pacing.

---

## Suggested progression / 建议节奏

1. Opening / 开场
2. Transition / 过渡
3. Main information / 主信息
4. Optional reminder / 可选提醒
5. Closing / 收尾

## Style-level options / 风格层级

- `short`: concise and task-focused / 简洁、偏任务型
- `normal`: balanced and practical / 平衡、实用
- `rich`: more natural and expressive / 更自然、更有表现力


path: references/config-schema.md
content: # Config Schema / 配置结构建议

Suggested config location:
- `config/scheduled-voice-briefing.json`

## Runtime contract / 运行时约定

Recommended runtime fields:
- `provider`: runtime-provided voice backend name
- `voice`: abstract voice choice or runtime-specific voice name
- `rate`: speaking speed
- `volume`: playback volume
- `tone`: style hint such as calm / energetic / gentle
- `contextText`: richer style prompt for the runtime voice layer
- `fallback`: fallback playback strategy if the preferred provider is unavailable
- `playbackMode`: batch / staged / streaming

建议的运行时字段：
- `provider`：运行环境提供的语音后端名称
- `voice`：抽象 voice 选择或运行时特定 voice 名称
- `rate`：语速
- `volume`：播放音量
- `tone`：风格提示，例如 calm / energetic / gentle
- `contextText`：提供给语音层的更详细风格提示
- `fallback`：首选 provider 不可用时的降级策略
- `playbackMode`：batch / staged / streaming

## Example A — Scheduled weekday briefing / 示例 A：工作日定时播报

```json
{
  "enabled": true,
  "timezone": "UTC",
  "speaker": {
    "language": "zh",
    "voiceType": "female",
    "tone": "gentle",
    "runtime": {
      "provider": "runtime-local-voice",
      "voice": "default-female",
      "rate": 180,
      "volume": 1.0,
      "contextText": "温和、清晰、适合定时通知播报",
      "fallback": "text-only",
      "playbackMode": "batch"
    }
  },
  "schedules": [
    {
      "id": "scheduled-briefing",
      "enabled": true,
      "days": [1, 2, 3, 4, 5],
      "time": "08:00",
      "modules": ["opening", "environment_brief", "schedule_brief", "closing"],
      "style": {"length": "short", "variability": "medium"}
    }
  ]
}
```

## Example B — One-time reminder override / 示例 B：一次性提醒覆盖

```json
{
  "override": {
    "enabled": true,
    "date": "2026-03-30",
    "time": "08:30",
    "modules": ["opening", "closing"],
    "message": "记得带上资料，出门前再检查一次。"
  }
}
```

## Example C — Multi-slot daily schedules / 示例 C：多时段播报

```json
{
  "schedules": [
    {
      "id": "daily-briefing",
      "enabled": true,
      "days": [1, 2, 3, 4, 5],
      "time": "08:00",
      "modules": ["environment_brief", "schedule_brief"]
    },
    {
      "id": "evening-reminder",
      "enabled": true,
      "days": [1, 2, 3, 4, 5, 6, 7],
      "time": "21:00",
      "modules": ["opening", "closing"]
    }
  ]
}
```

## Optional staged sequence / 可选的分阶段通知序列

```json
{
  "speaker": {
    "runtime": {
      "playbackMode": "staged",
      "stagedSequence": {
        "enabled": true,
        "steps": [
          {
            "text": "It is time for your scheduled notification.",
            "pauseAfterMs": 1000,
            "contextText": "clear, energetic"
          },
          {
            "text": "Are you ready to continue?",
            "pauseAfterMs": 2000,
            "contextText": "soft, clear, and steady"
          }
        ]
      }
    }
  }
}
```

## Data-provider boundary / 数据提供边界

The public skill does not directly connect to calendar, weather, or proprietary data systems.
Those inputs should be provided by the user or injected by the runtime environment through adapters.

本公共 Skill 不直接连接日历、天气或私有数据系统。
这些输入应由用户提供，或由运行环境通过 adapter 注入。


path: scripts/build_briefing.py
content: #!/usr/bin/env python3
"""
Assemble a bounded briefing text from a structured config.

This public example focuses on content assembly only and does not bundle
provider-specific playback, local workspace assumptions, or external API integrations.
"""

from __future__ import annotations
import argparse
import json
from datetime import datetime
from pathlib import Path

WEEKDAYS_ZH = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
WEATHER_DESC_ZH = {
    "Light rain shower": "小阵雨",
    "Patchy rain nearby": "附近有零星小雨",
    "Partly Cloudy": "多云间晴",
    "Partly cloudy": "多云间晴",
    "Sunny": "晴",
    "Overcast": "阴天",
    "Clear": "晴朗",
    "Clear ": "晴朗",
}


def load_config(path: Path) -> dict:
    if not path.exists():
        return {}
    return json.loads(path.read_text(encoding="utf-8"))


def normalize_weather_desc(desc: str) -> str:
    return WEATHER_DESC_ZH.get(desc, desc)


def build_text(config: dict, now: datetime) -> str:
    speaker = config.get("speaker", {})
    schedules = config.get("schedules", [])
    modules = schedules[0].get("modules", []) if schedules else []
    tone = speaker.get("tone", "calm")
    weekday = WEEKDAYS_ZH[now.isoweekday() - 1]

    briefing = schedules[0].get("briefing", {}) if schedules else {}
    weather = briefing.get("weather", {}) if isinstance(briefing, dict) else {}
    opening = briefing.get("opening") or "早上好。"
    closing = briefing.get("closing") or "祝你今天顺利。"
    custom_text = briefing.get("customText") if briefing.get("includeCustomText") else ""

    parts: list[str] = []
    parts.append(opening)
    parts.append(f"今天是{now.month}月{now.day}日，{weekday}。")

    if "environment_brief" in modules:
        if isinstance(weather, dict) and weather:
            location = weather.get("location", "当前所在城市")
            current_temp = weather.get("tempC")
            raw_desc = weather.get("desc", "天气情况正常")
            desc = normalize_weather_desc(str(raw_desc))
            max_temp = weather.get("maxTempC")
            min_temp = weather.get("minTempC")
            summary = weather.get("summary", "")
            weather_sentence = f"现在为你播报{location}的天气。"
            if current_temp is not None:
                weather_sentence += f" 当前大约{current_temp}度，{desc}。"
            if max_temp is not None and min_temp is not None:
                weather_sentence += f" 今天最高{max_temp}度，最低{min_temp}度。"
            if summary:
                weather_sentence += f" {summary}。"
            parts.append(weather_sentence)
        else:
            parts.append("现在为你播报天气信息。")

    if "schedule_brief" in modules:
        parts.append("下面是今天的日程摘要。")

    tone_map = {
        "calm": "我会用温和、清晰的方式继续播报。",
        "energetic": "我会用更有精神的方式继续播报。",
        "gentle": "我会用更轻柔的方式继续播报。",
    }
    parts.append(tone_map.get(tone, "我会用自然、清晰的方式继续播报。"))

    if custom_text:
        parts.append(str(custom_text).strip())
    parts.append(closing)
    return "".join(parts)


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--config", required=True)
    parser.add_argument("--now")
    args = parser.parse_args()
    now = datetime.fromisoformat(args.now) if args.now else datetime.now()
    config = load_config(Path(args.config))
    print(build_text(config, now))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())


path: scripts/update_schedule.py
content: #!/usr/bin/env python3
"""
Convert natural-language schedule updates into a structured config delta.

This public example focuses on config transformation and preview output.
It does not write local files unless explicitly requested.
"""

from __future__ import annotations
import argparse
import json
from pathlib import Path


def load(path: Path | None) -> dict:
    if path and path.exists():
        return json.loads(path.read_text(encoding="utf-8"))
    return {
        "enabled": True,
        "speaker": {"language": "zh", "voiceType": "female", "tone": "calm"},
        "schedules": [],
    }


def build_updated_config(data: dict, args: argparse.Namespace) -> dict:
    days = [int(x.strip()) for x in args.days.split(",") if x.strip()]
    modules = [x.strip() for x in args.modules.split(",") if x.strip()]

    item = {
        "id": args.id,
        "enabled": True,
        "days": days,
        "time": args.time,
        "modules": modules,
        "style": {"length": "short", "variability": "medium"},
    }

    data["speaker"] = {
        "language": args.language,
        "voiceType": args.voice_type,
        "tone": args.tone,
    }
    if args.timezone:
        data["timezone"] = args.timezone
    schedules = [s for s in data.get("schedules", []) if s.get("id") != args.id]
    schedules.append(item)
    data["schedules"] = schedules
    return data


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--config")
    parser.add_argument("--write", action="store_true")
    parser.add_argument("--id", required=True)
    parser.add_argument("--time", required=True)
    parser.add_argument("--days", default="1,2,3,4,5")
    parser.add_argument("--modules", default="environment_brief,schedule_brief")
    parser.add_argument("--language", default="zh")
    parser.add_argument("--voice-type", default="female")
    parser.add_argument("--tone", default="calm")
    parser.add_argument("--timezone")
    args = parser.parse_args()

    path = Path(args.config) if args.config else None
    data = load(path)
    updated = build_updated_config(data, args)

    if args.write:
        if path is None:
            raise SystemExit("--write requires --config")
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text(json.dumps(updated, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
        print(str(path))
    else:
        print(json.dumps(updated, ensure_ascii=False, indent=2))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)