daemora 1.0.0

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 (115) hide show
  1. package/README.md +666 -0
  2. package/SOUL.md +104 -0
  3. package/config/hooks.json +14 -0
  4. package/config/mcp.json +145 -0
  5. package/package.json +86 -0
  6. package/skills/.gitkeep +0 -0
  7. package/skills/apple-notes.md +193 -0
  8. package/skills/apple-reminders.md +189 -0
  9. package/skills/camsnap.md +162 -0
  10. package/skills/coding.md +14 -0
  11. package/skills/documents.md +13 -0
  12. package/skills/email.md +13 -0
  13. package/skills/gif-search.md +196 -0
  14. package/skills/healthcheck.md +225 -0
  15. package/skills/image-gen.md +147 -0
  16. package/skills/model-usage.md +182 -0
  17. package/skills/obsidian.md +207 -0
  18. package/skills/pdf.md +211 -0
  19. package/skills/research.md +13 -0
  20. package/skills/skill-creator.md +142 -0
  21. package/skills/spotify.md +149 -0
  22. package/skills/summarize.md +230 -0
  23. package/skills/things.md +199 -0
  24. package/skills/tmux.md +204 -0
  25. package/skills/trello.md +183 -0
  26. package/skills/video-frames.md +202 -0
  27. package/skills/weather.md +127 -0
  28. package/src/a2a/A2AClient.js +136 -0
  29. package/src/a2a/A2AServer.js +316 -0
  30. package/src/a2a/AgentCard.js +79 -0
  31. package/src/agents/SubAgentManager.js +369 -0
  32. package/src/agents/Supervisor.js +192 -0
  33. package/src/channels/BaseChannel.js +104 -0
  34. package/src/channels/DiscordChannel.js +288 -0
  35. package/src/channels/EmailChannel.js +172 -0
  36. package/src/channels/GoogleChatChannel.js +316 -0
  37. package/src/channels/HttpChannel.js +26 -0
  38. package/src/channels/LineChannel.js +168 -0
  39. package/src/channels/SignalChannel.js +186 -0
  40. package/src/channels/SlackChannel.js +329 -0
  41. package/src/channels/TeamsChannel.js +272 -0
  42. package/src/channels/TelegramChannel.js +347 -0
  43. package/src/channels/WhatsAppChannel.js +219 -0
  44. package/src/channels/index.js +198 -0
  45. package/src/cli.js +1267 -0
  46. package/src/config/agentProfiles.js +120 -0
  47. package/src/config/channels.js +32 -0
  48. package/src/config/default.js +206 -0
  49. package/src/config/models.js +123 -0
  50. package/src/config/permissions.js +167 -0
  51. package/src/core/AgentLoop.js +446 -0
  52. package/src/core/Compaction.js +143 -0
  53. package/src/core/CostTracker.js +116 -0
  54. package/src/core/EventBus.js +46 -0
  55. package/src/core/Task.js +67 -0
  56. package/src/core/TaskQueue.js +206 -0
  57. package/src/core/TaskRunner.js +226 -0
  58. package/src/daemon/DaemonManager.js +301 -0
  59. package/src/hooks/HookRunner.js +230 -0
  60. package/src/index.js +482 -0
  61. package/src/mcp/MCPAgentRunner.js +112 -0
  62. package/src/mcp/MCPClient.js +186 -0
  63. package/src/mcp/MCPManager.js +412 -0
  64. package/src/models/ModelRouter.js +180 -0
  65. package/src/safety/AuditLog.js +135 -0
  66. package/src/safety/CircuitBreaker.js +126 -0
  67. package/src/safety/FilesystemGuard.js +169 -0
  68. package/src/safety/GitRollback.js +139 -0
  69. package/src/safety/HumanApproval.js +156 -0
  70. package/src/safety/InputSanitizer.js +72 -0
  71. package/src/safety/PermissionGuard.js +83 -0
  72. package/src/safety/Sandbox.js +70 -0
  73. package/src/safety/SecretScanner.js +100 -0
  74. package/src/safety/SecretVault.js +250 -0
  75. package/src/scheduler/Heartbeat.js +115 -0
  76. package/src/scheduler/Scheduler.js +228 -0
  77. package/src/services/models/outputSchema.js +15 -0
  78. package/src/services/openai.js +25 -0
  79. package/src/services/sessions.js +65 -0
  80. package/src/setup/theme.js +110 -0
  81. package/src/setup/wizard.js +788 -0
  82. package/src/skills/SkillLoader.js +168 -0
  83. package/src/storage/TaskStore.js +69 -0
  84. package/src/systemPrompt.js +526 -0
  85. package/src/tenants/TenantContext.js +19 -0
  86. package/src/tenants/TenantManager.js +379 -0
  87. package/src/tools/ToolRegistry.js +141 -0
  88. package/src/tools/applyPatch.js +144 -0
  89. package/src/tools/browserAutomation.js +223 -0
  90. package/src/tools/createDocument.js +265 -0
  91. package/src/tools/cronTool.js +105 -0
  92. package/src/tools/editFile.js +139 -0
  93. package/src/tools/executeCommand.js +123 -0
  94. package/src/tools/glob.js +67 -0
  95. package/src/tools/grep.js +121 -0
  96. package/src/tools/imageAnalysis.js +120 -0
  97. package/src/tools/index.js +173 -0
  98. package/src/tools/listDirectory.js +47 -0
  99. package/src/tools/manageAgents.js +47 -0
  100. package/src/tools/manageMCP.js +159 -0
  101. package/src/tools/memory.js +478 -0
  102. package/src/tools/messageChannel.js +45 -0
  103. package/src/tools/projectTracker.js +259 -0
  104. package/src/tools/readFile.js +52 -0
  105. package/src/tools/screenCapture.js +112 -0
  106. package/src/tools/searchContent.js +76 -0
  107. package/src/tools/searchFiles.js +75 -0
  108. package/src/tools/sendEmail.js +118 -0
  109. package/src/tools/sendFile.js +63 -0
  110. package/src/tools/textToSpeech.js +161 -0
  111. package/src/tools/transcribeAudio.js +82 -0
  112. package/src/tools/useMCP.js +29 -0
  113. package/src/tools/webFetch.js +150 -0
  114. package/src/tools/webSearch.js +134 -0
  115. package/src/tools/writeFile.js +26 -0
@@ -0,0 +1,207 @@
1
+ ---
2
+ name: obsidian
3
+ description: Read, create, edit, search, and manage notes in an Obsidian vault. Use when the user asks to create a note, find a note, update an Obsidian file, link notes, search their vault, or manage their knowledge base. Obsidian vaults are plain Markdown files — no special tools needed for basic operations.
4
+ triggers: obsidian, note, vault, knowledge base, zettelkasten, markdown note, create note, find note, link note, obsidian search, obsidian plugin
5
+ ---
6
+
7
+ ## When to Use
8
+
9
+ ✅ Create notes, read notes, search vault, edit frontmatter, create links between notes, list all notes, tag management, daily notes
10
+
11
+ ❌ Obsidian UI automation (open a specific note visually) — use `obsidian://` URI scheme for that
12
+
13
+ ## Find the Vault Location
14
+
15
+ ```bash
16
+ # Obsidian tracks vaults here on macOS:
17
+ python3 -c "
18
+ import json, pathlib
19
+ config = pathlib.Path.home() / 'Library/Application Support/obsidian/obsidian.json'
20
+ if config.exists():
21
+ data = json.loads(config.read_text())
22
+ for vault in data.get('vaults', {}).values():
23
+ status = '✅ (open)' if vault.get('open') else ''
24
+ print(f\"{vault['path']} {status}\")
25
+ else:
26
+ print('Obsidian config not found — vault location unknown')
27
+ "
28
+ # Typical locations: ~/Documents/Obsidian/, ~/Notes/, ~/Documents/Notes/
29
+ ```
30
+
31
+ ## Read Notes
32
+
33
+ ```bash
34
+ # Find all notes in vault
35
+ find ~/Documents/Obsidian -name "*.md" | head -20
36
+
37
+ # Search by content
38
+ grep -r "search term" ~/Documents/Obsidian --include="*.md" -l
39
+
40
+ # Search with context (2 lines around match)
41
+ grep -r "search term" ~/Documents/Obsidian --include="*.md" -C 2
42
+
43
+ # Find notes with a specific tag
44
+ grep -r "^tags:.*tagname\|#tagname" ~/Documents/Obsidian --include="*.md" -l
45
+
46
+ # Find notes created today (macOS)
47
+ find ~/Documents/Obsidian -name "*.md" -newer $(date -v-1d +%Y-%m-%d 2>/dev/null || date -d "1 day ago" +%Y-%m-%d) 2>/dev/null
48
+ ```
49
+
50
+ ## Create a Note
51
+
52
+ ```python
53
+ #!/usr/bin/env python3
54
+ from pathlib import Path
55
+ from datetime import datetime
56
+
57
+ VAULT = Path.home() / "Documents/Obsidian" # adjust to actual vault path
58
+
59
+ def create_note(title: str, content: str, folder: str = "", tags: list = None):
60
+ """Create a note with YAML frontmatter."""
61
+ tags = tags or []
62
+ date = datetime.now().strftime("%Y-%m-%d")
63
+ time = datetime.now().strftime("%H:%M")
64
+
65
+ frontmatter = f"""---
66
+ title: {title}
67
+ date: {date}
68
+ time: {time}
69
+ tags: [{', '.join(tags)}]
70
+ ---
71
+
72
+ """
73
+ target_dir = VAULT / folder if folder else VAULT
74
+ target_dir.mkdir(parents=True, exist_ok=True)
75
+
76
+ # Sanitize filename
77
+ filename = title.replace("/", "-").replace(":", "-") + ".md"
78
+ filepath = target_dir / filename
79
+
80
+ # Don't overwrite — append timestamp if exists
81
+ if filepath.exists():
82
+ ts = datetime.now().strftime("%Y%m%d%H%M%S")
83
+ filepath = target_dir / f"{title} ({ts}).md"
84
+
85
+ filepath.write_text(frontmatter + content, encoding="utf-8")
86
+ print(f"Created: {filepath}")
87
+ return filepath
88
+
89
+ # Example usage:
90
+ create_note(
91
+ title="Meeting Notes — Q1 Review",
92
+ content="## Attendees\n- Alice\n- Bob\n\n## Key Points\n- ...",
93
+ folder="Meetings",
94
+ tags=["meeting", "q1-2026"]
95
+ )
96
+ ```
97
+
98
+ ## Create Daily Note
99
+
100
+ ```python
101
+ #!/usr/bin/env python3
102
+ from pathlib import Path
103
+ from datetime import datetime
104
+
105
+ VAULT = Path.home() / "Documents/Obsidian"
106
+ DAILY_FOLDER = VAULT / "Daily Notes"
107
+ DAILY_FOLDER.mkdir(parents=True, exist_ok=True)
108
+
109
+ today = datetime.now()
110
+ filename = today.strftime("%Y-%m-%d") + ".md"
111
+ filepath = DAILY_FOLDER / filename
112
+
113
+ if not filepath.exists():
114
+ content = f"""---
115
+ date: {today.strftime('%Y-%m-%d')}
116
+ tags: [daily]
117
+ ---
118
+
119
+ # {today.strftime('%A, %B %d, %Y')}
120
+
121
+ ## 🎯 Focus Today
122
+
123
+ ## 📝 Notes
124
+
125
+ ## ✅ Tasks
126
+ - [ ]
127
+
128
+ ## 💭 Reflections
129
+
130
+ """
131
+ filepath.write_text(content)
132
+ print(f"Created daily note: {filepath}")
133
+ else:
134
+ print(f"Daily note already exists: {filepath}")
135
+
136
+ # Open it in Obsidian
137
+ import subprocess
138
+ subprocess.run(["open", f"obsidian://open?vault=Obsidian&file=Daily Notes/{today.strftime('%Y-%m-%d')}"])
139
+ ```
140
+
141
+ ## Edit Existing Note
142
+
143
+ ```python
144
+ #!/usr/bin/env python3
145
+ from pathlib import Path
146
+
147
+ def append_to_note(note_path: str, content: str, section: str = None):
148
+ """Append content to a note, optionally under a specific section."""
149
+ path = Path(note_path)
150
+ existing = path.read_text(encoding="utf-8")
151
+
152
+ if section:
153
+ # Insert under the section heading
154
+ marker = f"## {section}"
155
+ if marker in existing:
156
+ idx = existing.index(marker) + len(marker)
157
+ # Find next line after heading
158
+ next_line = existing.find("\n", idx) + 1
159
+ updated = existing[:next_line] + "\n" + content + "\n" + existing[next_line:]
160
+ else:
161
+ updated = existing + f"\n## {section}\n\n{content}\n"
162
+ else:
163
+ updated = existing.rstrip() + f"\n\n{content}\n"
164
+
165
+ path.write_text(updated, encoding="utf-8")
166
+ print(f"Updated: {path}")
167
+
168
+ # Example: append a task under ## ✅ Tasks
169
+ append_to_note(
170
+ "/path/to/note.md",
171
+ "- [ ] New task item",
172
+ section="✅ Tasks"
173
+ )
174
+ ```
175
+
176
+ ## Create Linked Notes (Wiki Links)
177
+
178
+ Obsidian uses `[[Note Title]]` syntax for links. When creating a note that references others:
179
+
180
+ ```python
181
+ # Create a note with backlinks
182
+ content = """
183
+ ## Related Research
184
+
185
+ See [[Market Analysis 2026]] for context.
186
+ Building on [[Previous Meeting Notes]].
187
+
188
+ ## Summary
189
+ ...
190
+ """
191
+ ```
192
+
193
+ ## Open Note in Obsidian (macOS)
194
+
195
+ ```bash
196
+ # Open specific note
197
+ open "obsidian://open?vault=VAULT_NAME&file=PATH/TO/NOTE"
198
+
199
+ # Open search in Obsidian
200
+ open "obsidian://search?vault=VAULT_NAME&query=search+term"
201
+ ```
202
+
203
+ ## Error Handling
204
+
205
+ - **Vault not found**: read `~/Library/Application Support/obsidian/obsidian.json` to locate vaults; ask user to confirm vault path if multiple exist
206
+ - **File already exists**: always check before writing; either append content or create new file with timestamp suffix
207
+ - **Encoding issues**: always use `encoding="utf-8"` when reading/writing `.md` files
package/skills/pdf.md ADDED
@@ -0,0 +1,211 @@
1
+ ---
2
+ name: pdf
3
+ description: Create, read, extract text from, merge, split, rotate, compress, and manipulate PDF files. Use when the user asks to create a PDF report/document, extract text from a PDF, merge PDFs, convert to PDF, or do any PDF operation. Works on macOS and Linux without paid tools.
4
+ triggers: pdf, create pdf, read pdf, extract pdf, merge pdf, split pdf, convert to pdf, pdf report, pdf document, compress pdf, rotate pdf
5
+ ---
6
+
7
+ ## When to Use
8
+
9
+ ✅ Create PDF reports/documents, extract text from PDFs, merge multiple PDFs, split pages, rotate, compress, convert HTML/Markdown to PDF
10
+
11
+ ❌ PDF form filling with complex logic → use PyPDF2 + pdfrw; PDF digital signatures → use specialized tools
12
+
13
+ ## Method 1: Create PDF from HTML (best quality, macOS)
14
+
15
+ ```bash
16
+ # Install wkhtmltopdf (one-time)
17
+ brew install wkhtmltopdf
18
+
19
+ # Create HTML file, then convert
20
+ cat > /tmp/report.html << 'EOF'
21
+ <!DOCTYPE html>
22
+ <html>
23
+ <head>
24
+ <meta charset="UTF-8">
25
+ <style>
26
+ body { font-family: -apple-system, Arial, sans-serif; margin: 40px; color: #222; }
27
+ h1 { color: #1a1a2e; border-bottom: 2px solid #eee; padding-bottom: 10px; }
28
+ h2 { color: #444; margin-top: 30px; }
29
+ table { border-collapse: collapse; width: 100%; margin: 16px 0; }
30
+ th { background: #f0f4ff; padding: 10px; text-align: left; border: 1px solid #ddd; }
31
+ td { padding: 8px 10px; border: 1px solid #eee; }
32
+ .highlight { background: #fffbea; padding: 12px; border-left: 4px solid #f0c040; }
33
+ </style>
34
+ </head>
35
+ <body>
36
+ <h1>Report Title</h1>
37
+ <p>Content here</p>
38
+ </body>
39
+ </html>
40
+ EOF
41
+
42
+ wkhtmltopdf \
43
+ --page-size A4 \
44
+ --margin-top 20mm --margin-bottom 20mm \
45
+ --margin-left 15mm --margin-right 15mm \
46
+ --encoding UTF-8 \
47
+ --footer-center "[page] / [topage]" \
48
+ --footer-font-size 9 \
49
+ /tmp/report.html /tmp/report.pdf
50
+
51
+ echo "Created: /tmp/report.pdf"
52
+ open /tmp/report.pdf # preview on macOS
53
+ ```
54
+
55
+ ## Method 2: Create PDF with Python (cross-platform, no external binaries)
56
+
57
+ ```python
58
+ #!/usr/bin/env python3
59
+ """Create a PDF without any system binaries — uses reportlab."""
60
+ # Install once: pip install reportlab
61
+ from reportlab.lib.pagesizes import A4
62
+ from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
63
+ from reportlab.lib.units import mm
64
+ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
65
+ from reportlab.lib import colors
66
+
67
+ output_path = "/tmp/report.pdf"
68
+ doc = SimpleDocTemplate(output_path, pagesize=A4,
69
+ leftMargin=20*mm, rightMargin=20*mm,
70
+ topMargin=20*mm, bottomMargin=20*mm)
71
+
72
+ styles = getSampleStyleSheet()
73
+ story = []
74
+
75
+ # Title
76
+ title_style = ParagraphStyle('Title', parent=styles['Title'],
77
+ fontSize=24, spaceAfter=12)
78
+ story.append(Paragraph("Report Title", title_style))
79
+ story.append(Spacer(1, 8*mm))
80
+
81
+ # Body text
82
+ story.append(Paragraph("Your content here. Supports <b>bold</b>, <i>italic</i>, and links.", styles['BodyText']))
83
+ story.append(Spacer(1, 6*mm))
84
+
85
+ # Table
86
+ data = [['Column A', 'Column B', 'Column C'],
87
+ ['Row 1', 'Value', '$100'],
88
+ ['Row 2', 'Value', '$200']]
89
+ table = Table(data, colWidths=[60*mm, 60*mm, 40*mm])
90
+ table.setStyle(TableStyle([
91
+ ('BACKGROUND', (0,0), (-1,0), colors.HexColor('#e8eaf6')),
92
+ ('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
93
+ ('GRID', (0,0), (-1,-1), 0.5, colors.HexColor('#cccccc')),
94
+ ('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, colors.HexColor('#f8f9ff')]),
95
+ ('PADDING', (0,0), (-1,-1), 8),
96
+ ]))
97
+ story.append(table)
98
+
99
+ doc.build(story)
100
+ print(f"Created: {output_path}")
101
+ ```
102
+
103
+ ## Read / Extract Text from PDF
104
+
105
+ ```python
106
+ #!/usr/bin/env python3
107
+ # pip install pdfplumber (better than pypdf2 for text extraction)
108
+ import pdfplumber, sys
109
+
110
+ pdf_path = sys.argv[1] if len(sys.argv) > 1 else "/path/to/file.pdf"
111
+
112
+ with pdfplumber.open(pdf_path) as pdf:
113
+ print(f"Pages: {len(pdf.pages)}")
114
+ for i, page in enumerate(pdf.pages, 1):
115
+ text = page.extract_text()
116
+ if text:
117
+ print(f"\n--- Page {i} ---\n{text}")
118
+
119
+ # Also extract tables
120
+ tables = page.extract_tables()
121
+ for t in tables:
122
+ print(f"\n[Table on page {i}]")
123
+ for row in t:
124
+ print(" | ".join(str(c or '') for c in row))
125
+ ```
126
+
127
+ ## Merge PDFs
128
+
129
+ ```python
130
+ #!/usr/bin/env python3
131
+ # pip install pypdf
132
+ from pypdf import PdfWriter, PdfReader
133
+ import sys
134
+
135
+ output = "/tmp/merged.pdf"
136
+ writer = PdfWriter()
137
+
138
+ for path in sys.argv[1:]:
139
+ reader = PdfReader(path)
140
+ for page in reader.pages:
141
+ writer.add_page(page)
142
+
143
+ with open(output, "wb") as f:
144
+ writer.write(f)
145
+ print(f"Merged {len(sys.argv)-1} PDFs → {output}")
146
+ ```
147
+
148
+ ## Split PDF (extract page range)
149
+
150
+ ```python
151
+ #!/usr/bin/env python3
152
+ # pip install pypdf
153
+ from pypdf import PdfWriter, PdfReader
154
+
155
+ input_pdf = "/path/to/input.pdf"
156
+ output_pdf = "/tmp/extracted_pages.pdf"
157
+ start_page = 0 # 0-indexed
158
+ end_page = 5 # exclusive
159
+
160
+ reader = PdfReader(input_pdf)
161
+ writer = PdfWriter()
162
+ for i in range(start_page, min(end_page, len(reader.pages))):
163
+ writer.add_page(reader.pages[i])
164
+ with open(output_pdf, "wb") as f:
165
+ writer.write(f)
166
+ print(f"Extracted pages {start_page+1}-{end_page} → {output_pdf}")
167
+ ```
168
+
169
+ ## Compress PDF (macOS, no install)
170
+
171
+ ```bash
172
+ # Uses macOS built-in Quartz filter
173
+ "/System/Library/Printers/Libraries/quartzfilter" \
174
+ /System/Library/Filters/Reduce\ File\ Size.qfilter \
175
+ /path/to/input.pdf /tmp/compressed.pdf
176
+
177
+ # Or via Python with ghostscript
178
+ gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.5 \
179
+ -dPDFSETTINGS=/ebook \
180
+ -dNOPAUSE -dQUIET -dBATCH \
181
+ -sOutputFile=/tmp/compressed.pdf /path/to/input.pdf
182
+ # PDFSET options: /screen (72dpi) /ebook (150dpi) /printer (300dpi) /prepress (300dpi HQ)
183
+ ```
184
+
185
+ ## Convert Markdown → PDF
186
+
187
+ ```bash
188
+ # pip install md-to-pdf OR brew install pandoc
189
+ # With pandoc (best quality):
190
+ pandoc input.md -o output.pdf \
191
+ --pdf-engine=wkhtmltopdf \
192
+ -V geometry:margin=25mm \
193
+ -V fontsize=11pt
194
+
195
+ # Quick and dirty with Python:
196
+ pip install markdown weasyprint
197
+ python3 -c "
198
+ import markdown, weasyprint, pathlib
199
+ md = pathlib.Path('input.md').read_text()
200
+ html = markdown.markdown(md, extensions=['tables','fenced_code'])
201
+ weasyprint.HTML(string=f'<html><body style=\"font-family:sans-serif;margin:40px\">{html}</body></html>').write_pdf('output.pdf')
202
+ print('output.pdf')
203
+ "
204
+ ```
205
+
206
+ ## After Creating PDF
207
+
208
+ 1. Report the output path: "PDF saved to `/tmp/report.pdf`"
209
+ 2. On macOS, offer to open it: `executeCommand("open /tmp/report.pdf")`
210
+ 3. If the user wants to send it, use `sendFile("/tmp/report.pdf", channel, sessionId)`
211
+ 4. For large reports: mention page count and file size
@@ -0,0 +1,13 @@
1
+ ---
2
+ name: research
3
+ description: Use when researching topics, gathering information, or answering questions
4
+ triggers: research, search, find, look up, what is, how does, explain, learn, investigate, compare
5
+ ---
6
+ You are an expert researcher. When doing research tasks:
7
+
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.
@@ -0,0 +1,142 @@
1
+ ---
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. A skill is a Markdown file in the skills/ directory that gets auto-loaded when matching triggers are detected.
4
+ triggers: create skill, new skill, add skill, make skill, package skill, teach agent, skill creator, write skill
5
+ ---
6
+
7
+ ## What Is a Skill
8
+
9
+ A skill is a `.md` file in the `skills/` directory. When a user's task matches the skill's `triggers`, the skill's instructions are injected into the agent's system prompt for that task. Skills give the agent specialized knowledge, commands, and workflows for specific domains.
10
+
11
+ ## Skill File Format
12
+
13
+ ```markdown
14
+ ---
15
+ name: skill-name # unique slug, lowercase-hyphen
16
+ description: One sentence describing WHAT this skill does and WHEN to use it.
17
+ Include the specific trigger phrases. Be comprehensive — the description
18
+ is how the agent decides whether to load this skill.
19
+ triggers: keyword1, keyword2, phrase, another phrase
20
+ ---
21
+
22
+ ## When to Use
23
+
24
+ ✅ Specific cases where this skill applies
25
+ ❌ Cases where it does NOT apply (prevents false triggers)
26
+
27
+ ## Core Instructions
28
+
29
+ [Domain-specific knowledge, commands, workflows]
30
+
31
+ ## Examples / Commands
32
+
33
+ [Concrete, working examples with real commands the agent can copy-paste]
34
+
35
+ ## Error Handling
36
+
37
+ [What to do when things go wrong]
38
+ ```
39
+
40
+ ## Creation Process
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
+ ```
99
+
100
+ ### Step 4 — Reload skills
101
+
102
+ After creating a skill:
103
+
104
+ ```bash
105
+ curl -s -X POST http://localhost:8081/skills/reload
106
+ # → {"loaded": 12, "skills": ["coding", "research", ..., "new-skill"]}
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...
129
+ ```
130
+
131
+ Content should include:
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
136
+
137
+ ## Do NOT Create Skills For
138
+
139
+ - One-time tasks (just do the task directly)
140
+ - Things the agent already handles with its 36 built-in tools
141
+ - Duplicates of existing skills (check `skills/` first)
142
+ - Platform-specific things on wrong platform (e.g., Apple Notes skill on Linux)