mrstack 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. mrstack/__init__.py +4 -0
  2. mrstack/_data/config/com.mrstack.claude-telegram.plist +25 -0
  3. mrstack/_data/config/mcp-config.example.json +23 -0
  4. mrstack/_data/config/start-daemon.sh +53 -0
  5. mrstack/_data/config/start.sh +29 -0
  6. mrstack/_data/schedulers/manage-jobs.sh +87 -0
  7. mrstack/_data/schedulers/morning-briefing.sh +29 -0
  8. mrstack/_data/schedulers/register-jobs.py +182 -0
  9. mrstack/_data/schedulers/run-threads-briefing.sh +36 -0
  10. mrstack/_data/schedulers/weekly-review.sh +26 -0
  11. mrstack/_data/templates/DESIGN-GUIDE.md +160 -0
  12. mrstack/_data/templates/alert.md +56 -0
  13. mrstack/_data/templates/evening-summary.md +73 -0
  14. mrstack/_data/templates/jarvis-alert.md +64 -0
  15. mrstack/_data/templates/morning-briefing.md +53 -0
  16. mrstack/_data/templates/weekly-review.md +79 -0
  17. mrstack/_overlay/api/dashboard.py +223 -0
  18. mrstack/_overlay/api/templates/dashboard.html +328 -0
  19. mrstack/_overlay/bot/handlers/callback.py +1432 -0
  20. mrstack/_overlay/bot/handlers/command.py +1541 -0
  21. mrstack/_overlay/bot/utils/keyboards.py +125 -0
  22. mrstack/_overlay/bot/utils/ui_components.py +166 -0
  23. mrstack/_overlay/claude/session.py +341 -0
  24. mrstack/_overlay/jarvis/__init__.py +77 -0
  25. mrstack/_overlay/jarvis/coach.py +122 -0
  26. mrstack/_overlay/jarvis/context_engine.py +463 -0
  27. mrstack/_overlay/jarvis/pattern_learner.py +255 -0
  28. mrstack/_overlay/jarvis/persona.py +84 -0
  29. mrstack/_overlay/jarvis/platform.py +182 -0
  30. mrstack/_overlay/knowledge/__init__.py +6 -0
  31. mrstack/_overlay/knowledge/manager.py +464 -0
  32. mrstack/_overlay/knowledge/memory_index.py +180 -0
  33. mrstack/cli.py +330 -0
  34. mrstack/constants.py +77 -0
  35. mrstack/daemon.py +325 -0
  36. mrstack/patcher.py +169 -0
  37. mrstack/wizard.py +271 -0
  38. mrstack-1.1.0.dist-info/METADATA +640 -0
  39. mrstack-1.1.0.dist-info/RECORD +42 -0
  40. mrstack-1.1.0.dist-info/WHEEL +4 -0
  41. mrstack-1.1.0.dist-info/entry_points.txt +2 -0
  42. mrstack-1.1.0.dist-info/licenses/LICENSE +21 -0
mrstack/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ """Mr.Stack — Proactive AI Butler for Claude Code."""
2
+
3
+ __version__ = "1.1.0"
4
+ __app_name__ = "Mr.Stack"
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key>
6
+ <string>com.mrstack.claude-telegram</string>
7
+ <key>ProgramArguments</key>
8
+ <array>
9
+ <string>/bin/bash</string>
10
+ <string>$HOME/claude-telegram/start-daemon.sh</string>
11
+ </array>
12
+ <key>RunAtLoad</key>
13
+ <true/>
14
+ <key>KeepAlive</key>
15
+ <true/>
16
+ <key>ThrottleInterval</key>
17
+ <integer>30</integer>
18
+ <key>StandardOutPath</key>
19
+ <string>$HOME/claude-telegram/logs/daemon-stdout.log</string>
20
+ <key>StandardErrorPath</key>
21
+ <string>$HOME/claude-telegram/logs/daemon-stderr.log</string>
22
+ <key>WorkingDirectory</key>
23
+ <string>$HOME/claude-telegram</string>
24
+ </dict>
25
+ </plist>
@@ -0,0 +1,23 @@
1
+ {
2
+ "mcpServers": {
3
+ "google-calendar": {
4
+ "type": "http",
5
+ "url": "https://gcal.mcp.claude.com/mcp"
6
+ },
7
+ "notion": {
8
+ "command": "npx",
9
+ "args": ["-y", "@notionhq/notion-mcp-server"],
10
+ "env": {
11
+ "OPENAPI_MCP_HEADERS": "{\"Authorization\": \"Bearer YOUR_NOTION_API_KEY\", \"Notion-Version\": \"2022-06-28\"}"
12
+ }
13
+ },
14
+ "playwright": {
15
+ "command": "npx",
16
+ "args": ["-y", "@playwright/mcp"]
17
+ },
18
+ "applescript": {
19
+ "command": "npx",
20
+ "args": ["-y", "@peakmojo/applescript-mcp"]
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,53 @@
1
+ #!/bin/bash
2
+ # Daemon startup script for launchd (foreground execution)
3
+ # Use start.sh for manual/tmux execution instead
4
+
5
+ cd "$HOME/claude-telegram"
6
+
7
+ # Load environment variables
8
+ set -a
9
+ source .env
10
+ set +a
11
+
12
+ # Ensure claude CLI and other tools are in PATH
13
+ export PATH="$HOME/.local/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"
14
+
15
+ # Sleep prevention: only on AC power
16
+ # Bot always runs; caffeinate runs alongside only when on AC.
17
+ # A background loop checks power state every 60s and toggles caffeinate.
18
+
19
+ CAFF_PID=""
20
+
21
+ cleanup() {
22
+ [ -n "$CAFF_PID" ] && kill "$CAFF_PID" 2>/dev/null
23
+ kill "$BOT_PID" 2>/dev/null
24
+ wait
25
+ exit 0
26
+ }
27
+ trap cleanup SIGTERM SIGINT
28
+
29
+ is_on_ac() {
30
+ pmset -g batt | grep -q "AC Power"
31
+ }
32
+
33
+ # Start the bot in background
34
+ claude-telegram-bot &
35
+ BOT_PID=$!
36
+
37
+ # Power monitor loop
38
+ while kill -0 "$BOT_PID" 2>/dev/null; do
39
+ if is_on_ac; then
40
+ if [ -z "$CAFF_PID" ] || ! kill -0 "$CAFF_PID" 2>/dev/null; then
41
+ caffeinate -s -i -w "$BOT_PID" &
42
+ CAFF_PID=$!
43
+ fi
44
+ else
45
+ if [ -n "$CAFF_PID" ] && kill -0 "$CAFF_PID" 2>/dev/null; then
46
+ kill "$CAFF_PID" 2>/dev/null
47
+ CAFF_PID=""
48
+ fi
49
+ fi
50
+ sleep 60
51
+ done
52
+
53
+ wait "$BOT_PID"
@@ -0,0 +1,29 @@
1
+ #!/bin/bash
2
+ cd "$HOME/claude-telegram"
3
+
4
+ # Load env for webhook secret
5
+ set -a
6
+ source .env
7
+ set +a
8
+
9
+ export PATH="$HOME/.local/bin:$PATH"
10
+
11
+ # Start bot in background, wait for health check, then notify
12
+ claude-telegram-bot &
13
+ BOT_PID=$!
14
+
15
+ # Wait for API server to be ready (max 30 seconds)
16
+ for i in $(seq 1 30); do
17
+ if curl -s http://localhost:8080/health >/dev/null 2>&1; then
18
+ # Send startup notification via webhook
19
+ curl -s -X POST http://localhost:8080/webhooks/system \
20
+ -H "Authorization: Bearer $WEBHOOK_API_SECRET" \
21
+ -H "Content-Type: application/json" \
22
+ -d '{"event": "bot_started", "message": "Bot online and ready."}' >/dev/null 2>&1
23
+ break
24
+ fi
25
+ sleep 1
26
+ done
27
+
28
+ # Wait for bot process
29
+ wait $BOT_PID
@@ -0,0 +1,87 @@
1
+ #!/bin/bash
2
+ # Manage scheduled jobs in the bot's SQLite database
3
+ DB="$HOME/claude-telegram/data/bot.db"
4
+
5
+ usage() {
6
+ echo "Usage: $0 {list|add|remove|toggle|update-cron}"
7
+ echo ""
8
+ echo " list List all registered jobs"
9
+ echo " add <name> <cron> <prompt> Add a new job"
10
+ echo " remove <job-name> Remove a job by name"
11
+ echo " toggle <job-name> Toggle job active/inactive"
12
+ echo " update-cron <job-name> <cron> Update a job's cron expression"
13
+ echo ""
14
+ echo "Note: Restart bot after changes for scheduler to reload."
15
+ }
16
+
17
+ case "${1:-}" in
18
+ list)
19
+ echo "=== Scheduled Jobs ==="
20
+ sqlite3 -header -column "$DB" \
21
+ "SELECT job_name, cron_expression, is_active,
22
+ substr(prompt, 1, 50) || '...' as prompt_preview
23
+ FROM scheduled_jobs
24
+ ORDER BY job_name"
25
+ echo ""
26
+ echo "Total: $(sqlite3 "$DB" "SELECT COUNT(*) FROM scheduled_jobs") jobs"
27
+ echo "Active: $(sqlite3 "$DB" "SELECT COUNT(*) FROM scheduled_jobs WHERE is_active=1") jobs"
28
+ ;;
29
+
30
+ add)
31
+ if [ $# -lt 4 ]; then
32
+ echo "Usage: $0 add <name> <cron> <prompt>"
33
+ exit 1
34
+ fi
35
+ JOB_NAME="$2"
36
+ CRON="$3"
37
+ PROMPT="$4"
38
+ JOB_ID=$(python3 -c "import uuid; print(uuid.uuid4())")
39
+ NOW=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
40
+
41
+ sqlite3 "$DB" "INSERT INTO scheduled_jobs
42
+ (job_id, job_name, cron_expression, prompt, target_chat_ids, working_directory, is_active, created_at, updated_at)
43
+ VALUES ('$JOB_ID', '$JOB_NAME', '$CRON', '$PROMPT', 'YOUR_CHAT_ID', '$HOME', 1, '$NOW', '$NOW')"
44
+ echo "Created: $JOB_NAME ($CRON) -> $JOB_ID"
45
+ echo "Restart bot to apply."
46
+ ;;
47
+
48
+ remove)
49
+ if [ $# -lt 2 ]; then
50
+ echo "Usage: $0 remove <job-name>"
51
+ exit 1
52
+ fi
53
+ JOB_NAME="$2"
54
+ sqlite3 "$DB" "DELETE FROM scheduled_jobs WHERE job_name='$JOB_NAME'"
55
+ echo "Removed: $JOB_NAME"
56
+ echo "Restart bot to apply."
57
+ ;;
58
+
59
+ toggle)
60
+ if [ $# -lt 2 ]; then
61
+ echo "Usage: $0 toggle <job-name>"
62
+ exit 1
63
+ fi
64
+ JOB_NAME="$2"
65
+ sqlite3 "$DB" "UPDATE scheduled_jobs SET is_active = NOT is_active WHERE job_name='$JOB_NAME'"
66
+ STATUS=$(sqlite3 "$DB" "SELECT CASE WHEN is_active THEN 'ACTIVE' ELSE 'INACTIVE' END FROM scheduled_jobs WHERE job_name='$JOB_NAME'")
67
+ echo "$JOB_NAME -> $STATUS"
68
+ echo "Restart bot to apply."
69
+ ;;
70
+
71
+ update-cron)
72
+ if [ $# -lt 3 ]; then
73
+ echo "Usage: $0 update-cron <job-name> <new-cron>"
74
+ exit 1
75
+ fi
76
+ JOB_NAME="$2"
77
+ NEW_CRON="$3"
78
+ sqlite3 "$DB" "UPDATE scheduled_jobs SET cron_expression='$NEW_CRON' WHERE job_name='$JOB_NAME'"
79
+ echo "$JOB_NAME cron -> $NEW_CRON"
80
+ echo "Restart bot to apply."
81
+ ;;
82
+
83
+ *)
84
+ usage
85
+ exit 1
86
+ ;;
87
+ esac
@@ -0,0 +1,29 @@
1
+ #!/bin/bash
2
+ export PATH="$HOME/.local/bin:/opt/homebrew/bin:$PATH"
3
+
4
+ set -a
5
+ source "$HOME/claude-telegram/.env"
6
+ set +a
7
+
8
+ # Use Claude Code to generate briefing
9
+ CLAUDECODE= claude --print --output-format text "
10
+ You are DD's personal assistant. Generate a morning briefing.
11
+ Check:
12
+ 1. Today's calendar events (if Google Calendar MCP available)
13
+ 2. Unread important emails (if Gmail MCP available)
14
+ 3. Pending issues/tasks (if Linear/Jira MCP available)
15
+ 4. Unread Slack mentions (if Slack MCP available)
16
+ 5. Latest AI news from ~/claude-telegram/scrapers/threads/output/
17
+
18
+ Format as a concise Telegram message in Korean.
19
+ Start with: 🌅 오전 브리핑 (date)
20
+ " 2>/dev/null > /tmp/morning-briefing.txt
21
+
22
+ # Send via Telegram
23
+ MSG=$(cat /tmp/morning-briefing.txt)
24
+ if [ -n "$MSG" ]; then
25
+ curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
26
+ -H "Content-Type: application/json" \
27
+ -d "{\"chat_id\":\"${NOTIFICATION_CHAT_IDS}\",\"text\":$(echo "$MSG" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))')}" \
28
+ > /dev/null
29
+ fi
@@ -0,0 +1,182 @@
1
+ #!/usr/bin/env python3
2
+ """Register scheduled jobs into the bot's SQLite database."""
3
+
4
+ import sqlite3
5
+ import uuid
6
+ from datetime import datetime
7
+
8
+ import os
9
+
10
+ DB_PATH = os.path.expanduser("~/claude-telegram/data/bot.db")
11
+ CHAT_ID = os.environ.get("NOTIFICATION_CHAT_IDS", "YOUR_CHAT_ID")
12
+ WORKING_DIR = os.path.expanduser("~")
13
+
14
+ # Model routing:
15
+ # "sonnet" — simple lookups, status checks, no creative work
16
+ # "opus" — complex analysis, writing, multi-step reasoning
17
+ # None — inherit CLI default (opus)
18
+
19
+ JOBS = [
20
+ {
21
+ "job_name": "morning-briefing",
22
+ "cron_expression": "0 8 * * 1-5",
23
+ "model": "sonnet",
24
+ "prompt": """아침 브리핑:
25
+ 1. 오늘 날짜/요일
26
+ 2. Google Calendar MCP로 오늘 일정
27
+ 3. ~/claude-telegram/memory/ 어제 daily 요약 + pending
28
+ 4. ~/claude-telegram/scrapers/threads/output/ 최신 AI 뉴스 (1파일)
29
+ 5. 오늘 할 일 제안
30
+
31
+ ~/claude-telegram/templates/morning-briefing.md 템플릿 참고.""",
32
+ },
33
+ {
34
+ "job_name": "evening-summary",
35
+ "cron_expression": "0 21 * * *",
36
+ "model": "opus",
37
+ "prompt": """일일 요약 작성 + 저장:
38
+ 1. 오늘 대화 핵심 정리
39
+ 2. 결정사항, 완료 작업, 새 정보
40
+ 3. 내일 할 일
41
+ 4. ~/claude-telegram/memory/daily/YYYY-MM-DD.md 저장
42
+ 5. decisions/log.md 업데이트 (있으면)
43
+ 6. projects/ 해당 파일 업데이트 (있으면)
44
+ 7. [Coach] ~/claude-telegram/memory/patterns/interactions.jsonl 분석:
45
+ - 총 요청 수, 유형 분포, 피크 시간대
46
+ - 디버깅 비율 지적, 컨텍스트 전환 패턴
47
+ - 코칭 포인트 2-3개 (interactions.jsonl 없으면 건너뛰기)
48
+
49
+ 파일 저장 후 텔레그램으로 요약 전송. ~/claude-telegram/templates/morning-briefing.md 형식 참고.""",
50
+ },
51
+ {
52
+ "job_name": "weekly-review",
53
+ "cron_expression": "0 18 * * 5",
54
+ "model": "opus",
55
+ "prompt": """주간 회고:
56
+ 1. ~/claude-telegram/memory/daily/ 이번 주 파일들
57
+ 2. 성과 요약
58
+ 3. 주요 결정사항 + 이유
59
+ 4. 배운 것 / 개선점
60
+ 5. 다음 주 계획
61
+
62
+ ~/claude-telegram/templates/weekly-review.md 템플릿 참고.""",
63
+ },
64
+ {
65
+ "job_name": "calendar-check",
66
+ "cron_expression": "0 9,12,15,18 * * 1-5",
67
+ "model": "haiku",
68
+ "prompt": """Google Calendar MCP로 3시간 내 일정 확인.
69
+ 있으면: 시간, 제목, 장소, 준비사항.
70
+ 없으면 빈 응답.""",
71
+ },
72
+ {
73
+ "job_name": "threads-notify",
74
+ "cron_expression": "30 8,20 * * *",
75
+ "model": "sonnet",
76
+ "prompt": """~/claude-telegram/scrapers/threads/output/ 최신 JSON 분석:
77
+ 1. 최근 1파일의 AI/테크 포스트 요약 (최대 5개)
78
+ 2. 포스트별: 주제, 핵심 1-2줄, 링크
79
+ 3. 트렌드 한줄 요약
80
+ 4. Notion MCP → "AI 업무 로그" DB(data_source_id: 14e322c2-110e-4cce-9a22-f2c529abb54e) 저장:
81
+ 제목: "[Threads] YYYY-MM-DD 오전/오후 AI 뉴스", 카테고리: "학습"
82
+ (Notion 불가시 건너뛰기)
83
+
84
+ 새 파일 없거나 이미 브리핑한 파일이면 빈 응답.""",
85
+ },
86
+ {
87
+ "job_name": "memory-sync",
88
+ "cron_expression": "0 */3 * * *",
89
+ "model": "sonnet",
90
+ "prompt": """메모리 동기화 — 최근 대화에서 추출:
91
+ - 새 사람 → ~/claude-telegram/memory/people/{name}.md
92
+ - 프로젝트 → projects/{name}.md
93
+ - 선호도 → preferences/
94
+ - 지식 → knowledge/
95
+ - 결정 → decisions/log.md""",
96
+ },
97
+ {
98
+ "job_name": "notion-sync",
99
+ "cron_expression": "0 22 * * *",
100
+ "model": "sonnet",
101
+ "prompt": """오늘 중요 대화/결정을 Notion 저장:
102
+ 1. 기술 결정, 진행상황, 학습 내용 추출
103
+ 2. Notion MCP → "AI 업무 로그" DB(data_source_id: 14e322c2-110e-4cce-9a22-f2c529abb54e)
104
+ 3. properties: 제목, 카테고리(결정/진행/학습/이슈/아이디어), 날짜, 요약
105
+ 4. Notion 불가시 ~/claude-telegram/memory/daily/ 기록 + 상태 알림""",
106
+ },
107
+ {
108
+ "job_name": "github-check",
109
+ "cron_expression": "0 */2 * * *",
110
+ "model": "haiku",
111
+ "prompt": """`gh api notifications` 로 읽지 않은 알림 확인.
112
+ 새 PR/이슈/코멘트 요약. PR review, 멘션, CI 실패 강조.""",
113
+ },
114
+ {
115
+ "job_name": "token-check",
116
+ "cron_expression": "0 10 * * *",
117
+ "model": "haiku",
118
+ "prompt": """~/.claude/.credentials.json 토큰 확인.
119
+ 만료 7일 이내면 경고. 만료/파일없음이면 "claude CLI 실행하여 갱신" 알림.""",
120
+ },
121
+ ]
122
+
123
+
124
+ def register():
125
+ conn = sqlite3.connect(DB_PATH)
126
+ cursor = conn.cursor()
127
+
128
+ for job in JOBS:
129
+ # Check if job already exists
130
+ cursor.execute(
131
+ "SELECT job_id FROM scheduled_jobs WHERE job_name = ?",
132
+ (job["job_name"],),
133
+ )
134
+ existing = cursor.fetchone()
135
+
136
+ if existing:
137
+ # Update existing job
138
+ cursor.execute(
139
+ """UPDATE scheduled_jobs
140
+ SET cron_expression = ?, prompt = ?, target_chat_ids = ?,
141
+ working_directory = ?, model = ?, is_active = 1, updated_at = ?
142
+ WHERE job_name = ?""",
143
+ (
144
+ job["cron_expression"],
145
+ job["prompt"],
146
+ CHAT_ID,
147
+ WORKING_DIR,
148
+ job.get("model"),
149
+ datetime.now().isoformat(),
150
+ job["job_name"],
151
+ ),
152
+ )
153
+ print(f"Updated: {job['job_name']} ({job['cron_expression']}) model={job.get('model', 'default')}")
154
+ else:
155
+ # Insert new job
156
+ job_id = str(uuid.uuid4())
157
+ cursor.execute(
158
+ """INSERT INTO scheduled_jobs
159
+ (job_id, job_name, cron_expression, prompt, target_chat_ids,
160
+ working_directory, model, is_active, created_at, updated_at)
161
+ VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?, ?)""",
162
+ (
163
+ job_id,
164
+ job["job_name"],
165
+ job["cron_expression"],
166
+ job["prompt"],
167
+ CHAT_ID,
168
+ WORKING_DIR,
169
+ job.get("model"),
170
+ datetime.now().isoformat(),
171
+ datetime.now().isoformat(),
172
+ ),
173
+ )
174
+ print(f"Created: {job['job_name']} ({job['cron_expression']}) model={job.get('model', 'default')} -> {job_id}")
175
+
176
+ conn.commit()
177
+ conn.close()
178
+ print(f"\nDone. {len(JOBS)} jobs registered.")
179
+
180
+
181
+ if __name__ == "__main__":
182
+ register()
@@ -0,0 +1,36 @@
1
+ #!/bin/bash
2
+ export PATH="$HOME/.local/bin:/opt/homebrew/bin:$PATH"
3
+ cd "$HOME/claude-telegram/scrapers/threads"
4
+
5
+ # Source env
6
+ set -a
7
+ source "$HOME/claude-telegram/.env"
8
+ set +a
9
+
10
+ # Run scraper
11
+ node scraper.js 2>&1 | tee -a "$HOME/claude-telegram/logs/threads.log"
12
+
13
+ # Get latest output file
14
+ LATEST=$(ls -t output/*.json 2>/dev/null | head -1)
15
+
16
+ if [ -n "$LATEST" ]; then
17
+ POST_COUNT=$(python3 -c "import json; print(len(json.load(open('$LATEST'))))" 2>/dev/null || echo "0")
18
+
19
+ # Send to bot's webhook API for Claude-powered analysis
20
+ WEBHOOK_RESULT=$(curl -s -o /dev/null -w "%{http_code}" \
21
+ -X POST http://localhost:8080/webhooks/threads \
22
+ -H "Authorization: Bearer $WEBHOOK_API_SECRET" \
23
+ -H "Content-Type: application/json" \
24
+ -d "{\"file\": \"$LATEST\", \"count\": $POST_COUNT}" 2>/dev/null)
25
+
26
+ if [ "$WEBHOOK_RESULT" = "200" ] || [ "$WEBHOOK_RESULT" = "202" ]; then
27
+ echo "Webhook sent successfully (HTTP $WEBHOOK_RESULT)" | tee -a "$HOME/claude-telegram/logs/threads.log"
28
+ else
29
+ echo "Webhook failed (HTTP $WEBHOOK_RESULT), falling back to direct briefing" | tee -a "$HOME/claude-telegram/logs/threads.log"
30
+ # Fallback: direct briefing
31
+ node briefing.js "$LATEST" 2>&1 | tee -a "$HOME/claude-telegram/logs/threads.log"
32
+ fi
33
+
34
+ # Save to Notion via Claude Code
35
+ bash "$HOME/claude-telegram/scrapers/threads/save-to-notion.sh" "$LATEST" 2>&1 | tee -a "$HOME/claude-telegram/logs/threads.log"
36
+ fi
@@ -0,0 +1,26 @@
1
+ #!/bin/bash
2
+ export PATH="$HOME/.local/bin:/opt/homebrew/bin:$PATH"
3
+
4
+ set -a
5
+ source "$HOME/claude-telegram/.env"
6
+ set +a
7
+
8
+ CLAUDECODE= claude --print --output-format text "
9
+ You are DD's personal assistant. Generate a weekly review.
10
+ Check:
11
+ 1. This week's completed tasks and accomplishments
12
+ 2. Next week's scheduled events from Google Calendar
13
+ 3. AI trends summary from ~/claude-telegram/scrapers/threads/output/ (this week's files)
14
+ 4. Any pending items that need attention
15
+
16
+ Format as a concise Telegram message in Korean.
17
+ Start with: 📊 주간 회고 (date range)
18
+ " 2>/dev/null > /tmp/weekly-review.txt
19
+
20
+ MSG=$(cat /tmp/weekly-review.txt)
21
+ if [ -n "$MSG" ]; then
22
+ curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
23
+ -H "Content-Type: application/json" \
24
+ -d "{\"chat_id\":\"${NOTIFICATION_CHAT_IDS}\",\"text\":$(echo "$MSG" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))')}" \
25
+ > /dev/null
26
+ fi
@@ -0,0 +1,160 @@
1
+ # Mr.Stack Message Design Guide
2
+
3
+ Mr.Stack 텔레그램 봇의 메시지 디자인 시스템 규칙.
4
+
5
+ ---
6
+
7
+ ## 1. 메시지 구조
8
+
9
+ ### 헤더/푸터 구분선
10
+ - 상단: `━━━ Mr.Stack {제목} ━━━`
11
+ - 하단: `━━━━━━━━━━━━━━━━━━━━` (━ x 20)
12
+ - 문자: ━ (U+2501, BOX DRAWINGS HEAVY HORIZONTAL)
13
+ - Jarvis 알림은 구분선 없이 `[Mr.Stack]` prefix만 사용
14
+
15
+ ### 섹션 prefix
16
+ - `▸` (U+25B8, BLACK RIGHT-POINTING SMALL TRIANGLE)
17
+ - 섹션 제목 앞에 사용: `▸ 오늘 일정`
18
+ - 텔레그램 HTML: `<b>▸ 섹션 제목</b>`
19
+
20
+ ### 들여쓰기
21
+ - 섹션 내용은 공백 2칸 들여쓰기
22
+ - 일정/태스크 항목 정렬 유지
23
+
24
+ ---
25
+
26
+ ## 2. 프로그레스 바
27
+
28
+ ### 기본 규칙
29
+ - 총 10칸 고정
30
+ - 채워진 칸: █ (U+2588, FULL BLOCK)
31
+ - 빈 칸: ░ (U+2591, LIGHT SHADE)
32
+
33
+ ### 변환 공식
34
+ ```
35
+ filled = round(value / max_value * 10)
36
+ empty = 10 - filled
37
+ bar = "█" * filled + "░" * empty
38
+ ```
39
+
40
+ ### 예시
41
+ | 값 | 바 | 표기 |
42
+ |---|---|---|
43
+ | 0% | ░░░░░░░░░░ | 0/10 |
44
+ | 30% | ███░░░░░░░ | 3/10 |
45
+ | 50% | █████░░░░░ | 5/10 |
46
+ | 80% | ████████░░ | 8/10 |
47
+ | 100% | ██████████ | 10/10 |
48
+
49
+ ### 용도별
50
+ - **생산성 스코어**: value=score, max=10
51
+ - **시간 분석**: value=hours, max=total_working_hours
52
+ - **프로젝트 비율**: value=percentage, max=100
53
+
54
+ ---
55
+
56
+ ## 3. 상태 이모지 매핑
57
+
58
+ | 기호 | 의미 | 용도 |
59
+ |------|------|------|
60
+ | ✓ | 완료/성공 | 완료된 태스크, 성공한 작업 |
61
+ | ✗ | 실패/에러 | 실패한 빌드, 에러 발생 |
62
+ | ◻ | 미완료/대기 | Pending 태스크, 체크리스트 |
63
+ | · | 정보/항목 | 뉴스, 리스트 아이템, 일반 나열 |
64
+ | — | 없음/구분 | "일정 없음", 설명 구분 |
65
+
66
+ ### 알림 등급 이모지
67
+
68
+ | 이모지 | 등급 | 사용 상황 |
69
+ |--------|------|-----------|
70
+ | 🔴 | 긴급 | 즉시 조치 (서비스 장애, 보안 이슈, 배터리 < 10%) |
71
+ | 🟡 | 주의 | 확인 필요 (성능 저하, 이상 패턴, 배터리 < 30%) |
72
+ | 🟢 | 정보 | 참고용 (작업 완료, 상태 변경) |
73
+ | ⚪ | 시스템 | 내부 상태 (토큰 갱신, 동기화, 메모리 업데이트) |
74
+
75
+ ### 카테고리 이모지
76
+
77
+ | 이모지 | 용도 |
78
+ |--------|------|
79
+ | 📅 | 날짜, 일정 |
80
+ | 📊 | 통계, 분석, 리포트 |
81
+ | 💡 | 코칭, 인사이트, 팁 |
82
+ | 🏷️ | 키워드, 태그 |
83
+
84
+ ---
85
+
86
+ ## 4. Jarvis 선제 알림 포맷
87
+
88
+ ### 구조
89
+ ```
90
+ [Mr.Stack] {메시지}
91
+ ```
92
+
93
+ ### 규칙
94
+ - prefix: 반드시 `[Mr.Stack]`으로 시작
95
+ - 길이: 한 줄, 최대 100자
96
+ - 조치 필요 시: `—` (em dash) 뒤에 구체적 행동 제안
97
+ - 존댓말 사용, 간결하게
98
+ - 이모지 사용 금지 (prefix가 브랜딩 역할)
99
+
100
+ ### 텔레그램 HTML
101
+ ```html
102
+ <b>[Mr.Stack]</b> {메시지}
103
+ ```
104
+
105
+ 강조가 필요한 수치:
106
+ ```html
107
+ <b>[Mr.Stack]</b> 배터리 <b>12%</b> — 저장하시고 충전하세요.
108
+ ```
109
+
110
+ ### 알림음 정책
111
+ - 긴급 (배터리 < 10%, 장애): `disable_notification=false`
112
+ - 일반 (정보, 상태): `disable_notification=true`
113
+
114
+ ---
115
+
116
+ ## 5. 텔레그램 HTML 호환 태그
117
+
118
+ 텔레그램 Bot API `parse_mode=HTML`에서 지원하는 태그:
119
+
120
+ | 태그 | 용도 | 예시 |
121
+ |------|------|------|
122
+ | `<b>text</b>` | 굵게 | 섹션 제목, 강조 수치 |
123
+ | `<i>text</i>` | 기울임 | 코칭 라인, 키워드 |
124
+ | `<code>text</code>` | 인라인 코드 | 변수명, 명령어, 브랜치명 |
125
+ | `<pre>text</pre>` | 코드 블록 | 멀티라인 코드 |
126
+ | `<a href="url">text</a>` | 링크 | 외부 URL |
127
+ | `<s>text</s>` | 취소선 | 취소된 항목 |
128
+ | `<u>text</u>` | 밑줄 | 특별 강조 (절제해서 사용) |
129
+ | `<tg-spoiler>text</tg-spoiler>` | 스포일러 | 숨김 처리 |
130
+ | `<blockquote>text</blockquote>` | 인용 | 인용문 |
131
+
132
+ ### 주의사항
133
+ - `<`, `>`, `&` 문자는 반드시 이스케이프: `&lt;`, `&gt;`, `&amp;`
134
+ - 지원하지 않는 태그 사용 시 메시지 전송 실패
135
+ - 태그 중첩 가능하나 최소한으로 사용
136
+ - Markdown 파싱 모드와 혼용 불가 — HTML 모드만 사용
137
+
138
+ ---
139
+
140
+ ## 6. 템플릿 목록
141
+
142
+ | 파일 | 용도 | 발송 시점 |
143
+ |------|------|-----------|
144
+ | morning-briefing.md | 모닝 브리핑 | 평일 08:00 |
145
+ | evening-summary.md | 데일리 리포트 | 매일 21:00 |
146
+ | weekly-review.md | 주간 회고 | 금요일 18:00 |
147
+ | alert.md | 시스템/서비스 알림 | 이벤트 발생 시 |
148
+ | jarvis-alert.md | Jarvis 선제 알림 | ContextEngine 트리거 시 |
149
+
150
+ ---
151
+
152
+ ## 7. 일반 원칙
153
+
154
+ 1. **일관성**: 모든 정기 메시지는 구분선 + 섹션 prefix 패턴 사용
155
+ 2. **간결성**: 불필요한 수식어 제거, 데이터 중심
156
+ 3. **실행 가능성**: 알림에는 반드시 구체적 행동 제안 포함
157
+ 4. **연결성**: evening-summary의 "내일 제안" → morning-briefing의 "코칭" 라인으로 이어짐
158
+ 5. **한국어 기본**: 기술 용어, 브랜치명, 코드는 영어 유지
159
+ 6. **최대 길이 준수**: 각 템플릿별 글자 수 제한 엄수
160
+ 7. **이모지 절제**: 카테고리 이모지만 사용, 장식용 이모지 금지