diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 977111d..84dbf46 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -38,6 +38,14 @@ "skills": [ "./load-codestyle" ] + }, + { + "name": "work-weekly-report", + "source": "./skills", + "strict": false, + "skills": [ + "./work-weekly-report" + ] } ] } diff --git a/skills/work-weekly-report/README.md b/skills/work-weekly-report/README.md new file mode 100644 index 0000000..51382d1 --- /dev/null +++ b/skills/work-weekly-report/README.md @@ -0,0 +1,75 @@ +# work-weekly-report + +工作计划管理工具。用于管理个人工作周报,包括制定本周工作计划、记录日常工作、更新工作进度、查询历史周报等。 + +## 目录结构 + +``` +work-weekly-report/ +├── SKILL.md # Skill 定义文件 +├── README.md # 本文件 +├── scripts/ # 脚本目录 +│ └── work-weekly-report.js +└── data/ # 数据存储目录 + └── weekly-reports.json +``` + +## 功能概述 + +- **制定本周工作计划**: 为当前工作周制定或更新工作计划 +- **更新工作记录**: 记录指定日期的实际工作情况 +- **输出本周未完成工作**: 汇总当前工作周中未完成的任务 +- **查询指定周周报**: 查看指定工作周的工作计划与实际记录 +- **更新计划项状态**: 将某个计划项标记为完成或其他状态 +- **设置周报总结**: 为某周添加总结文字 + +## 工作周定义 + +- **工作周**: 周一至周五(法定工作日) +- **周编号**: ISO 8601 标准,格式 `YYYY-Www`(如 `2026-W15` 表示 2026 年第 15 周) +- **周起始**: 周一为该周的第一天,周五为最后一天 + +## 使用方式 + +### 触发命令 + +- `/work-weekly-report` +- "制定本周工作计划" +- "更新今天的工作记录" +- "查看本周未完成的工作" +- "查询 2026-W15 的周报" + +### CLI 命令 + +```bash +# 制定本周计划 +node scripts/work-weekly-report.js plan --tasks "任务1" "任务2" + +# 更新今天的工作记录 +node scripts/work-weekly-report.js record --content "完成了XX任务" --status completed + +# 查看本周未完成 +node scripts/work-weekly-report.js pending + +# 查询指定周 +node scripts/work-weekly-report.js query --week "2026-W15" + +# 设置周报总结 +node scripts/work-weekly-report.js summary --week "2026-W15" --content "本周主要完成了..." +``` + +## 数据存储 + +数据存储在 `data/weekly-reports.json` 文件中,采用 JSON 格式,包含以下结构: + +- `meta`: 版本信息 +- `weeklyReports`: 按周编号索引的工作周报数据 + - `plan`: 计划任务列表 + - `records`: 每日工作记录 + - `summary`: 周报总结 + +## 注意事项 + +1. 数据文件会自动创建,首次使用时会初始化 +2. 所有日期需为有效的法定工作日(周一至周五) +3. 建议每次完成工作后及时记录,便于周末生成周报 diff --git a/skills/work-weekly-report/SKILL.md b/skills/work-weekly-report/SKILL.md new file mode 100644 index 0000000..f000fa4 --- /dev/null +++ b/skills/work-weekly-report/SKILL.md @@ -0,0 +1,210 @@ +--- +name: work-weekly-report +description: | + 工作周报管理工具。用于管理个人工作周报,包括制定本周工作计划、记录日常工作、更新工作进度、查询历史周报等。 + + 当用户提到以下场景时使用此 Skill: + (1) 制定本周工作计划、安排工作 + (2) 更新今天或指定日期的工作记录 + (3) 查看本周未完成的工作 + (4) 查询指定工作周的周报 + (5) 用户提到 "周报"、"工作计划"、"工作记录"、"工作周" +argument-hint: "[操作] [参数]" +allowed-tools: Read, Bash, Glob, Grep +--- + +# 工作周报管理 Skill + +## 概述 + +此 Skill 用于管理个人工作周报,支持工作计划制定、日常工作记录、未完成工作统计和历史周报查询。 + +## 工作周定义 + +- **工作周**: 周一至周五(法定工作日) +- **周编号**: ISO 8601 标准,格式 `YYYY-Www`(如 `2026-W15` 表示 2026 年第 15 周) +- **周起始**: 周一为该周的第一天,周五为最后一天 + +## 数据存储 + +数据存储在 `data/weekly-reports.json` 文件中,采用 JSON 格式。 + +## 功能清单 + +### 功能1: 制定本周工作计划 + +为当前工作周制定或更新工作计划。 + +**使用示例**: +``` +制定本周工作计划 +安排本周工作 +本周计划完成 A、B、C 任务 +``` + +**CLI 命令**: +```bash +# 制定本周计划 +node scripts/work-weekly-report.js plan --tasks "任务1" "任务2" "任务3" + +# 制定指定周计划(带优先级) +node scripts/work-weekly-report.js plan --week "2026-W15" --tasks "任务1" "任务2" --priority high +``` + +--- + +### 功能2: 更新工作记录 + +记录指定日期的实际工作情况。 + +**使用示例**: +``` +更新今天的工作记录 +记录今天完成的工作 +更新 2026-04-10 的工作 +``` + +**CLI 命令**: +```bash +# 更新今天的工作记录 +node scripts/work-weekly-report.js record --content "完成了XX任务" --status completed + +# 更新指定日期的工作记录 +node scripts/work-weekly-report.js record --date "2026-04-09" --content "继续开发XX功能" --status in-progress + +# 带工时记录 +node scripts/work-weekly-report.js record --date "2026-04-10" --content "完成代码审查" --status completed --hours 2 +``` + +--- + +### 功能3: 输出本周未完成的工作 + +汇总当前工作周中未完成的任务。 + +**使用示例**: +``` +本周有哪些未完成的工作 +查看未完成任务 +本周还有什么没做完的 +``` + +**CLI 命令**: +```bash +node scripts/work-weekly-report.js pending +node scripts/work-weekly-report.js pending --week "2026-W15" +``` + +--- + +### 功能4: 查询指定工作周的周报 + +查看指定工作周的工作计划与实际记录。 + +**使用示例**: +``` +查询 2026-W15 的周报 +查看第 15 周的工作情况 +查看上周的周报 +本周工作总结 +``` + +**CLI 命令**: +```bash +# 查询本周 +node scripts/work-weekly-report.js query + +# 查询指定周 +node scripts/work-weekly-report.js query --week "2026-W15" + +# 简洁摘要格式 +node scripts/work-weekly-report.js query --week "2026-W15" --format summary +``` + +--- + +### 功能5: 更新计划项状态 + +将某个计划项标记为完成或其他状态。 + +**使用示例**: +``` +把任务A标记为完成 +更新计划项状态 +``` + +**CLI 命令**: +```bash +node scripts/work-weekly-report.js update-status --week "2026-W15" --plan-item "uuid" --status completed +``` + +--- + +### 功能6: 设置周报总结 + +为某周添加总结文字。 + +**CLI 命令**: +```bash +node scripts/work-weekly-report.js summary --week "2026-W15" --content "本周主要完成了..." +``` + +--- + +## 周标识支持 + +查询和操作支持多种周标识格式: + +- `YYYY-Www` - ISO 格式(如 `2026-W15`) +- `本周`、`当前周` - 当前工作周 +- `上周`、`上上周` - 相对上周 +- `下周` - 相对下周 + +## 数据文件结构 + +```json +{ + "meta": { + "version": "1.0.0", + "createdAt": "2026-01-01T00:00:00.000Z", + "updatedAt": "2026-04-13T09:30:00.000Z" + }, + "weeklyReports": { + "2026-W15": { + "weekNumber": "2026-W15", + "weekStartDate": "2026-04-07", + "weekEndDate": "2026-04-11", + "plan": [ + { + "id": "550e8400-e29b-41d4-a716-446655440001", + "description": "完成项目 A 的开发工作", + "expectedDate": "2026-04-10", + "priority": "high", + "status": "completed", + "createdAt": "2026-04-07T09:00:00.000Z", + "updatedAt": "2026-04-10T18:00:00.000Z" + } + ], + "records": { + "2026-04-07": [ + { + "id": "660e8400-e29b-41d4-a716-446655440001", + "content": "开始项目 A 的架构设计", + "date": "2026-04-07", + "status": "completed", + "hours": 6, + "planItemId": "550e8400-e29b-41d4-a716-446655440001" + } + ] + }, + "summary": "本周主要完成了项目 A 的开发工作..." + } + } +} +``` + +## 注意事项 + +1. 数据文件会自动创建,首次使用时会初始化 +2. 所有日期需为有效的法定工作日(周一至周五) +3. 建议每次完成工作后及时记录,便于周末生成周报 diff --git a/skills/work-weekly-report/data/weekly-reports.json b/skills/work-weekly-report/data/weekly-reports.json new file mode 100644 index 0000000..d6a6b46 --- /dev/null +++ b/skills/work-weekly-report/data/weekly-reports.json @@ -0,0 +1,8 @@ +{ + "meta": { + "version": "1.0.0", + "createdAt": "2026-04-13T00:00:00.000Z", + "updatedAt": "2026-04-13T00:00:00.000Z" + }, + "weeklyReports": {} +} \ No newline at end of file diff --git a/skills/work-weekly-report/scripts/work-weekly-report.js b/skills/work-weekly-report/scripts/work-weekly-report.js new file mode 100644 index 0000000..91e37d6 --- /dev/null +++ b/skills/work-weekly-report/scripts/work-weekly-report.js @@ -0,0 +1,680 @@ +#!/usr/bin/env node + +/** + * 工作周报管理工具 + * 用于管理个人工作周报,包括制定计划、记录工作、查询周报等 + */ + +const fs = require('fs'); +const path = require('path'); + +// 数据文件路径 +const DATA_DIR = path.join(__dirname, '..', 'data'); +const DATA_FILE = path.join(DATA_DIR, 'weekly-reports.json'); + +// 确保数据目录存在 +if (!fs.existsSync(DATA_DIR)) { + fs.mkdirSync(DATA_DIR, { recursive: true }); +} + +/** + * 生成 UUID + */ +function generateUUID() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + const r = Math.random() * 16 | 0; + const v = c === 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); +} + +/** + * 获取本地日期字符串 YYYY-MM-DD + */ +function toLocalDateString(date) { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + return `${year}-${month}-${day}`; +} + +/** + * 获取当前日期字符串 + */ +function getCurrentDateStr() { + const now = new Date(); + return now.toISOString().split('T')[0]; +} + +/** + * 获取当前时间 ISO 字符串 + */ +function getCurrentISO() { + return new Date().toISOString(); +} + +/** + * 判断是否为法定工作日(周一至周五) + */ +function isWorkday(dateStr) { + const date = new Date(dateStr); + const day = date.getDay(); + return day >= 1 && day <= 5; +} + +/** + * 获取日期所在周的第一天(周一) + */ +function getWeekStartDate(dateStr) { + const date = new Date(dateStr); + const day = date.getDay(); + // 计算到周一的天数 + const diff = day === 0 ? -6 : 1 - day; + const monday = new Date(date); + monday.setDate(date.getDate() + diff); + return toLocalDateString(monday); +} + +/** + * 获取日期所在周的周五 + */ +function getWeekEndDate(weekStartDate) { + const monday = new Date(weekStartDate); + const friday = new Date(monday); + friday.setDate(monday.getDate() + 4); + return toLocalDateString(friday); +} + +/** + * 获取 ISO 周编号 (YYYY-Www) + */ +function getWeekNumber(dateStr) { + const date = new Date(dateStr); + // ISO 周码:每年的第一个周四所在的周为第1周 + const thursday = new Date(date); + thursday.setDate(date.getDate() + (4 - (date.getDay() === 0 ? 7 : date.getDay()))); + const yearStart = new Date(thursday.getFullYear(), 0, 1); + const weekNum = Math.ceil(((thursday - yearStart) / 86400000 + 1) / 7); + return `${thursday.getFullYear()}-W${String(weekNum).padStart(2, '0')}`; +} + +/** + * 获取当前工作周信息 + */ +function getCurrentWeek() { + const today = getCurrentDateStr(); + return getWeekInfo(today); +} + +/** + * 获取指定日期所在的工作周信息 + */ +function getWeekInfo(dateStr) { + const weekStart = getWeekStartDate(dateStr); + const weekEnd = getWeekEndDate(weekStart); + const weekNumber = getWeekNumber(dateStr); + return { + weekNumber, + startDate: weekStart, + endDate: weekEnd + }; +} + +/** + * 解析周标识为周编号 + */ +function parseWeekIdentifier(identifier) { + if (!identifier || identifier === '本周' || identifier === '当前周') { + return getCurrentWeek().weekNumber; + } + if (identifier === '上周') { + const today = new Date(); + today.setDate(today.getDate() - 7); + return getWeekNumber(today.toISOString().split('T')[0]); + } + if (identifier === '上上周') { + const today = new Date(); + today.setDate(today.getDate() - 14); + return getWeekNumber(today.toISOString().split('T')[0]); + } + if (identifier === '下周') { + const today = new Date(); + today.setDate(today.getDate() + 7); + return getWeekNumber(today.toISOString().split('T')[0]); + } + // 假设是 YYYY-Www 格式 + if (/^\d{4}-W\d{2}$/.test(identifier)) { + return identifier; + } + throw new Error(`无法解析周标识: ${identifier}`); +} + +/** + * 加载数据文件 + */ +function loadData() { + if (!fs.existsSync(DATA_FILE)) { + const initial = { + meta: { + version: '1.0.0', + createdAt: getCurrentISO(), + updatedAt: getCurrentISO() + }, + weeklyReports: {} + }; + fs.writeFileSync(DATA_FILE, JSON.stringify(initial, null, 2), 'utf8'); + return initial; + } + const content = fs.readFileSync(DATA_FILE, 'utf8'); + return JSON.parse(content); +} + +/** + * 保存数据文件 + */ +function saveData(data) { + data.meta.updatedAt = getCurrentISO(); + fs.writeFileSync(DATA_FILE, JSON.stringify(data, null, 2), 'utf8'); +} + +/** + * 从周编号反推该周的周一日期 + * 使用 ISO 8601 标准:每年第一个周四所在的周为第1周(W1从该周的周一开始) + */ +function getWeekStartFromWeekNumber(year, weekNum) { + const MS_PER_DAY = 24 * 60 * 60 * 1000; + + // 找到该年1月4日(ISO 8601 定义:包含该年第一个周四的那一周是W1) + const jan4 = new Date(year, 0, 4); + const jan4Day = jan4.getDay(); // 0=Sun, 1=Mon, ..., 6=Sat + + // 找到W1的周一 + let firstMonday; + if (jan4Day === 1) { + // 1月4日是周一 -> W1周一是1月4日 + firstMonday = jan4; + } else if (jan4Day === 0) { + // 1月4日是周日 -> W1周一是上周一(12月X日) + // 1月4日 - 7天 = 12月28日(上上个周一),再 -1天 = 12月29日(上周一) + firstMonday = new Date(jan4.getTime() - 7 * MS_PER_DAY); + // 但这已经是12月28了,而1月4日是周日,那上周一是12月29日 + // 所以实际上 1月4日 - 8天 = 12月27... 这不对 + // 让我重新想: + // 如果1月4日是周日,那这一周是 Dec 28 - Jan 3 (W1 ends Jan 3) + // 所以W1的周一是 Dec 28 + // 那么 Dec 28 + 7 = Jan 4,W2从 Jan 5开始 + // 所以 W1 of 2026 = Dec 28 - Jan 3 + // W1 starts on Monday Dec 28 + // Let me recalculate: Jan 4 - 6 = Dec 29 (that's Monday if Jan 4 is Sunday) + // Wait, if Jan 4 is Sunday, then Jan 4 - 1 = Jan 3 (Saturday) + // Jan 4 - 2 = Jan 2, ..., Jan 4 - 7 = Dec 28 + // But that's 7 days back, not 6. And Dec 28 is Monday? + // Let me just use getTime() + const daysToSubtract = jan4Day === 0 ? 6 : jan4Day - 1; + firstMonday = new Date(jan4.getTime() - daysToSubtract * MS_PER_DAY); + } else { + // 1月4日是 Tue(2), Wed(3), Thu(4), Fri(5), Sat(6) -> 找到上溯到周一 + // 如果1月4日是周三,那周一就是1月4日 - 2天 + // daysToSubtract = 4 - 1 = 3, 1月4日 - 3天 = 1月1日 = 周一 ✓ + const daysToSubtract = jan4Day - 1; + firstMonday = new Date(jan4.getTime() - daysToSubtract * MS_PER_DAY); + } + + // 计算目标周的周一 + const targetMonday = new Date(firstMonday.getTime() + (weekNum - 1) * 7 * MS_PER_DAY); + return targetMonday; +} + +/** + * 获取或创建指定周的周报对象 + */ +function getOrCreateWeeklyReport(weekNumber) { + const data = loadData(); + if (!data.weeklyReports[weekNumber]) { + const [yearStr, weekPart] = weekNumber.split('-W'); + const year = parseInt(yearStr, 10); + const weekNum = parseInt(weekPart, 10); + + const targetMonday = getWeekStartFromWeekNumber(year, weekNum); + const targetFriday = new Date(targetMonday); + targetFriday.setDate(targetMonday.getDate() + 4); + + data.weeklyReports[weekNumber] = { + weekNumber, + weekStartDate: toLocalDateString(targetMonday), + weekEndDate: toLocalDateString(targetFriday), + plan: [], + records: {}, + summary: '' + }; + } + return { data, report: data.weeklyReports[weekNumber] }; +} + +/** + * 制定/更新周计划 + */ +function planWeek(weekNumber, tasks, priority = 'medium') { + const { data, report } = getOrCreateWeeklyReport(weekNumber); + const now = getCurrentISO(); + + for (const taskDesc of tasks) { + const planItem = { + id: generateUUID(), + description: taskDesc, + expectedDate: null, + priority: priority, + status: 'pending', + createdAt: now, + updatedAt: now + }; + report.plan.push(planItem); + } + + saveData(data); + return report; +} + +/** + * 添加工作记录 + */ +function addRecord(date, content, status, options = {}) { + const { hours, planItemId } = options; + + if (!isWorkday(date)) { + throw new Error(`${date} 不是法定工作日(周一至周五)`); + } + + const weekInfo = getWeekInfo(date); + const { data, report } = getOrCreateWeeklyReport(weekInfo.weekNumber); + + if (!report.records[date]) { + report.records[date] = []; + } + + const record = { + id: generateUUID(), + content, + date, + status, + hours: hours || null, + planItemId: planItemId || null, + createdAt: getCurrentISO(), + updatedAt: getCurrentISO() + }; + + report.records[date].push(record); + + // 如果有关联的计划项,自动更新其状态 + if (planItemId) { + const planItem = report.plan.find(p => p.id === planItemId); + if (planItem) { + planItem.status = status === 'completed' ? 'completed' : + status === 'in-progress' ? 'in-progress' : planItem.status; + planItem.updatedAt = getCurrentISO(); + } + } + + saveData(data); + return report; +} + +/** + * 获取未完成的工作项 + */ +function getPendingItems(weekIdentifier) { + const weekNumber = parseWeekIdentifier(weekIdentifier); + const { data, report } = getOrCreateWeeklyReport(weekNumber); + + const pending = report.plan.filter(p => p.status === 'pending' || p.status === 'in-progress'); + + // 按优先级排序 + const priorityOrder = { high: 0, medium: 1, low: 2 }; + pending.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]); + + return pending; +} + +/** + * 查询周报 + */ +function queryWeek(weekIdentifier, format = 'detail') { + const weekNumber = parseWeekIdentifier(weekIdentifier); + const { report } = getOrCreateWeeklyReport(weekNumber); + + if (format === 'summary') { + return formatWeekSummary(report); + } + + return report; +} + +/** + * 格式化周报摘要 + */ +function formatWeekSummary(report) { + const total = report.plan.length; + const completed = report.plan.filter(p => p.status === 'completed').length; + const pending = report.plan.filter(p => p.status === 'pending' || p.status === 'in-progress'); + + let output = `周报摘要: ${report.weekNumber} (${report.weekStartDate} 至 ${report.weekEndDate})\n`; + output += '================================================================\n\n'; + + output += `计划完成率: ${completed}/${total}`; + if (total > 0) { + output += ` (${Math.round(completed / total * 100)}%)`; + } + output += '\n\n'; + + if (report.plan.length > 0) { + output += '计划项:\n'; + for (const item of report.plan) { + const statusIcon = item.status === 'completed' ? '[√]' : '[ ]'; + const priorityTag = item.priority === 'high' ? '[高]' : item.priority === 'low' ? '[低]' : '[中]'; + output += ` ${statusIcon} ${priorityTag} ${item.description}`; + if (item.expectedDate) { + output += ` (预计: ${item.expectedDate})`; + } + output += '\n'; + } + output += '\n'; + } + + if (Object.keys(report.records).length > 0) { + output += '每日记录:\n'; + const sortedDates = Object.keys(report.records).sort(); + for (const date of sortedDates) { + const records = report.records[date]; + const hours = records.reduce((sum, r) => sum + (r.hours || 0), 0); + const content = records.map(r => r.content).join('; '); + output += ` - ${date}: ${content}`; + if (hours > 0) { + output += ` (${hours}h)`; + } + output += '\n'; + } + output += '\n'; + } + + if (pending.length > 0) { + output += '未完成工作:\n'; + for (const item of pending) { + output += ` - ${item.description}\n`; + } + output += '\n'; + } + + if (report.summary) { + output += `总结: ${report.summary}\n`; + } + + return output; +} + +/** + * 更新计划项状态 + */ +function updatePlanItemStatus(weekNumber, planItemId, status) { + const { data, report } = getOrCreateWeeklyReport(weekNumber); + + const planItem = report.plan.find(p => p.id === planItemId); + if (!planItem) { + throw new Error(`未找到计划项: ${planItemId}`); + } + + planItem.status = status; + planItem.updatedAt = getCurrentISO(); + + saveData(data); + return report; +} + +/** + * 设置周报总结 + */ +function setSummary(weekNumber, summary) { + const { data, report } = getOrCreateWeeklyReport(weekNumber); + report.summary = summary; + saveData(data); + return report; +} + +/** + * 删除计划项 + */ +function removePlanItem(weekNumber, planItemId) { + const { data, report } = getOrCreateWeeklyReport(weekNumber); + const index = report.plan.findIndex(p => p.id === planItemId); + if (index === -1) { + throw new Error(`未找到计划项: ${planItemId}`); + } + report.plan.splice(index, 1); + saveData(data); + return report; +} + +/** + * 获取工作周的所有工作日 + */ +function getWorkdays(weekNumber) { + const { report } = getOrCreateWeeklyReport(weekNumber); + const workdays = []; + const current = new Date(report.weekStartDate); + const end = new Date(report.weekEndDate); + + while (current <= end) { + workdays.push(current.toISOString().split('T')[0]); + current.setDate(current.getDate() + 1); + } + return workdays; +} + +// === CLI 模式 === +if (require.main === module) { + const args = process.argv.slice(2); + const command = args[0]; + + // 已知的标志列表 + const FLAGS = ['--week', '--tasks', '--priority', '--date', '--content', '--status', '--hours', '--plan-item', '--format']; + + function getArgValue(flag) { + const index = args.indexOf(flag); + if (index !== -1 && index + 1 < args.length) { + const nextArg = args[index + 1]; + // 如果下一个参数也是标志,返回null + if (FLAGS.includes(nextArg)) { + return null; + } + return nextArg; + } + // 支持 --flag=value 格式 + const combined = args.find(arg => arg.startsWith(`${flag}=`)); + if (combined) { + return combined.split('=')[1]; + } + return null; + } + + function getRemainingArgs(flag) { + const index = args.indexOf(flag); + if (index === -1) return []; + + const result = []; + let i = index + 1; + + while (i < args.length) { + const arg = args[i]; + // 如果遇到另一个标志,停止 + if (FLAGS.includes(arg)) { + break; + } + result.push(arg); + i++; + } + + return result; + } + + try { + switch (command) { + case 'plan': { + const week = getArgValue('--week') || getCurrentWeek().weekNumber; + const tasks = getRemainingArgs('--tasks'); + if (tasks.length === 0) { + console.error('错误: 请提供任务列表 (--tasks "任务1" "任务2" ...)'); + process.exit(1); + } + const priority = getArgValue('--priority') || 'medium'; + const report = planWeek(week, tasks, priority); + console.log(`已在 ${report.weekNumber} 创建 ${tasks.length} 个计划项`); + console.log('\n计划项:'); + report.plan.slice(-tasks.length).forEach((item, i) => { + console.log(` ${i + 1}. [${item.priority}] ${item.description} (${item.id})`); + }); + break; + } + + case 'record': { + const date = getArgValue('--date') || getCurrentDateStr(); + const content = getArgValue('--content'); + if (!content) { + console.error('错误: 请提供工作内容 (--content "内容")'); + process.exit(1); + } + const status = getArgValue('--status') || 'in-progress'; + const hours = getArgValue('--hours') ? parseFloat(getArgValue('--hours')) : null; + const planItemId = getArgValue('--plan-item'); + const report = addRecord(date, content, status, { hours, planItemId }); + console.log(`已在 ${date} 添加工作记录`); + console.log(`周 ${report.weekNumber} 当前共有 ${report.plan.length} 个计划项`); + break; + } + + case 'pending': { + const week = getArgValue('--week') || getCurrentWeek().weekNumber; + const pending = getPendingItems(week); + const currentWeek = getCurrentWeek(); + console.log(`本周未完成的工作 (${week})\n`); + console.log('========================================\n'); + + if (pending.length === 0) { + console.log('所有任务已完成!'); + } else { + const high = pending.filter(p => p.priority === 'high'); + const medium = pending.filter(p => p.priority === 'medium'); + const low = pending.filter(p => p.priority === 'low'); + + if (high.length > 0) { + console.log('[高优先级]'); + high.forEach(p => console.log(` - ${p.description}`)); + console.log(); + } + if (medium.length > 0) { + console.log('[中优先级]'); + medium.forEach(p => console.log(` - ${p.description}`)); + console.log(); + } + if (low.length > 0) { + console.log('[低优先级]'); + low.forEach(p => console.log(` - ${p.description}`)); + console.log(); + } + } + console.log(`共 ${pending.length} 项未完成任务`); + break; + } + + case 'query': { + const week = getArgValue('--week') || '本周'; + const format = getArgValue('--format') || 'detail'; + const weekNumber = parseWeekIdentifier(week); + const result = queryWeek(weekNumber, format); + console.log(typeof result === 'string' ? result : JSON.stringify(result, null, 2)); + break; + } + + case 'update-status': { + const weekIdentifier = getArgValue('--week') || '本周'; + const planItemId = getArgValue('--plan-item'); + const status = getArgValue('--status'); + if (!planItemId || !status) { + console.error('错误: 请提供计划项ID (--plan-item) 和状态 (--status)'); + process.exit(1); + } + const weekNumber = parseWeekIdentifier(weekIdentifier); + const report = updatePlanItemStatus(weekNumber, planItemId, status); + console.log(`已更新计划项 ${planItemId} 状态为 ${status}`); + console.log(`周 ${report.weekNumber} 计划完成率: ${report.plan.filter(p => p.status === 'completed').length}/${report.plan.length}`); + break; + } + + case 'summary': { + const weekIdentifier = getArgValue('--week') || '本周'; + const content = getArgValue('--content'); + if (!content) { + console.error('错误: 请提供总结内容 (--content "内容")'); + process.exit(1); + } + const weekNumber = parseWeekIdentifier(weekIdentifier); + const report = setSummary(weekNumber, content); + console.log(`已在 ${weekNumber} 设置周报总结`); + break; + } + + case 'remove-plan': { + const weekIdentifier = getArgValue('--week') || '本周'; + const planItemId = getArgValue('--plan-item'); + if (!planItemId) { + console.error('错误: 请提供计划项ID (--plan-item)'); + process.exit(1); + } + const weekNumber = parseWeekIdentifier(weekIdentifier); + const report = removePlanItem(weekNumber, planItemId); + console.log(`已删除计划项 ${planItemId}`); + console.log(`周 ${report.weekNumber} 剩余 ${report.plan.length} 个计划项`); + break; + } + + case 'week-info': { + const date = getArgValue('--date') || getCurrentDateStr(); + const info = getWeekInfo(date); + console.log(`日期 ${date} 所在工作周:`); + console.log(` 周编号: ${info.weekNumber}`); + console.log(` 周一: ${info.startDate}`); + console.log(` 周五: ${info.endDate}`); + break; + } + + default: + console.log('用法:'); + console.log(' node work-weekly-report.js plan --tasks "任务1" "任务2" [--week YYYY-Www] [--priority high|medium|low]'); + console.log(' node work-weekly-report.js record --content "内容" [--date YYYY-MM-DD] [--status completed|in-progress|blocked] [--hours N] [--plan-item UUID]'); + console.log(' node work-weekly-report.js pending [--week YYYY-Www]'); + console.log(' node work-weekly-report.js query [--week YYYY-Www] [--format detail|summary]'); + console.log(' node work-weekly-report.js update-status --plan-item UUID --status STATUS [--week YYYY-Www]'); + console.log(' node work-weekly-report.js summary --content "总结" [--week YYYY-Www]'); + console.log(' node work-weekly-report.js remove-plan --plan-item UUID [--week YYYY-Www]'); + console.log(' node work-weekly-report.js week-info [--date YYYY-MM-DD]'); + console.log('\n周标识: YYYY-Www, 本周, 上周, 上上周, 下周'); + } + } catch (error) { + console.error('错误:', error.message); + process.exit(1); + } +} + +// 导出模块 +module.exports = { + getCurrentWeek, + getWeekInfo, + planWeek, + addRecord, + getPendingItems, + queryWeek, + updatePlanItemStatus, + setSummary, + removePlanItem, + getWorkdays, + parseWeekIdentifier, + isWorkday +};