# From Configuration to Architecture: Building the Advanced JiuwenSwarm Daily Report Generator ## Introduction — When “daily report automation” meets real office work Over the past year, AI agents have been discussed everywhere — from simple Q&A to complex workflows. Putting an agent into everyday office work surfaces a practical gap: > A conversational agent is not the same as an assistant that automates work. Office work is rarely “just chat.” It includes repetitive chores: end-of-day reports, weekly rollups, monthly summaries. They are not intellectually hard, but they consume time and attention. If an agent only chats, it stays a novelty. To be a **deliverable automation assistant**, it needs three things: (1) collect data from multiple sources, (2) analyze productivity intelligently, and (3) push results through the right channels proactively. This article covers: - Multi-source collection (Git commits + email stats + memory + todos) - A work-analysis engine (metrics, trends, keyword extraction) - Report generation (daily, weekly, monthly) - Scheduled tasks and push configuration - Implementation and validation If you are asking: - How to turn a demo agent into a productivity tool - How to make an agent push information instead of only reacting - How to build a reusable modular skill set the sections below may help. --- ## Project environment > **This document reflects a real project.** Configuration and code match what was actually used. ### Runtime | Item | Value | | --- | --- | | **Project path** | `D:\Download\jiuwenswarm` | | **OS** | Windows 10 | | **Python** | 3.10+ | | **Model** | ModelScope (`Qwen/Qwen3-235B-A22B-Instruct-2507`) | ### Data sources | Source | Value | | --- | --- | | **Git repo** | `D:\Download\jiuwenswarm` (this project) | | **Email** | `zxworkem@163.com` (NetEase 163 Email) | | **Delivery** | Feishu (`cli_a92035b1823a9cd2`) | | **Heartbeat window** | Daily 18:00–18:30 | ### Key paths ```plain D:\Download\jiuwenswarm\ ├── .env ├── config/config.yaml # App config (heartbeat, Feishu) ├── workspace/ │ ├── HEARTBEAT.md # Heartbeat tasks │ └── agent/skills/daily-report/ # Skill module │ ├── SKILL.md # Skill definition v2.0.0 │ ├── collectors/ │ ├── analyzers/ │ └── generators/ ``` ![](https://cdn.nlark.com/yuque/0/2026/png/27326384/1772822490912-87defae8-95ee-4c68-890b-d5082761d783.png) ## 1. Problem background ### 1.1 Limits of a “basic” daily report skill People often assume: > “Isn’t it just memory + todos + a template?” That works for toy demos. In production you hit three problems: 1. **Single source** — Only memory and todos; no visibility into commits, email, or real output. 2. **No analysis** — Lists tasks but does not compute metrics, trends, or advice. 3. **Fixed report type** — Daily only; no weekly or monthly rollups. Example: “commit trend this week” or “productivity vs last week” needs Git history, time windows, and comparison logic — a basic skill cannot do that. The **advanced daily report** skill aggregates multiple sources, runs analysis, and produces metrics, trends, and suggestions. ### 1.2 Office pain points **Writing the daily report takes too long** — 15–20 minutes at the end of the day to remember what shipped. **Formats drift** — Sometimes bullet lists, sometimes narrative, sometimes skipped; managers struggle to compare. **Important work gets forgotten** — A critical bug fix or feature may be omitted from the report. Auto-collecting Git commits reduces that gap. **Email work is invisible** — Volume, unread counts, and follow-ups rarely make it into a manual report. With the advanced skill, Git, email stats, memory, and todos feed an engine that computes metrics and trends. ### 1.3 JiuwenSwarm skills | Capability | Description | | --- | --- | | **Modular skills** | Multiple Python modules per skill | | **Tools** | `allowed_tools` for system integration | | **Heartbeat** | Periodic skill execution | | **Channels** | Feishu, Web, etc. | The payoff is **extensibility**: collection, analysis, and reporting stay separated. ## 2. Technical approach ### 2.1 Layering in JiuwenSwarm The advanced daily report skill sits in the application layer (see diagram in the original article). ![](https://cdn.nlark.com/yuque/0/2026/png/27326384/1772822901009-32c4955a-17b4-47f8-81f3-22283748f998.png) ### 2.2 Three-layer data flow ![](https://cdn.nlark.com/yuque/0/2026/png/27326384/1772861778248-67064c9c-4de8-41c7-850a-699ca4205cd1.png) ### 2.3 Core components | Component | Type | Role | Module | | --- | --- | --- | --- | | **GitCollector** | Collector | Git commits | `collectors/git_collector.py` | | **EmailCollector** | Collector | NetEase mailbox stats | `collectors/email_collector.py` | | **MemoryCollector** | Collector | Memory files | `collectors/memory_collector.py` | | **TodoCollector** | Collector | Todo lists | `collectors/todo_collector.py` | | **DataAggregator** | Aggregator | Merge sources | `collectors/aggregator.py` | | **WorkAnalyzer** | Analyzer | Work analysis | `analyzers/work_analyzer.py` | | **ReportGenerator** | Generator | Reports | `generators/report_generator.py` | ![](https://cdn.nlark.com/yuque/0/2026/png/27326384/1772861858079-e087425b-3bfd-40de-8567-92c874d0b396.png) ### 2.4 Flow and design choices ![](https://cdn.nlark.com/yuque/0/2026/png/27326384/1772861961611-4d5aae74-099e-4cc4-aec5-622b14fc515e.png) | Decision | Choice | Reason | | --- | --- | --- | | Git | `git log` | No extra deps | | Email | IMAP | Supported by NetEase | | Tokenization | jieba (optional) | Good for Chinese; can degrade gracefully | | Reports | Markdown | Portable; Feishu renders | | Trigger | Heartbeat + manual | Scheduled + on demand | ## Chapter 3 — Skills system engineering ### 3.1 Skills directory structure ```plain workspace/agent/skills/daily-report/ ├── SKILL.md ├── collectors/ │ ├── __init__.py │ ├── git_collector.py │ ├── email_collector.py │ ├── memory_collector.py │ ├── todo_collector.py │ └── aggregator.py ├── analyzers/ │ ├── __init__.py │ └── work_analyzer.py ├── generators/ │ ├── __init__.py │ └── report_generator.py └── report_helper.py # Legacy compatibility ``` ![](https://cdn.nlark.com/yuque/0/2026/png/27326384/1772862008911-7b9d807a-9175-4c62-a208-c3b6381169be.png) ### 3.2 `advanced-daily-report` SKILL.md ```markdown --- name: advanced-daily-report version: 2.0.0 description: Advanced version daily report generator, supports multi-data source collection, work analysis, trend comparison, and weekly and monthly report aggregation tags: [report, automation, productivity, daily, weekly, monthly, advanced] allowed_tools: [read_memory, write_memory, mcp_exec_command, read_file, write_file] --- # Advanced version daily report generator Automatically collect multi-source data, intelligently analyze work efficiency, generate daily report/weekly report/monthly report and push them to Feishu. ## Core capabilities ### 1. Multi-data source collection | Data source | Collected content | Frequency | |--------|----------|------| | **Git repositories** | submission records, code change statistics | real time | | **Netease email** | sent/received email statistics, unread reminders | real time | | **memory system** | today’s work records, long-term memory | real time | | **to-do items** | task status, completion rate | real time | ### 2. Intelligent work analysis - **Efficiency metric calculation** - task completion rate = completed / total tasks - productivity score (0-100) - focus score (0-100) - **Trend comparison** - compared with yesterday - compared with the same period last week - weekly trend chart - **Keyword extraction** - automatically extract today’s work keywords - work topic clustering ### 3. Multiple report types | Type | Trigger method | Push time | |------|----------|----------| | **Daily report** | manual/timed | every day 18:00 | | **Weekly report** | timed | every Friday 18:00 | | **Monthly report** | timed | on the last day of every month 18:00 | ## Directory structure ``` daily-report/ ├── SKILL.md # Skill definition (this file) ├── collectors/ │ ├── __init__.py │ ├── git_collector.py │ ├── email_collector.py │ ├── memory_collector.py │ ├── todo_collector.py │ └── aggregator.py ├── analyzers/ │ ├── __init__.py │ └── work_analyzer.py ├── generators/ │ ├── __init__.py │ └── report_generator.py └── report_helper.py # Legacy compatibility ``` ## Usage ### ⚠️ Important: how to run This skill collects data by running Python scripts (Git, mailbox, memory, todos). **You must use the `mcp_exec_command` tool to run the scripts** — do not reply to the user without executing them. **What the scripts collect automatically:** - **Git commits**: `git log` on repo `D:/Download/jiuwenswarm` - **Mailbox stats**: IMAP to `zxworkem@163.com` (requires mailbox authorization code) - **Memory**: daily files under `workspace/agent/memory/` - **Todos**: `todo.md` under `workspace/session/` ### Manual trigger When the user asks for a daily / weekly / monthly report, **run:** ```bash # Today’s daily report (Git + todos + memory) cd D:/Download/jiuwenswarm && python workspace/agent/skills/daily-report/run_report.py daily --save # Daily report for a specific date cd D:/Download/jiuwenswarm && python workspace/agent/skills/daily-report/run_report.py daily --date 2026-03-06 --save # Weekly report (aggregate one week) cd D:/Download/jiuwenswarm && python workspace/agent/skills/daily-report/run_report.py weekly --save # Monthly report (aggregate one month, incl. per-day Git stats) cd D:/Download/jiuwenswarm && python workspace/agent/skills/daily-report/run_report.py monthly --save # Monthly report for a given month cd D:/Download/jiuwenswarm && python workspace/agent/skills/daily-report/run_report.py monthly --year 2026 --month 3 --save ``` ### Execution steps 1. User sends something like “generate daily / weekly / monthly report”. 2. **Run the commands above with `mcp_exec_command`.** 3. The script collects: - **Git**: `git log` for commits and churn - **Email**: IMAP stats (if mail is configured) - **Memory**: work notes from memory files - **Todos**: task state from `todo.md` 4. Wait until the script finishes and read its output. 5. Send the report content to the user. ### Trigger phrases (examples) - **Daily**: generate today’s report, yesterday’s report, today’s work, today’s commits - **Weekly**: this week’s summary, weekly rollup, week in review - **Monthly**: this month’s summary, monthly review, summarize this month’s mail into a report, this month’s commit stats ### Data sources | Source | How | Where to configure | |--------|-----|--------------------| | **Git** | `git log` | Repo path: `D:/Download/jiuwenswarm` | | **NetEase** | IMAP | `.env`: `EMAIL_ADDRESS`, `EMAIL_TOKEN` | | **Memory** | Read Markdown | `workspace/agent/memory/YYYY-MM-DD.md` | | **Todos** | Parse `todo.md` | `workspace/session/*/todo.md` | ### Scheduled trigger Configure periodic runs in `HEARTBEAT.md`: ```markdown ## Active tasks - Generate today’s work report # daily - Generate weekly report # every Friday - Generate monthly report # month end ``` ## Daily report template ```markdown # 📋 Work daily — 2026-03-06 ## 📊 Overview | Metric | Value | |--------|-------| | Commits | 5 | | Tasks done | 3/8 | | Code churn | +350/-80 | | Email | in 12 / out 3 | | Productivity | 78.5 | ## ✅ Done - Finished daily-report skill - Configured Feishu channel - Tested heartbeat ## 🔄 In progress - Write documentation - Weekly aggregation ## 💻 Commits | Time | Message | Churn | |------|---------|-------| | 09:30 | feat: daily report | +120/-30 | | 14:15 | fix: email collector | +45/-12 | ## 📧 Email - Inbox today: 12 - Sent today: 3 - Unread: 2 ## 📈 Trends - Commits: ↑ 2 vs yesterday - Efficiency: ↑ 5.2 pts ## 💡 Suggestions 1. Focus seems low — reduce interruptions 2. Task completion rate can improve ## 🔜 Tomorrow - Polish daily template - Add weekly rollup ``` ## Configuration ### Git repo Monitored repository (read automatically by scripts): ``` Repo path: D:/Download/jiuwenswarm ``` The script uses `git log` to collect: - Commit hash, message, author, time - Per commit: files changed, insertions, deletions ### Email Configure in `.env` (shape used in the project): ```env EMAIL_ADDRESS=zxworkem@163.com EMAIL_TOKEN= EMAIL_PROVIDER=163 ``` **Note:** `EMAIL_TOKEN` is the mailbox **authorization code**, not the login password. In 163: Settings → POP3/SMTP/IMAP → enable IMAP → generate auth code. ### Heartbeat ```yaml heartbeat: every: 3600 target: feishu active_hours: start: 18:00 end: 18:30 ``` ## API reference ### Data aggregator ```python from collectors import DataAggregator aggregator = DataAggregator( workspace_dir="workspace", git_repo="path/to/repo", email_config={ "address": "xxx@163.com", "auth_code": "xxx", "provider": "163" } ) # Collect today data = aggregator.collect() # Collect one week week_data = aggregator.collect_week() ``` ### Work analyzer ```python from analyzers import WorkAnalyzer analyzer = WorkAnalyzer() result = analyzer.analyze(data.to_dict()) print(f"Productivity: {result.metrics.productivity_score}") print(f"Keywords: {result.keywords}") print(f"Suggestions: {result.suggestions}") ``` ### Report generator ```python from generators import ReportGenerator generator = ReportGenerator(aggregator) daily = generator.generate_daily() weekly = generator.generate_weekly() monthly = generator.generate_monthly(2026, 3) ``` ## Notes 1. **Git**: ensure the repo path is correct and readable. 2. **Mail**: use the authorization code, not the login password. 3. **Heartbeat**: restart services after changes. 4. **Storage**: reports are saved under `workspace/agent/reports/`. ## Changelog - **v2.0.0** (2026-03-06): Advanced — multi-source, trends, weekly/monthly. - **v1.0.0** (2026-03-06): Initial basic daily report. ``` ![](https://cdn.nlark.com/yuque/0/2026/png/27326384/1772862026601-dd7ed3ff-26da-4182-8f6f-c45c437feffc.png) ### 3.3 Daily report template (minimal example) ```markdown # 📋 Work daily — 2026-03-06 ## 📊 Overview | Metric | Value | |--------|-------| | Commits | 5 | | Tasks done | 3/8 | | Code churn | +350/-80 | | Email | in 12 / out 3 | | Productivity | 78.5 | ## ✅ Done - Finished daily-report skill - Configured Feishu channel ## 💻 Commits | Time | Message | Churn | |------|---------|-------| | 09:30 | feat: add daily report | +120/-30 | ## 📧 Email - Inbox today: 12 - Sent today: 3 - Unread: 2 ## 📈 Trends - Commits: ↑ 2 - Efficiency: ↑ 5.2 pts ## 💡 Suggestions 1. Focus seems low — reduce interruptions ## 🔜 Tomorrow - Polish daily template ``` ## Chapter 4 — Data collection layer (full implementation) ### 4.1 Git collector ```python # collectors/git_collector.py # -*- coding: utf-8 -*- """ Git 提交记录采集器 功能: - 获取指定日期的 Git 提交记录 - 统计提交次数、修改文件数、代码行数变化 - 支持多个仓库 """ import subprocess from dataclasses import dataclass, field from datetime import datetime, timedelta from pathlib import Path from typing import Optional @dataclass class GitCommit: """Git 提交记录""" hash: str # 提交哈希 message: str # 提交信息 author: str # 作者 date: datetime # 提交时间 files_changed: int = 0 # 修改文件数 insertions: int = 0 # 新增行数 deletions: int = 0 # 删除行数 def to_dict(self) -> dict: return { "hash": self.hash, "message": self.message, "author": self.author, "date": self.date.isoformat(), "files_changed": self.files_changed, "insertions": self.insertions, "deletions": self.deletions, } @dataclass class GitStats: """Git 统计数据""" commits: list[GitCommit] = field(default_factory=list) total_commits: int = 0 total_files_changed: int = 0 total_insertions: int = 0 total_deletions: int = 0 @property def net_lines(self) -> int: """净增行数""" return self.total_insertions - self.total_deletions def to_dict(self) -> dict: return { "total_commits": self.total_commits, "total_files_changed": self.total_files_changed, "total_insertions": self.total_insertions, "total_deletions": self.total_deletions, "net_lines": self.net_lines, "commits": [c.to_dict() for c in self.commits], } class GitCollector: """Git 提交记录采集器""" def __init__(self, repo_path: str | Path): """ 初始化 Git 采集器 Args: repo_path: Git 仓库路径 """ self.repo_path = Path(repo_path).resolve() def _run_git_command(self, args: list[str], timeout: int = 30) -> str: """执行 Git 命令""" try: result = subprocess.run( ["git", "-C", str(self.repo_path)] + args, capture_output=True, text=True, timeout=timeout, encoding="utf-8", errors="replace", ) return result.stdout.strip() except subprocess.TimeoutExpired: return "" except Exception as e: return "" def get_commits(self, date: Optional[str] = None, author: Optional[str] = None) -> GitStats: """ 获取指定日期的提交记录 Args: date: 日期字符串 (YYYY-MM-DD),默认今天 author: 作者名称过滤,默认不过滤 Returns: GitStats: Git 统计数据 """ if date is None: date = datetime.now().strftime("%Y-%m-%d") stats = GitStats() # 获取提交列表 log_format = "%H|%s|%an|%ai" since = f"{date} 00:00:00" until = f"{date} 23:59:59" cmd_args = [ "log", f"--since={since}", f"--until={until}", f"--format={log_format}", ] if author: cmd_args.append(f"--author={author}") log_output = self._run_git_command(cmd_args) if not log_output: return stats # 解析提交记录 for line in log_output.split("\n"): if not line.strip(): continue parts = line.split("|", 3) if len(parts) < 4: continue commit_hash, message, author_name, date_str = parts try: commit_date = datetime.fromisoformat( date_str.replace(" ", "T").split("+")[0] ) except ValueError: commit_date = datetime.now() # 获取每个提交的文件变更统计 numstat = self._run_git_command( ["show", "--numstat", "--format=", commit_hash] ) files_changed = 0 insertions = 0 deletions = 0 for stat_line in numstat.split("\n"): if not stat_line.strip(): continue stat_parts = stat_line.split("\t") if len(stat_parts) >= 2: try: ins = int(stat_parts[0]) if stat_parts[0] != "-" else 0 dels = int(stat_parts[1]) if stat_parts[1] != "-" else 0 insertions += ins deletions += dels files_changed += 1 except ValueError: continue commit = GitCommit( hash=commit_hash[:8], message=message.strip(), author=author_name, date=commit_date, files_changed=files_changed, insertions=insertions, deletions=deletions, ) stats.commits.append(commit) stats.total_commits += 1 stats.total_files_changed += files_changed stats.total_insertions += insertions stats.total_deletions += deletions return stats ``` ### 4.2 Email Statistics Collector > **Important Note**: The 163 email service’s IMAP has special restrictions. Using the `SELECT` command directly will return an `"Unsafe Login"` error. > Solution: > 1. Register the ID command in `imaplib`: `imaplib.Commands['ID'] = ('NONAUTH', 'AUTH', 'SELECTED')` > 2. After logging in, send the ID command to declare the client identity > 3. Use the `STATUS` command to obtain email statistics (bypassing the `SELECT` restriction) > 4. If you need to read email content, after the ID command is successfully sent, you can use `SELECT` normally ```python # collectors/email_collector.py # -*- coding: utf-8 -*- """ Email Statistics Collector Supports: - NetEase Mail (163/126/yeah) - Reading emails via the IMAP protocol Features: - Count the number of emails in the inbox - Get the number of unread emails - Read email content previews Special handling for 163: - Must register the ID command and send it after logging in - Use the STATUS command to get statistics (bypass the Unsafe Login restriction) """ import email import re from dataclasses import dataclass, field from datetime import datetime, timedelta from email.header import decode_header from typing import Optional try: import imaplib IMAP_AVAILABLE = True # 163 mailbox must: register ID command in imaplib imaplib.Commands['ID'] = ('NONAUTH', 'AUTH', 'SELECTED') except ImportError: IMAP_AVAILABLE = False imaplib = None # NetEase IMAP server configuration NETEASE_IMAP_SERVERS = { "163": "imap.163.com", "126": "imap.126.com", "yeah": "imap.yeah.net", } @dataclass class EmailInfo: """Email information""" subject: str = "" # Subject sender: str = "" # Sender date: str = "" # Date body_preview: str = "" # Body preview def to_dict(self) -> dict: return { "subject": self.subject, "sender": self.sender, "date": self.date, "body_preview": self.body_preview[:200] if self.body_preview else "", } @dataclass class EmailStats: """Email statistics data""" total_emails: int = 0 # Total number of emails in the mailbox unread: int = 0 # Number of unread emails recent_emails: list[EmailInfo] = field(default_factory=list) # Recent emails def to_dict(self) -> dict: return { "total_emails": self.total_emails, "unread": self.unread, "recent_emails": [e.to_dict() for e in self.recent_emails], } class EmailCollector: """Email statistics collector""" def __init__( self, email_address: str, auth_code: str, provider: str = "163", ): """ Initialize the email collector Args: email_address: Email address auth_code: Authorization code (not the login password) provider: Email provider (163/126/yeah) """ if not IMAP_AVAILABLE: raise ImportError("imaplib module is not available") self.email_address = email_address self.auth_code = auth_code self.provider = provider.lower() if self.provider not in NETEASE_IMAP_SERVERS: raise ValueError(f"Unsupported email provider: {provider}") self.imap_server = NETEASE_IMAP_SERVERS[self.provider] self._connection = None def _decode_str(self, s: str) -> str: """Decode an email string""" if s is None: return "" decoded_parts = decode_header(s) result = [] for part, encoding in decoded_parts: if isinstance(part, bytes): try: result.append(part.decode(encoding or "utf-8", errors="ignore")) except Exception: result.append(part.decode("utf-8", errors="ignore")) else: result.append(part) return "".join(result) def connect(self) -> bool: """Connect to the IMAP server and send the ID command""" try: self._connection = imaplib.IMAP4_SSL(self.imap_server, 993) self._connection.login(self.email_address, self.auth_code) # 163 mailbox must: send ID immediately after login args = '("name" "python" "version" "1.0" "vendor" "python-imap")' self._connection._simple_command("ID", args) return True except Exception as e: print(f"Failed to connect to mailbox: {e}") return False def disconnect(self): """Disconnect""" if self._connection: try: self._connection.logout() except Exception: pass self._connection = None def get_stats(self) -> EmailStats: """ Get email statistics (use the STATUS command, bypass SELECT restrictions) Returns: EmailStats: Email statistics data """ stats = EmailStats() if not self._connection: if not self.connect(): return stats try: # Use STATUS to get statistics (SELECT on 163 will return Unsafe Login) status, data = self._connection.status("INBOX", "(MESSAGES UNSEEN)") if status == "OK" and data: # Parse response: b'"INBOX" (MESSAGES 39 UNSEEN 32)' response = data[0].decode() if isinstance(data[0], bytes) else str(data[0]) messages_match = re.search(r'MESSAGES\s+(\d+)', response) unseen_match = re.search(r'UNSEEN\s+(\d+)', response) if messages_match: stats.total_emails = int(messages_match.group(1)) if unseen_match: stats.unread = int(unseen_match.group(1)) except Exception as e: print(f"Failed to get email statistics: {e}") return stats def get_recent_emails(self, limit: int = 10, days: int = 30) -> list[EmailInfo]: """ Read recent email content (after sending the ID command, SELECT works normally) Args: limit: Maximum number of emails to read days: Only read emails within the last N days Returns: List of emails """ if not self._connection: if not self.connect(): return [] emails = [] try: # ID command has been sent; now SELECT works normally typ, dat = self._connection.select("INBOX") if typ != "OK": return [] # Search for emails within the last N days since_date = (datetime.now() - timedelta(days=days)).strftime("%d-%b-%Y") typ, msg_ids = self._connection.search(None, f'(SINCE {since_date})') if typ != "OK" or not msg_ids[0]: return [] ids = msg_ids[0].split()[-limit:] # Get the latest N emails for msg_id in reversed(ids): try: typ, msg_data = self._connection.fetch(msg_id, "(RFC822)") if typ != "OK": continue raw_email = msg_data[0][1] msg = email.message_from_bytes(raw_email) # Decode subject subject = self._decode_str(msg["Subject"]) or "(No subject)" from_addr = self._decode_str(msg.get("From", "")) date_str = msg.get("Date", "") # Extract body body = "" if msg.is_multipart(): for part in msg.walk(): content_type = part.get_content_type() if content_type == "text/plain": payload = part.get_payload(decode=True) charset = part.get_content_charset() or "utf-8" body = payload.decode(charset, errors="ignore") break elif content_type == "text/html" and not body: payload = part.get_payload(decode=True) charset = part.get_content_charset() or "utf-8" html_body = payload.decode(charset, errors="ignore") body = re.sub(r'<[^>]+>', ' ', html_body) body = re.sub(r'\s+', ' ', body).strip() else: payload = msg.get_payload(decode=True) charset = msg.get_content_charset() or "utf-8" body = payload.decode(charset, errors="ignore") if payload else "" emails.append(EmailInfo( subject=subject[:100], sender=from_addr[:80], date=date_str, body_preview=body[:500] if body else "" )) except Exception: continue except Exception as e: print(f"Failed to read email content: {e}") return emails def __enter__(self): self.connect() return self def __exit__(self, exc_type, exc_val, exc_tb): self.disconnect() return False ``` ### 4.3 Memory Data Collector ```python # collectors/memory_collector.py # -*- coding: utf-8 -*- """ Memory Data Collector Function: - Read today's memory file - Read long-term memory - Extract work summary """ import re from dataclasses import dataclass, field from datetime import datetime, timedelta from pathlib import Path from typing import Optional @dataclass class MemoryData: """Memory data""" today_content: str = "" # Today's memory content long_term_content: str = "" # Long-term memory content work_summaries: list[str] = field(default_factory=list) # List of work summaries key_decisions: list[str] = field(default_factory=list) # Key decisions def to_dict(self) -> dict: return { "today_content": self.today_content[:500] if self.today_content else "", "work_summaries": self.work_summaries, "key_decisions": self.key_decisions, } class MemoryCollector: """Memory Data Collector""" def __init__(self, workspace_dir: str | Path): """ Initialize memory collector Args: workspace_dir: workspace directory path """ self.workspace_dir = Path(workspace_dir) self.memory_dir = self.workspace_dir / "agent" / "memory" def _read_file_safe(self, file_path: Path) -> str: """Safely read file""" if not file_path.exists(): return "" try: return file_path.read_text(encoding="utf-8") except Exception: return "" def _extract_list_items(self, content: str) -> list[str]: """Extract list items (lines starting with - or *)""" items = [] for line in content.split("\n"): stripped = line.strip() if stripped.startswith("-") or stripped.startswith("*"): item = stripped.lstrip("-* ").strip() # Skip comments and empty items if item and not item.startswith(" - Generate today's work daily report --- ## Completed task items --- ## Task description ### Daily report task - **Trigger time**: Every day 18:00 - 18:30 (configured according to config.yaml) - **Push target**: Feishu - **Content**: Today's Git commits, task completion status, email statistics, work efficiency analysis ### Weekly report task - **Trigger time**: Every Friday 18:00 - 18:30 - **Push target**: Feishu - **Content**: This week's data aggregation, trend analysis, next week's plan ### Monthly report task - **Trigger time**: The last day of every month 18:00 - 18:30 - **Push target**: Feishu - **Content**: This month's data aggregation, achievements summary, next month's plan --- ## Configuration method Modify the `heartbeat` configuration in `config/config.yaml`: ```yaml heartbeat: every: 3600 # Heartbeat interval (seconds) target: feishu # Push target active_hours: start: 18:00 # Effective start time end: 18:30 # Effective end time ``` **After modification, restart the service for it to take effect.** ``` ### 6.3 Git Repository Configuration The Git repositories monitored by this project (the script will automatically read): ```plain Repository path: D:/Download/jiuwenswarm ``` **Collection method**:The script collects data through the `git log` command; no additional configuration is needed. **Collection content**: + Commit hash + Commit message + Commit author, commit time + Number of changed files, number of added lines, number of deleted lines **Execution command**: ```bash # The git command executed internally by the script git -C D:/Download/jiuwenswarm log --since="2026-03-07 00:00:00" --until="2026-03-07 23:59:59" --format="%H|%s|%an|%ai" --numstat ``` **Multi-repository support**:If you need to monitor multiple repositories, expand `DataAggregator`: ```python # Example of extending configuration (needs to be implemented by yourself) git_repos: - path: "D:/Download/jiuwenswarm" name: "jiuwenswarm" - path: "D:/Projects/another-repo" name: "another-project" ``` ### 6.4 Feishu Channel Configuration (config.yaml) The Feishu configuration actually used by this project in `config.yaml`: ```yaml heartbeat: # Heartbeat interval (seconds), default 3600 (1 hour) every: 3600 # The channel for returning heartbeat results target: feishu # Heartbeat effective time window (local time) # Daily report generation will be triggered during 18:00-18:30 active_hours: start: 18:00 end: 18:30 channels: feishu: # Feishu application configuration # Get方式: Feishu Open Platform → create an enterprise self-built application → get App ID and App Secret app_id: cli_a92035b1823a9cd2 app_secret: ***** encrypt_key: # Encryption key (optional) verification_token: # Verification token (optional) allow_from: # IP whitelist (optional) enabled: true ``` **Feishu application configuration steps**: 1. Access [Feishu Open Platform](https://open.feishu.cn/) 2. Create an enterprise self-built application to get `app_id` and `app_secret` 3. Add “Bot” capability 4. Configure event subscription: `im.message.receive_v1` 5. Publish the app to all members --- ## Chapter 7|Test and Verification ### 7.1 Test Data Collector ```bash # Test Git collection (collect commit records for the specified date) cd D:\Download\jiuwenswarm python workspace/agent/skills/daily-report/run_report.py daily --date 2026-03-07 # Test monthly report generation (collect data for the whole month) python workspace/agent/skills/daily-report/run_report.py monthly --year 2026 --month 3 # Test saving to a file python workspace/agent/skills/daily-report/run_report.py daily --save ``` ![](https://cdn.nlark.com/yuque/0/2026/png/27326384/1772862327714-d3e2c75f-83bc-46fc-8c25-e95e723f9dee.png) ### 7.2 Complete Test Procedure #### Step 1:Create a To-do List (test to-do data collection) Send in Feishu or the Web frontend: ```plain Help me create a to-do list: 1. Complete daily report generator skill development 2. Implement the Git commit data collection module 3. Implement the email statistics data collection module 4. Configure Feishu channel push 5. Test the heartbeat trigger function 6. Write the development documentation ``` ![](https://cdn.nlark.com/yuque/0/2026/png/27326384/1772862423989-5bdc4508-5cfe-4465-86c1-ba696f0246f8.png) #### Step 2:Simulate Work Records (test memory data collection) ```plain Help me record today's work: - In the morning, completed the writing of the SKILL.md skill definition file - Created the Git commit collector git_collector.py - Created the email statistics collector email_collector.py - Created the memory data collector memory_collector.py - Created the to-do items collector todo_collector.py - In the afternoon, completed the work analysis engine work_analyzer.py - Implemented the report generator report_generator.py - Configured heartbeat and Feishu push - Conducted functional testing and debugging ``` ![](https://cdn.nlark.com/yuque/0/2026/png/27326384/1772862487637-8206f051-3e56-42b9-acc5-fa6a9d2ae819.png) ![](https://cdn.nlark.com/yuque/0/2026/png/27326384/1772862510145-a395e7f8-075a-4f4c-8757-10d4ba4074d9.png) #### Step 3:Submit Code (test Git data collection) ```bash # Submit some code in the project to test Git collection git add . git commit -m "feat: Add complete daily report generator function - Implement multi-data source collection (Git/email/memory/todo) - Add work analysis engine - Support daily/weekly/monthly report generation - Configure Feishu push and heartbeat trigger" ``` ![](https://cdn.nlark.com/yuque/0/2026/png/27326384/1772862545512-3cf0d5fd-ab1e-4d4a-bea2-520a843302f4.png) ![](https://cdn.nlark.com/yuque/0/2026/png/27326384/1772862651273-81909a36-6233-46a6-b35f-2e21f783f7cc.png) #### Step 4:Generate the Daily Report ```plain Generate today's daily report ``` ![](https://cdn.nlark.com/yuque/0/2026/png/27326384/1772863455503-33da3fbd-859f-4ea0-8578-7a846c5438c4.png) #### Step 5:Generate the Monthly Report (test email data collection) ```plain Read this month's content in the mailbox and organize it into a monthly report ``` ![](https://cdn.nlark.com/yuque/0/2026/png/27326384/1772866734099-2059a8b7-632a-4e42-978b-0bdc80f9c2b5.png) ## Chapter 9|Expansion Directions ### 9.1 More Data Sources | Data source | Collection method | Value | | --- | --- | --- | | WeCom/ DingTalk | API | Message communication statistics | | Schedules/Calendar | CalDAV/iCal | Meeting time analysis | | Jira/Feishu tasks | API | Project progress tracking | | Browser history | Local database | Tracing work content | ### 9.2 Smarter Analysis + **Work mode recognition**:identify efficient time periods, inefficient time periods + **Fatigue level early warning**:based on continuous work duration + **Time allocation suggestions**:optimize task priority ### 9.3 Richer Interaction + **Feishu button interaction**:edit, regenerate, push + **Daily report editing functionality**:save after online modification + **Approval process**:Leader review and confirmation --- ## To end with From the original simple idea—“Can AI help me write daily reports”—to this now complete multi-data source daily report generation system, this project has undergone multiple iterations and optimizations. The biggest challenge encountered during development was **IMAP protocol adaptation for the 163 email**. NetEase mail security restrictions caused the `SELECT` command to return the “Unsafe Login” error. After repeated debugging and consulting materials, the following solution was ultimately used: 1. **Register ID command**:`imaplib.Commands['ID'] = ('NONAUTH', 'AUTH', 'SELECTED')` 2. **Send identity declaration**:immediately send the `ID` command after logging in 3. **Use STATUS command**:bypass the SELECT restriction to obtain email statistics This system can now: - Automatically collect Git commits, email statistics, memory records, and to-do items - Generate daily, weekly, and monthly reports - Push on a schedule through Feishu - Read email content and generate summaries If you are also trying to build similar AI Agent applications, I hope this article can provide you with some references. > **Let the AI Agent truly become an intelligent work assistant, starting with the advanced daily report generator.** > > — JiuwenSwarm Advanced daily report generator development practice --- **References**: - [163 email IMAP ID command solution](https://github.com/HKUDS/nanobot/issues/1123) - [NetEase email help center](https://help.mail.163.com/) - [Python imaplib documentation](https://docs.python.org/3/library/imaplib.html)