daemora 1.0.1 → 1.0.3
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.
- package/README.md +106 -76
- package/SOUL.md +100 -28
- package/config/mcp.json +9 -9
- package/package.json +15 -8
- package/skills/apple-notes.md +0 -52
- package/skills/apple-reminders.md +1 -87
- package/skills/camsnap.md +20 -144
- package/skills/coding.md +7 -7
- package/skills/documents.md +6 -6
- package/skills/email.md +6 -6
- package/skills/gif-search.md +28 -171
- package/skills/healthcheck.md +21 -203
- package/skills/image-gen.md +24 -123
- package/skills/model-usage.md +18 -165
- package/skills/obsidian.md +28 -174
- package/skills/pdf.md +30 -181
- package/skills/research.md +6 -6
- package/skills/skill-creator.md +35 -111
- package/skills/spotify.md +2 -17
- package/skills/summarize.md +36 -193
- package/skills/things.md +23 -175
- package/skills/tmux.md +1 -91
- package/skills/trello.md +32 -157
- package/skills/video-frames.md +26 -166
- package/skills/weather.md +6 -6
- package/src/a2a/A2AClient.js +2 -2
- package/src/a2a/A2AServer.js +6 -6
- package/src/a2a/AgentCard.js +2 -2
- package/src/agents/SubAgentManager.js +61 -19
- package/src/agents/Supervisor.js +4 -4
- package/src/channels/BaseChannel.js +6 -6
- package/src/channels/BlueBubblesChannel.js +112 -0
- package/src/channels/DiscordChannel.js +8 -8
- package/src/channels/EmailChannel.js +54 -26
- package/src/channels/FeishuChannel.js +140 -0
- package/src/channels/GoogleChatChannel.js +8 -8
- package/src/channels/HttpChannel.js +2 -2
- package/src/channels/IRCChannel.js +144 -0
- package/src/channels/LineChannel.js +13 -13
- package/src/channels/MatrixChannel.js +97 -0
- package/src/channels/MattermostChannel.js +119 -0
- package/src/channels/NextcloudChannel.js +133 -0
- package/src/channels/NostrChannel.js +175 -0
- package/src/channels/SignalChannel.js +9 -9
- package/src/channels/SlackChannel.js +10 -10
- package/src/channels/TeamsChannel.js +10 -10
- package/src/channels/TelegramChannel.js +8 -8
- package/src/channels/TwitchChannel.js +128 -0
- package/src/channels/WhatsAppChannel.js +10 -10
- package/src/channels/ZaloChannel.js +119 -0
- package/src/channels/iMessageChannel.js +150 -0
- package/src/channels/index.js +241 -11
- package/src/cli.js +835 -38
- package/src/config/agentProfiles.js +19 -19
- package/src/config/channels.js +1 -1
- package/src/config/default.js +12 -7
- package/src/config/models.js +3 -3
- package/src/config/permissions.js +2 -2
- package/src/core/AgentLoop.js +13 -13
- package/src/core/Compaction.js +3 -3
- package/src/core/CostTracker.js +2 -2
- package/src/core/EventBus.js +15 -15
- package/src/core/TaskQueue.js +24 -7
- package/src/core/TaskRunner.js +19 -6
- package/src/daemon/DaemonManager.js +4 -4
- package/src/hooks/HookRunner.js +4 -4
- package/src/index.js +6 -2
- package/src/mcp/MCPAgentRunner.js +3 -3
- package/src/mcp/MCPClient.js +9 -9
- package/src/mcp/MCPManager.js +14 -14
- package/src/models/ModelRouter.js +2 -2
- package/src/safety/AuditLog.js +3 -3
- package/src/safety/CircuitBreaker.js +2 -2
- package/src/safety/CommandGuard.js +132 -0
- package/src/safety/FilesystemGuard.js +23 -3
- package/src/safety/GitRollback.js +5 -5
- package/src/safety/HumanApproval.js +9 -9
- package/src/safety/InputSanitizer.js +81 -8
- package/src/safety/PermissionGuard.js +2 -2
- package/src/safety/Sandbox.js +1 -1
- package/src/safety/SecretScanner.js +90 -28
- package/src/safety/SecretVault.js +2 -2
- package/src/scheduler/Heartbeat.js +3 -3
- package/src/scheduler/Scheduler.js +6 -6
- package/src/setup/theme.js +171 -66
- package/src/setup/wizard.js +432 -57
- package/src/skills/SkillLoader.js +145 -8
- package/src/storage/TaskStore.js +39 -15
- package/src/systemPrompt.js +45 -43
- package/src/tenants/TenantManager.js +79 -22
- package/src/tools/ToolRegistry.js +3 -3
- package/src/tools/applyPatch.js +2 -2
- package/src/tools/browserAutomation.js +4 -4
- package/src/tools/calendar.js +155 -0
- package/src/tools/clipboard.js +71 -0
- package/src/tools/contacts.js +138 -0
- package/src/tools/createDocument.js +2 -2
- package/src/tools/cronTool.js +14 -14
- package/src/tools/database.js +165 -0
- package/src/tools/editFile.js +10 -10
- package/src/tools/executeCommand.js +11 -3
- package/src/tools/generateImage.js +79 -0
- package/src/tools/gitTool.js +141 -0
- package/src/tools/glob.js +1 -1
- package/src/tools/googlePlaces.js +136 -0
- package/src/tools/grep.js +2 -2
- package/src/tools/iMessageTool.js +86 -0
- package/src/tools/imageAnalysis.js +3 -3
- package/src/tools/index.js +56 -2
- package/src/tools/makeVoiceCall.js +283 -0
- package/src/tools/manageAgents.js +2 -2
- package/src/tools/manageMCP.js +38 -20
- package/src/tools/memory.js +25 -32
- package/src/tools/messageChannel.js +1 -1
- package/src/tools/notification.js +90 -0
- package/src/tools/philipsHue.js +147 -0
- package/src/tools/projectTracker.js +8 -8
- package/src/tools/readFile.js +1 -1
- package/src/tools/readPDF.js +73 -0
- package/src/tools/screenCapture.js +6 -6
- package/src/tools/searchContent.js +2 -2
- package/src/tools/searchFiles.js +1 -1
- package/src/tools/sendEmail.js +79 -24
- package/src/tools/sendFile.js +4 -4
- package/src/tools/sonos.js +137 -0
- package/src/tools/sshTool.js +130 -0
- package/src/tools/textToSpeech.js +5 -5
- package/src/tools/transcribeAudio.js +4 -4
- package/src/tools/useMCP.js +4 -4
- package/src/tools/webFetch.js +2 -2
- package/src/tools/webSearch.js +1 -1
- package/src/utils/Embeddings.js +79 -0
- package/src/voice/VoiceSessionManager.js +170 -0
- package/src/voice/VoiceWebhook.js +188 -0
package/skills/research.md
CHANGED
|
@@ -5,9 +5,9 @@ triggers: research, search, find, look up, what is, how does, explain, learn, in
|
|
|
5
5
|
---
|
|
6
6
|
You are an expert researcher. When doing research tasks:
|
|
7
7
|
|
|
8
|
-
1. **Search first**
|
|
9
|
-
2. **Multiple sources**
|
|
10
|
-
3. **Cite sources**
|
|
11
|
-
4. **Be current**
|
|
12
|
-
5. **Summarize clearly**
|
|
13
|
-
6. **Distinguish fact from opinion**
|
|
8
|
+
1. **Search first** - Use webSearch to find relevant information before answering.
|
|
9
|
+
2. **Multiple sources** - Check at least 2-3 sources to verify information.
|
|
10
|
+
3. **Cite sources** - Include URLs and source names in your response.
|
|
11
|
+
4. **Be current** - Prefer recent sources over older ones.
|
|
12
|
+
5. **Summarize clearly** - Present findings in a structured, easy-to-read format.
|
|
13
|
+
6. **Distinguish fact from opinion** - Clearly mark what is established fact vs. interpretation.
|
package/skills/skill-creator.md
CHANGED
|
@@ -1,142 +1,66 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: skill-creator
|
|
3
|
-
description: Create new Daemora skills. Use when the user asks to create a new skill, package a capability, teach the agent a new behavior, or add a new skill file.
|
|
3
|
+
description: Create new Daemora skills. Use when the user asks to create a new skill, package a capability, teach the agent a new behavior, or add a new skill file.
|
|
4
4
|
triggers: create skill, new skill, add skill, make skill, package skill, teach agent, skill creator, write skill
|
|
5
|
+
metadata: {"daemora": {"emoji": "🛠️"}}
|
|
5
6
|
---
|
|
6
7
|
|
|
7
|
-
## What
|
|
8
|
+
## What is a skill
|
|
8
9
|
|
|
9
|
-
A
|
|
10
|
+
A `.md` file in `skills/`. When a task matches the skill's `triggers`, the skill's content is injected into the system prompt for that task.
|
|
10
11
|
|
|
11
|
-
## Skill
|
|
12
|
+
## Skill format
|
|
12
13
|
|
|
13
14
|
```markdown
|
|
14
15
|
---
|
|
15
|
-
name: skill-name
|
|
16
|
-
description:
|
|
17
|
-
Include the specific trigger phrases. Be comprehensive — the description
|
|
18
|
-
is how the agent decides whether to load this skill.
|
|
16
|
+
name: skill-name
|
|
17
|
+
description: What this skill does and WHEN to use it. Include trigger phrases - this is how the agent decides to load it.
|
|
19
18
|
triggers: keyword1, keyword2, phrase, another phrase
|
|
19
|
+
metadata: {"daemora": {"emoji": "🔧", "requires": {"bins": ["tool-name"]}, "install": ["brew install tool-name"], "os": ["darwin"]}}
|
|
20
20
|
---
|
|
21
21
|
|
|
22
|
-
## When to
|
|
22
|
+
## When to use
|
|
23
23
|
|
|
24
|
-
✅
|
|
25
|
-
❌ Cases where it does NOT apply
|
|
24
|
+
✅ Cases where this skill applies
|
|
25
|
+
❌ Cases where it does NOT apply
|
|
26
26
|
|
|
27
|
-
## Core
|
|
27
|
+
## Core behavior
|
|
28
28
|
|
|
29
|
-
[
|
|
29
|
+
[What to do, what commands to run, how to respond]
|
|
30
30
|
|
|
31
|
-
##
|
|
31
|
+
## Common commands
|
|
32
32
|
|
|
33
|
-
[
|
|
33
|
+
[Brief command examples - 2-5 lines, not full scripts]
|
|
34
34
|
|
|
35
|
-
##
|
|
35
|
+
## Errors
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
| Error | Fix |
|
|
38
|
+
|-------|-----|
|
|
39
|
+
| ... | ... |
|
|
38
40
|
```
|
|
39
41
|
|
|
40
|
-
## Creation
|
|
41
|
-
|
|
42
|
-
### Step 1 — Gather requirements
|
|
43
|
-
|
|
44
|
-
Ask (or infer from context):
|
|
45
|
-
- What specific task should this skill handle?
|
|
46
|
-
- What tools/commands/APIs does it use?
|
|
47
|
-
- Does it require any external tools to be installed?
|
|
48
|
-
- What platform does it run on (macOS/Linux/all)?
|
|
49
|
-
- What are the "don't use this for" cases?
|
|
50
|
-
|
|
51
|
-
### Step 2 — Design the skill
|
|
52
|
-
|
|
53
|
-
Plan what to include:
|
|
54
|
-
- **Trigger keywords**: think of every way a user might ask for this — colloquial terms, technical terms, action verbs
|
|
55
|
-
- **Core commands**: real, tested commands with correct flags
|
|
56
|
-
- **Error cases**: the 3-5 most common failure modes and their fixes
|
|
57
|
-
- **Response format**: how should the agent present results to the user
|
|
58
|
-
|
|
59
|
-
### Step 3 — Write and save
|
|
60
|
-
|
|
61
|
-
```python
|
|
62
|
-
#!/usr/bin/env python3
|
|
63
|
-
"""Create a new Daemora skill file."""
|
|
64
|
-
import re
|
|
65
|
-
from pathlib import Path
|
|
66
|
-
|
|
67
|
-
SKILLS_DIR = Path(__file__).parent.parent / "skills"
|
|
68
|
-
|
|
69
|
-
def create_skill(name: str, description: str, triggers: list[str], content: str) -> Path:
|
|
70
|
-
"""
|
|
71
|
-
name: slug like "my-skill"
|
|
72
|
-
description: full description for triggering
|
|
73
|
-
triggers: list of trigger keywords/phrases
|
|
74
|
-
content: markdown body of the skill (without frontmatter)
|
|
75
|
-
"""
|
|
76
|
-
# Validate name
|
|
77
|
-
if not re.match(r'^[a-z0-9-]+$', name):
|
|
78
|
-
raise ValueError(f"Skill name must be lowercase-hyphen: {name}")
|
|
79
|
-
|
|
80
|
-
trigger_str = ", ".join(triggers)
|
|
81
|
-
frontmatter = f"""---
|
|
82
|
-
name: {name}
|
|
83
|
-
description: {description}
|
|
84
|
-
triggers: {trigger_str}
|
|
85
|
-
---
|
|
86
|
-
|
|
87
|
-
"""
|
|
88
|
-
skill_path = SKILLS_DIR / f"{name}.md"
|
|
89
|
-
if skill_path.exists():
|
|
90
|
-
print(f"⚠️ Skill already exists: {skill_path}")
|
|
91
|
-
overwrite = input("Overwrite? (y/N): ").lower() == "y"
|
|
92
|
-
if not overwrite:
|
|
93
|
-
return skill_path
|
|
94
|
-
|
|
95
|
-
skill_path.write_text(frontmatter + content, encoding="utf-8")
|
|
96
|
-
print(f"✅ Skill created: {skill_path}")
|
|
97
|
-
return skill_path
|
|
98
|
-
```
|
|
42
|
+
## Creation process
|
|
99
43
|
|
|
100
|
-
|
|
44
|
+
1. **Understand the domain** - what tools/commands/APIs does it use? Platform requirements?
|
|
45
|
+
2. **Choose good triggers** - every way a user might ask: colloquial + technical + action verbs
|
|
46
|
+
3. **Write behavioral instructions** - what to DO, not code templates to copy-paste
|
|
47
|
+
4. **Include brief command examples** - 2-5 lines max per example
|
|
48
|
+
5. **Save as** `skills/skill-name.md`
|
|
101
49
|
|
|
102
|
-
After creating
|
|
50
|
+
## After creating
|
|
103
51
|
|
|
104
52
|
```bash
|
|
105
53
|
curl -s -X POST http://localhost:8081/skills/reload
|
|
106
|
-
# → {"loaded":
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
Or restart the agent: `daemora start`
|
|
110
|
-
|
|
111
|
-
## Skill Quality Checklist
|
|
112
|
-
|
|
113
|
-
Before finalizing a skill, verify:
|
|
114
|
-
|
|
115
|
-
- [ ] `description` clearly states WHAT the skill does and the key trigger phrases
|
|
116
|
-
- [ ] `triggers` covers all reasonable ways a user would ask for this
|
|
117
|
-
- [ ] Has "When to Use / When NOT to Use" section
|
|
118
|
-
- [ ] All commands are real and tested — no fictional examples
|
|
119
|
-
- [ ] Platform requirements noted (macOS-only, requires X installed, etc.)
|
|
120
|
-
- [ ] Error handling covers the most common failure modes
|
|
121
|
-
- [ ] No duplicate of existing skills — check `ls skills/`
|
|
122
|
-
|
|
123
|
-
## Example: Creating a "Trello" Skill
|
|
124
|
-
|
|
125
|
-
```
|
|
126
|
-
name: trello
|
|
127
|
-
description: Manage Trello boards, lists, and cards via the Trello REST API...
|
|
128
|
-
triggers: trello, kanban board, create card, move card, trello list...
|
|
54
|
+
# → {"loaded": 22, "skills": [..., "new-skill"]}
|
|
129
55
|
```
|
|
130
56
|
|
|
131
|
-
|
|
132
|
-
1. API auth setup (`TRELLO_API_KEY` + `TRELLO_TOKEN`)
|
|
133
|
-
2. Get boards/lists/cards commands
|
|
134
|
-
3. Create/move/archive card commands
|
|
135
|
-
4. Webhook setup for automation
|
|
57
|
+
Or restart: `daemora start`
|
|
136
58
|
|
|
137
|
-
##
|
|
59
|
+
## Checklist before saving
|
|
138
60
|
|
|
139
|
-
-
|
|
140
|
-
-
|
|
141
|
-
-
|
|
142
|
-
-
|
|
61
|
+
- [ ] `description` includes the key trigger phrases
|
|
62
|
+
- [ ] `triggers` covers all reasonable ways a user would ask
|
|
63
|
+
- [ ] Body is behavioral instructions, not code templates to copy-paste
|
|
64
|
+
- [ ] Commands are real - no invented flags or fictional APIs
|
|
65
|
+
- [ ] Platform requirements noted (macOS-only, requires X, etc.)
|
|
66
|
+
- [ ] Not a duplicate of an existing skill (`ls skills/`)
|
package/skills/spotify.md
CHANGED
|
@@ -13,7 +13,7 @@ triggers: spotify, play music, pause music, skip song, next track, previous trac
|
|
|
13
13
|
## Setup (one-time)
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
# Install spogo (preferred
|
|
16
|
+
# Install spogo (preferred - simpler auth)
|
|
17
17
|
brew tap steipete/tap && brew install spogo
|
|
18
18
|
spogo auth import --browser chrome # import from Chrome cookies
|
|
19
19
|
spogo status # verify it works
|
|
@@ -36,7 +36,7 @@ spogo prev
|
|
|
36
36
|
|
|
37
37
|
# Current track info
|
|
38
38
|
spogo status
|
|
39
|
-
# → 🎵 Now Playing: Daft Punk
|
|
39
|
+
# → 🎵 Now Playing: Daft Punk - Get Lucky (Random Access Memories)
|
|
40
40
|
# ⏱ 2:34 / 4:08 🔊 65% 🔀 Shuffle: off
|
|
41
41
|
|
|
42
42
|
# Volume
|
|
@@ -109,21 +109,6 @@ spotify_player search "query"
|
|
|
109
109
|
spotify_player connect
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
-
## Get Rich Now-Playing Info
|
|
113
|
-
|
|
114
|
-
```python
|
|
115
|
-
#!/usr/bin/env python3
|
|
116
|
-
"""Get detailed now-playing info using spogo status output."""
|
|
117
|
-
import subprocess, re
|
|
118
|
-
|
|
119
|
-
result = subprocess.run(["spogo", "status"], capture_output=True, text=True)
|
|
120
|
-
output = result.stdout.strip()
|
|
121
|
-
if "Not playing" in output or result.returncode != 0:
|
|
122
|
-
print("⏸ Nothing is currently playing")
|
|
123
|
-
else:
|
|
124
|
-
print(f"🎵 {output}")
|
|
125
|
-
```
|
|
126
|
-
|
|
127
112
|
## Error Handling
|
|
128
113
|
|
|
129
114
|
| Error | Fix |
|
package/skills/summarize.md
CHANGED
|
@@ -1,230 +1,73 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: summarize
|
|
3
|
-
description: Summarize long documents, articles, PDFs, web pages, emails, chat threads, meeting notes,
|
|
3
|
+
description: Summarize long documents, articles, PDFs, web pages, emails, chat threads, meeting notes, or any large text. Use when asked to summarize, give a TL;DR, extract key points, or condense content.
|
|
4
4
|
triggers: summarize, summary, tldr, tl;dr, condense, key points, brief, shorten, digest, recap, highlights, executive summary, abstract, overview
|
|
5
|
+
metadata: {"daemora": {"emoji": "✂️"}}
|
|
5
6
|
---
|
|
6
7
|
|
|
7
|
-
##
|
|
8
|
+
## Summarization levels
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
❌ Summarizing real-time data or live events — fetch the content first, then summarize
|
|
12
|
-
|
|
13
|
-
## Summarization Levels
|
|
14
|
-
|
|
15
|
-
Choose based on what the user needs:
|
|
16
|
-
|
|
17
|
-
| Level | Output | Use Case |
|
|
10
|
+
| Level | Output | Use case |
|
|
18
11
|
|-------|--------|---------|
|
|
19
|
-
| **TL;DR** | 1-3 sentences | Quick
|
|
20
|
-
| **Key Points** | 5-10 bullets | Decision making
|
|
21
|
-
| **Executive Summary** | 2-3 paragraphs | Briefing
|
|
22
|
-
| **
|
|
23
|
-
| **Action Items** | Bullet list of tasks | After meetings, emails |
|
|
24
|
-
|
|
25
|
-
## Summarize a URL
|
|
26
|
-
|
|
27
|
-
```python
|
|
28
|
-
# Fetch page content, then summarize
|
|
29
|
-
# (Uses built-in webFetch tool — the model reads the page and summarizes)
|
|
30
|
-
|
|
31
|
-
# Strategy for long pages:
|
|
32
|
-
# 1. webFetch(url) → get the content
|
|
33
|
-
# 2. If content > 4000 words: extract the main article body, skip nav/footer
|
|
34
|
-
# 3. Structure the summary based on what the user asked for
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Summarize a File
|
|
38
|
-
|
|
39
|
-
```python
|
|
40
|
-
#!/usr/bin/env python3
|
|
41
|
-
from pathlib import Path
|
|
42
|
-
|
|
43
|
-
def read_for_summary(path: str, max_chars: int = 50000) -> str:
|
|
44
|
-
"""Read file content for summarization."""
|
|
45
|
-
p = Path(path)
|
|
46
|
-
suffix = p.suffix.lower()
|
|
12
|
+
| **TL;DR** | 1-3 sentences | Quick skim |
|
|
13
|
+
| **Key Points** | 5-10 bullets | Decision making |
|
|
14
|
+
| **Executive Summary** | 2-3 paragraphs | Briefing |
|
|
15
|
+
| **Action Items** | Bullet list | After meetings |
|
|
47
16
|
|
|
48
|
-
|
|
49
|
-
content = p.read_text(encoding='utf-8', errors='ignore')
|
|
17
|
+
## Getting content
|
|
50
18
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
with pdfplumber.open(path) as pdf:
|
|
56
|
-
content = "\n\n".join(
|
|
57
|
-
f"[Page {i+1}]\n{page.extract_text() or ''}"
|
|
58
|
-
for i, page in enumerate(pdf.pages)
|
|
59
|
-
)
|
|
60
|
-
except ImportError:
|
|
61
|
-
# Fall back to pdftotext (brew install poppler)
|
|
62
|
-
import subprocess
|
|
63
|
-
result = subprocess.run(["pdftotext", path, "-"], capture_output=True, text=True)
|
|
64
|
-
content = result.stdout
|
|
19
|
+
- URL → `webFetch(url)`
|
|
20
|
+
- PDF → `pdftotext file.pdf -` or `pdfplumber`
|
|
21
|
+
- `.docx` → `pandoc file.docx -t plain`
|
|
22
|
+
- Long content → summarize in chunks, then summarize the summaries
|
|
65
23
|
|
|
66
|
-
|
|
67
|
-
import subprocess
|
|
68
|
-
result = subprocess.run(["pandoc", path, "-t", "plain"], capture_output=True, text=True)
|
|
69
|
-
content = result.stdout
|
|
24
|
+
## Code / PR summary
|
|
70
25
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
content = json.dumps(data, indent=2)
|
|
75
|
-
|
|
76
|
-
elif suffix in ('.csv',):
|
|
77
|
-
lines = p.read_text().split('\n')
|
|
78
|
-
headers = lines[0] if lines else ""
|
|
79
|
-
content = f"CSV with {len(lines)-1} rows.\nHeaders: {headers}\n\nFirst 10 rows:\n" + '\n'.join(lines[1:11])
|
|
80
|
-
|
|
81
|
-
else:
|
|
82
|
-
content = p.read_text(encoding='utf-8', errors='ignore')
|
|
83
|
-
|
|
84
|
-
# Truncate if too long (keep start + end for context)
|
|
85
|
-
if len(content) > max_chars:
|
|
86
|
-
half = max_chars // 2
|
|
87
|
-
content = (content[:half] +
|
|
88
|
-
f"\n\n[... {len(content) - max_chars} characters truncated ...]\n\n" +
|
|
89
|
-
content[-half:])
|
|
90
|
-
return content
|
|
91
|
-
|
|
92
|
-
# Read the file and pass to the model for summarization
|
|
93
|
-
content = read_for_summary("/path/to/document.pdf")
|
|
94
|
-
print(f"Ready to summarize: {len(content)} chars")
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
## Summarize Long Email Thread
|
|
98
|
-
|
|
99
|
-
```python
|
|
100
|
-
#!/usr/bin/env python3
|
|
101
|
-
def parse_email_thread(raw_email: str) -> list[dict]:
|
|
102
|
-
"""Extract individual messages from an email thread."""
|
|
103
|
-
messages = []
|
|
104
|
-
# Split on common email separators
|
|
105
|
-
import re
|
|
106
|
-
parts = re.split(r'\n-{10,}\n|On .+ wrote:\n', raw_email)
|
|
107
|
-
for i, part in enumerate(parts):
|
|
108
|
-
lines = part.strip().split('\n')
|
|
109
|
-
# Try to extract From/Date
|
|
110
|
-
from_line = next((l for l in lines[:5] if l.startswith('From:')), f"Message {i+1}")
|
|
111
|
-
body = '\n'.join(lines[3:]) # skip headers
|
|
112
|
-
messages.append({"from": from_line, "body": body[:500]})
|
|
113
|
-
return messages
|
|
114
|
-
|
|
115
|
-
# Format for summarization
|
|
116
|
-
def format_thread_for_summary(messages: list[dict]) -> str:
|
|
117
|
-
return "\n\n---\n\n".join(
|
|
118
|
-
f"**{m['from']}**\n{m['body']}"
|
|
119
|
-
for m in messages
|
|
120
|
-
)
|
|
26
|
+
```bash
|
|
27
|
+
git diff main..feature --stat
|
|
28
|
+
git log main..feature --oneline
|
|
121
29
|
```
|
|
122
30
|
|
|
123
|
-
##
|
|
124
|
-
|
|
125
|
-
Use these structures when presenting summaries:
|
|
31
|
+
## Output templates
|
|
126
32
|
|
|
127
|
-
|
|
33
|
+
**TL;DR**
|
|
128
34
|
```
|
|
129
|
-
**TL;DR:** [1-2 sentences capturing the
|
|
35
|
+
**TL;DR:** [1-2 sentences capturing the core message]
|
|
130
36
|
```
|
|
131
37
|
|
|
132
|
-
|
|
38
|
+
**Key Points**
|
|
133
39
|
```
|
|
134
40
|
**Key Points:**
|
|
135
41
|
• [Most important insight]
|
|
136
42
|
• [Second key point]
|
|
137
43
|
• [Third key point]
|
|
138
|
-
• [Fourth key point]
|
|
139
|
-
• [Fifth key point]
|
|
140
44
|
|
|
141
|
-
**Bottom line:** [One sentence conclusion
|
|
45
|
+
**Bottom line:** [One sentence conclusion]
|
|
142
46
|
```
|
|
143
47
|
|
|
144
|
-
|
|
48
|
+
**Executive Summary**
|
|
145
49
|
```
|
|
146
50
|
**Executive Summary**
|
|
147
51
|
|
|
148
|
-
**What:** [
|
|
149
|
-
|
|
150
|
-
**
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
**Decisions / Actions:**
|
|
154
|
-
• [Decision or action item 1]
|
|
155
|
-
• [Decision or action item 2]
|
|
156
|
-
|
|
157
|
-
**Next Steps:**
|
|
158
|
-
• [What happens next]
|
|
52
|
+
**What:** [one sentence]
|
|
53
|
+
**Key findings:** [2-3 sentences]
|
|
54
|
+
**Actions / decisions:** [bullets]
|
|
55
|
+
**Next steps:** [bullets]
|
|
159
56
|
```
|
|
160
57
|
|
|
161
|
-
|
|
58
|
+
**Meeting recap**
|
|
162
59
|
```
|
|
163
|
-
**Meeting Recap
|
|
60
|
+
**Meeting Recap - [Date] - [Topic]**
|
|
164
61
|
**Attendees:** [names]
|
|
165
|
-
|
|
166
|
-
**
|
|
167
|
-
|
|
168
|
-
• [Topic 2]
|
|
169
|
-
|
|
170
|
-
**Decisions Made:**
|
|
171
|
-
• [Decision 1]
|
|
172
|
-
|
|
173
|
-
**Action Items:**
|
|
174
|
-
• [Person] → [Task] by [Date]
|
|
175
|
-
• [Person] → [Task]
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
## Summarizing Code / PRs / Diffs
|
|
179
|
-
|
|
180
|
-
When summarizing code or a git diff:
|
|
181
|
-
|
|
182
|
-
```bash
|
|
183
|
-
# Get a diff to summarize
|
|
184
|
-
git diff main..feature-branch --stat
|
|
185
|
-
git log main..feature-branch --oneline
|
|
186
|
-
|
|
187
|
-
# For a large diff, summarize by file:
|
|
188
|
-
git diff main..feature-branch -- src/ | head -200
|
|
62
|
+
**Discussed:** [bullets]
|
|
63
|
+
**Decisions:** [bullets]
|
|
64
|
+
**Action items:** [Person → Task by Date]
|
|
189
65
|
```
|
|
190
66
|
|
|
191
|
-
|
|
67
|
+
**Code changes**
|
|
192
68
|
```
|
|
193
69
|
**Changes Summary**
|
|
194
|
-
- Files changed
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
**What changed:**
|
|
198
|
-
• [Feature or component]: [what was done]
|
|
199
|
-
• [Another area]: [what was done]
|
|
200
|
-
|
|
201
|
-
**Why (from commit messages):** [inferred purpose]
|
|
202
|
-
**Risk level:** Low / Medium / High — [brief reason]
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
## Chunking Strategy for Very Long Content
|
|
206
|
-
|
|
207
|
-
When content exceeds context limits, use this strategy:
|
|
208
|
-
|
|
209
|
-
```python
|
|
210
|
-
def chunk_for_summary(text: str, chunk_size: int = 8000) -> list[str]:
|
|
211
|
-
"""Split text into overlapping chunks for multi-pass summarization."""
|
|
212
|
-
chunks = []
|
|
213
|
-
overlap = 200 # chars overlap between chunks
|
|
214
|
-
start = 0
|
|
215
|
-
while start < len(text):
|
|
216
|
-
end = min(start + chunk_size, len(text))
|
|
217
|
-
# Try to end at a paragraph boundary
|
|
218
|
-
if end < len(text):
|
|
219
|
-
last_para = text.rfind('\n\n', start, end)
|
|
220
|
-
if last_para > start + chunk_size // 2:
|
|
221
|
-
end = last_para
|
|
222
|
-
chunks.append(text[start:end])
|
|
223
|
-
start = end - overlap
|
|
224
|
-
return chunks
|
|
225
|
-
|
|
226
|
-
# Strategy:
|
|
227
|
-
# 1. Summarize each chunk individually
|
|
228
|
-
# 2. Combine chunk summaries
|
|
229
|
-
# 3. Summarize the combined summaries → final summary
|
|
70
|
+
- Files: N changed, +X/-Y lines
|
|
71
|
+
**What changed:** [per-component bullets]
|
|
72
|
+
**Risk:** Low/Medium/High - [reason]
|
|
230
73
|
```
|