Skill: jvy/jiejiari (version 1.0.0)

--- name: jiejiari description: "Look up official Mainland China public holiday schedules and make-up workdays from bundled annual reference data. Use when the user asks about 法定节假日, 放假安排, 调休, 补班, 国务院办公厅 holiday notices, or whether a specific 2026 date is a holiday/workday around 元旦, 春节, 清明节, 劳动节, 端午节, 中秋节, and 国庆节. Distinguish official holiday arrangements from lunar festival date lookup. NOT for calendar event CRUD, reminders, or non-China holiday calendars." metadata: { "openclaw": { "emoji": "🎏" } } --- # Jiejiari Skill 用这个技能回答中国法定节假日放假安排与调休问题。 ## 范围 - 只根据本技能内置的年度参考数据回答。 - 当前已内置 2026 年安排，数据文件在 `references/cn-holidays-2026.json`。 - “节日日期”与“放假安排”分开处理： - 问农历节日本身是几月几号，可改用 `rili`。 - 问春节/中秋/国庆具体放几天、哪天补班，优先用本技能。 ## 回答规则 - 优先给出精确日期，不要只说“本周日”“下周六”。 - 明确区分： - `holiday_days`: 连休天数 - `start_date` 到 `end_date`: 放假区间 - `work_days`: 调休上班日期 - 如果用户问“某天放不放假/要不要上班”，先在年度数据里定位该日期，再明确回答属于： - 放假日 - 调休日 - 普通周末或普通工作日 - 如果用户把农历节日日期和官方调休混在一起问，先拆开回答，避免混淆。 - 数据里没有的年份或地区性安排，不要猜。明确说明当前技能只内置了哪些年份。 ## 使用方式 - 先读取对应年份的参考文件。 - 按 `festival`、日期区间、`work_days` 回答。 - 用户要完整清单时，按节日顺序列出每个节日的放假区间、总天数、调休日。 ## 命令 ### 查某天状态 ```bash node {baseDir}/scripts/jiejiari.js date 2026-02-14 node {baseDir}/scripts/jiejiari.js date 2026-02-17 --json ``` 输出会明确标注该日属于： - 节假日 - 调休上班 - 普通周末 - 普通工作日 ### 查某个节日安排 ```bash node {baseDir}/scripts/jiejiari.js festival 春节 node {baseDir}/scripts/jiejiari.js festival 国庆节 --json ``` ### 列出全年安排 ```bash node {baseDir}/scripts/jiejiari.js list node {baseDir}/scripts/jiejiari.js list --json ``` ## 2026 节日列表 当前参考数据覆盖： - 元旦 - 春节 - 清明节 - 劳动节 - 端午节 - 中秋节 - 国庆节 ## 注意事项 - 这是“官方放假安排”技能，不是世界各国节假日百科。 - 不要把周末天然休息日误写成法定节假日。 - 不要自行推断“可能会调休”；只引用参考数据中的明确日期。 - 当前脚本只内置 2026 年数据；问到其他年份时要明确说明未内置。

path: scripts/jiejiari.js
content: #!/usr/bin/env node

import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const DATA_PATH = path.resolve(__dirname, "../references/cn-holidays-2026.json");
const YEAR = 2026;

function readHolidayData() {
  return JSON.parse(fs.readFileSync(DATA_PATH, "utf8"));
}

function assertIsoDate(isoDate) {
  const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(isoDate);
  if (!match) {
    throw new Error(`无效日期: ${isoDate}`);
  }
  const year = Number(match[1]);
  const month = Number(match[2]);
  const day = Number(match[3]);
  const date = new Date(Date.UTC(year, month - 1, day));
  if (
    Number.isNaN(date.getTime()) ||
    date.getUTCFullYear() !== year ||
    date.getUTCMonth() !== month - 1 ||
    date.getUTCDate() !== day
  ) {
    throw new Error(`无效日期: ${isoDate}`);
  }
  return { year, month, day };
}

function getWeekdayName(isoDate) {
  const { year, month, day } = assertIsoDate(isoDate);
  return new Intl.DateTimeFormat("zh-Hans-CN", {
    timeZone: "UTC",
    weekday: "long",
  }).format(new Date(Date.UTC(year, month - 1, day)));
}

function normalizeFestivalName(name) {
  return String(name ?? "")
    .trim()
    .replace(/\s+/g, "");
}

function isDateInRange(isoDate, startDate, endDate) {
  return isoDate >= startDate && isoDate <= endDate;
}

export function buildDateStatus(isoDate, entries = readHolidayData()) {
  const { year } = assertIsoDate(isoDate);
  if (year !== YEAR) {
    throw new Error(`当前只内置 ${YEAR} 年数据: ${isoDate}`);
  }

  const weekday = getWeekdayName(isoDate);
  for (const entry of entries) {
    if (isDateInRange(isoDate, entry.start_date, entry.end_date)) {
      return {
        isoDate,
        weekday,
        year,
        type: "holiday",
        label: "节假日",
        festival: entry.festival,
        startDate: entry.start_date,
        endDate: entry.end_date,
        holidayDays: entry.holiday_days,
        workDays: entry.work_days,
      };
    }
    if (entry.work_days.includes(isoDate)) {
      return {
        isoDate,
        weekday,
        year,
        type: "makeup_workday",
        label: "调休上班",
        festival: entry.festival,
        startDate: entry.start_date,
        endDate: entry.end_date,
        holidayDays: entry.holiday_days,
        workDays: entry.work_days,
      };
    }
  }

  const isWeekend = weekday === "星期六" || weekday === "星期日";
  return {
    isoDate,
    weekday,
    year,
    type: isWeekend ? "weekend" : "workday",
    label: isWeekend ? "普通周末" : "普通工作日",
  };
}

export function findFestival(name, entries = readHolidayData()) {
  const normalized = normalizeFestivalName(name);
  const entry = entries.find((item) => normalizeFestivalName(item.festival) === normalized);
  if (!entry) {
    throw new Error(`未找到节日安排: ${name}`);
  }
  return entry;
}

export function listSchedules(entries = readHolidayData()) {
  return entries.map((entry) => ({
    festival: entry.festival,
    startDate: entry.start_date,
    endDate: entry.end_date,
    holidayDays: entry.holiday_days,
    workDays: entry.work_days,
  }));
}

function formatDateStatusText(status) {
  const lines = [`${status.isoDate} ${status.weekday}: ${status.label}`];
  if (status.festival) {
    lines.push(`节日: ${status.festival}`);
    lines.push(`放假: ${status.startDate} 至 ${status.endDate} (${status.holidayDays} 天)`);
    lines.push(
      `调休: ${status.workDays.length > 0 ? status.workDays.join("、") : "无"}`,
    );
  }
  return lines.join("\n");
}

function formatFestivalText(entry) {
  return [
    `${entry.festival}: ${entry.start_date} 至 ${entry.end_date} (${entry.holiday_days} 天)`,
    `调休: ${entry.work_days.length > 0 ? entry.work_days.join("、") : "无"}`,
  ].join("\n");
}

function formatScheduleListText(entries) {
  return entries
    .map(
      (entry) =>
        `${entry.festival}: ${entry.startDate} 至 ${entry.endDate} (${entry.holidayDays} 天); 调休: ${entry.workDays.length > 0 ? entry.workDays.join("、") : "无"}`,
    )
    .join("\n");
}

export function usageText() {
  return [
    "用法:",
    "  jiejiari.js date <YYYY-MM-DD> [--json]",
    "  jiejiari.js festival <名称> [--json]",
    "  jiejiari.js list [--json]",
  ].join("\n");
}

export async function main(argv = process.argv.slice(2)) {
  const args = [...argv];
  const json = args.includes("--json");
  const filteredArgs = args.filter((arg) => arg !== "--json");
  const [command, ...rest] = filteredArgs;

  if (!command || command === "--help" || command === "-h") {
    console.log(usageText());
    return;
  }

  if (command === "date") {
    const isoDate = rest[0];
    if (!isoDate) {
      throw new Error("date 需要 <YYYY-MM-DD>");
    }
    const result = buildDateStatus(isoDate);
    console.log(json ? JSON.stringify(result, null, 2) : formatDateStatusText(result));
    return;
  }

  if (command === "festival") {
    const festivalName = rest.join("");
    if (!festivalName) {
      throw new Error("festival 需要节日名称");
    }
    const entry = findFestival(festivalName);
    console.log(json ? JSON.stringify(entry, null, 2) : formatFestivalText(entry));
    return;
  }

  if (command === "list") {
    const entries = listSchedules();
    console.log(json ? JSON.stringify(entries, null, 2) : formatScheduleListText(entries));
    return;
  }

  throw new Error(`不支持的命令: ${command}`);
}

if (process.argv[1] && path.resolve(process.argv[1]) === __filename) {
  main().catch((error) => {
    console.error(error instanceof Error ? error.message : String(error));
    process.exitCode = 1;
  });
}


path: references/cn-holidays-2026.json
content: [
  {
    "festival": "元旦",
    "start_date": "2026-01-01",
    "end_date": "2026-01-03",
    "holiday_days": 3,
    "work_days": ["2026-01-04"]
  },
  {
    "festival": "春节",
    "start_date": "2026-02-15",
    "end_date": "2026-02-23",
    "holiday_days": 9,
    "work_days": ["2026-02-14", "2026-02-28"]
  },
  {
    "festival": "清明节",
    "start_date": "2026-04-04",
    "end_date": "2026-04-06",
    "holiday_days": 3,
    "work_days": []
  },
  {
    "festival": "劳动节",
    "start_date": "2026-05-01",
    "end_date": "2026-05-05",
    "holiday_days": 5,
    "work_days": ["2026-05-09"]
  },
  {
    "festival": "端午节",
    "start_date": "2026-06-19",
    "end_date": "2026-06-21",
    "holiday_days": 3,
    "work_days": []
  },
  {
    "festival": "中秋节",
    "start_date": "2026-09-25",
    "end_date": "2026-09-27",
    "holiday_days": 3,
    "work_days": []
  },
  {
    "festival": "国庆节",
    "start_date": "2026-10-01",
    "end_date": "2026-10-07",
    "holiday_days": 7,
    "work_days": ["2026-09-20", "2026-10-10"]
  }
]


path: agents/openai.yaml
content: interface:
  display_name: "节假日安排"
  short_description: "查询中国大陆法定节假日与调休安排。"
  default_prompt: "Use $jiejiari to answer official Mainland China holiday schedule, make-up workday, and 2026 festival break questions with exact dates."

policy:
  allow_implicit_invocation: true


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