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.
Files changed (134) hide show
  1. package/README.md +106 -76
  2. package/SOUL.md +100 -28
  3. package/config/mcp.json +9 -9
  4. package/package.json +15 -8
  5. package/skills/apple-notes.md +0 -52
  6. package/skills/apple-reminders.md +1 -87
  7. package/skills/camsnap.md +20 -144
  8. package/skills/coding.md +7 -7
  9. package/skills/documents.md +6 -6
  10. package/skills/email.md +6 -6
  11. package/skills/gif-search.md +28 -171
  12. package/skills/healthcheck.md +21 -203
  13. package/skills/image-gen.md +24 -123
  14. package/skills/model-usage.md +18 -165
  15. package/skills/obsidian.md +28 -174
  16. package/skills/pdf.md +30 -181
  17. package/skills/research.md +6 -6
  18. package/skills/skill-creator.md +35 -111
  19. package/skills/spotify.md +2 -17
  20. package/skills/summarize.md +36 -193
  21. package/skills/things.md +23 -175
  22. package/skills/tmux.md +1 -91
  23. package/skills/trello.md +32 -157
  24. package/skills/video-frames.md +26 -166
  25. package/skills/weather.md +6 -6
  26. package/src/a2a/A2AClient.js +2 -2
  27. package/src/a2a/A2AServer.js +6 -6
  28. package/src/a2a/AgentCard.js +2 -2
  29. package/src/agents/SubAgentManager.js +61 -19
  30. package/src/agents/Supervisor.js +4 -4
  31. package/src/channels/BaseChannel.js +6 -6
  32. package/src/channels/BlueBubblesChannel.js +112 -0
  33. package/src/channels/DiscordChannel.js +8 -8
  34. package/src/channels/EmailChannel.js +54 -26
  35. package/src/channels/FeishuChannel.js +140 -0
  36. package/src/channels/GoogleChatChannel.js +8 -8
  37. package/src/channels/HttpChannel.js +2 -2
  38. package/src/channels/IRCChannel.js +144 -0
  39. package/src/channels/LineChannel.js +13 -13
  40. package/src/channels/MatrixChannel.js +97 -0
  41. package/src/channels/MattermostChannel.js +119 -0
  42. package/src/channels/NextcloudChannel.js +133 -0
  43. package/src/channels/NostrChannel.js +175 -0
  44. package/src/channels/SignalChannel.js +9 -9
  45. package/src/channels/SlackChannel.js +10 -10
  46. package/src/channels/TeamsChannel.js +10 -10
  47. package/src/channels/TelegramChannel.js +8 -8
  48. package/src/channels/TwitchChannel.js +128 -0
  49. package/src/channels/WhatsAppChannel.js +10 -10
  50. package/src/channels/ZaloChannel.js +119 -0
  51. package/src/channels/iMessageChannel.js +150 -0
  52. package/src/channels/index.js +241 -11
  53. package/src/cli.js +835 -38
  54. package/src/config/agentProfiles.js +19 -19
  55. package/src/config/channels.js +1 -1
  56. package/src/config/default.js +12 -7
  57. package/src/config/models.js +3 -3
  58. package/src/config/permissions.js +2 -2
  59. package/src/core/AgentLoop.js +13 -13
  60. package/src/core/Compaction.js +3 -3
  61. package/src/core/CostTracker.js +2 -2
  62. package/src/core/EventBus.js +15 -15
  63. package/src/core/TaskQueue.js +24 -7
  64. package/src/core/TaskRunner.js +19 -6
  65. package/src/daemon/DaemonManager.js +4 -4
  66. package/src/hooks/HookRunner.js +4 -4
  67. package/src/index.js +6 -2
  68. package/src/mcp/MCPAgentRunner.js +3 -3
  69. package/src/mcp/MCPClient.js +9 -9
  70. package/src/mcp/MCPManager.js +14 -14
  71. package/src/models/ModelRouter.js +2 -2
  72. package/src/safety/AuditLog.js +3 -3
  73. package/src/safety/CircuitBreaker.js +2 -2
  74. package/src/safety/CommandGuard.js +132 -0
  75. package/src/safety/FilesystemGuard.js +23 -3
  76. package/src/safety/GitRollback.js +5 -5
  77. package/src/safety/HumanApproval.js +9 -9
  78. package/src/safety/InputSanitizer.js +81 -8
  79. package/src/safety/PermissionGuard.js +2 -2
  80. package/src/safety/Sandbox.js +1 -1
  81. package/src/safety/SecretScanner.js +90 -28
  82. package/src/safety/SecretVault.js +2 -2
  83. package/src/scheduler/Heartbeat.js +3 -3
  84. package/src/scheduler/Scheduler.js +6 -6
  85. package/src/setup/theme.js +171 -66
  86. package/src/setup/wizard.js +432 -57
  87. package/src/skills/SkillLoader.js +145 -8
  88. package/src/storage/TaskStore.js +39 -15
  89. package/src/systemPrompt.js +45 -43
  90. package/src/tenants/TenantManager.js +79 -22
  91. package/src/tools/ToolRegistry.js +3 -3
  92. package/src/tools/applyPatch.js +2 -2
  93. package/src/tools/browserAutomation.js +4 -4
  94. package/src/tools/calendar.js +155 -0
  95. package/src/tools/clipboard.js +71 -0
  96. package/src/tools/contacts.js +138 -0
  97. package/src/tools/createDocument.js +2 -2
  98. package/src/tools/cronTool.js +14 -14
  99. package/src/tools/database.js +165 -0
  100. package/src/tools/editFile.js +10 -10
  101. package/src/tools/executeCommand.js +11 -3
  102. package/src/tools/generateImage.js +79 -0
  103. package/src/tools/gitTool.js +141 -0
  104. package/src/tools/glob.js +1 -1
  105. package/src/tools/googlePlaces.js +136 -0
  106. package/src/tools/grep.js +2 -2
  107. package/src/tools/iMessageTool.js +86 -0
  108. package/src/tools/imageAnalysis.js +3 -3
  109. package/src/tools/index.js +56 -2
  110. package/src/tools/makeVoiceCall.js +283 -0
  111. package/src/tools/manageAgents.js +2 -2
  112. package/src/tools/manageMCP.js +38 -20
  113. package/src/tools/memory.js +25 -32
  114. package/src/tools/messageChannel.js +1 -1
  115. package/src/tools/notification.js +90 -0
  116. package/src/tools/philipsHue.js +147 -0
  117. package/src/tools/projectTracker.js +8 -8
  118. package/src/tools/readFile.js +1 -1
  119. package/src/tools/readPDF.js +73 -0
  120. package/src/tools/screenCapture.js +6 -6
  121. package/src/tools/searchContent.js +2 -2
  122. package/src/tools/searchFiles.js +1 -1
  123. package/src/tools/sendEmail.js +79 -24
  124. package/src/tools/sendFile.js +4 -4
  125. package/src/tools/sonos.js +137 -0
  126. package/src/tools/sshTool.js +130 -0
  127. package/src/tools/textToSpeech.js +5 -5
  128. package/src/tools/transcribeAudio.js +4 -4
  129. package/src/tools/useMCP.js +4 -4
  130. package/src/tools/webFetch.js +2 -2
  131. package/src/tools/webSearch.js +1 -1
  132. package/src/utils/Embeddings.js +79 -0
  133. package/src/voice/VoiceSessionManager.js +170 -0
  134. package/src/voice/VoiceWebhook.js +188 -0
package/skills/things.md CHANGED
@@ -1,184 +1,20 @@
1
1
  ---
2
2
  name: things
3
- description: Manage Things 3 tasks, projects, and areas on macOS. Create todos with due dates, deadlines, checklists, tags, and notes. List inbox, today, upcoming, and anytime tasks. Search and update existing tasks. Use when the user asks to add a task to Things, check their to-do list, create a project, or manage Things 3. macOS only — requires Things 3 app installed.
3
+ description: Manage Things 3 tasks, projects, and areas on macOS. Create todos with due dates, deadlines, checklists, tags, and notes. List inbox, today, and upcoming tasks. Use when the user asks to add a task to Things, check their to-do list, create a project, or manage Things 3.
4
4
  triggers: things, things 3, add task, todo, to-do, create task, things inbox, things today, upcoming tasks, things project, things area, things tag
5
+ metadata: {"daemora": {"emoji": "✅", "install": ["brew install --cask things"], "os": ["darwin"]}}
5
6
  ---
6
7
 
7
- ## When to Use
8
-
9
- ✅ Add todos with dates/tags/projects, read inbox/today/upcoming, search tasks, move to a project, mark complete, create checklists
10
-
11
- ❌ Complex project management across teams (use Trello) — Things is personal task management; no API, no web access
12
-
13
- ## Requirements
14
-
15
- - macOS with Things 3 installed (`brew install --cask things`)
16
- - For read operations: grant Full Disk Access to Terminal/Daemora in System Settings → Privacy & Security
17
- - For write operations: Things URL scheme (no extra permissions needed)
18
-
19
- ## Read Tasks
8
+ ## Add task (URL scheme - no permissions needed)
20
9
 
21
10
  ```bash
22
- # Find Things 3 database
23
- THINGS_DB=$(find ~/Library/Group\ Containers -name "*.sqlite3" -path "*Things3*" 2>/dev/null | head -1)
24
- echo "DB: $THINGS_DB"
25
-
26
- # List today's tasks
27
- sqlite3 "$THINGS_DB" "
28
- SELECT title, dueDate, notes
29
- FROM TMTask
30
- WHERE status = 0
31
- AND (startDate <= strftime('%s','now') OR startDate IS NULL)
32
- AND trashed = 0
33
- ORDER BY dueDate ASC
34
- LIMIT 20;"
35
- ```
36
-
37
- ## Python Helper (Read + Write)
38
-
39
- ```python
40
- #!/usr/bin/env python3
41
- import subprocess, glob, sqlite3, os, urllib.parse
42
- from datetime import datetime, date
43
- from pathlib import Path
44
-
45
- # ── Read: direct SQLite access ─────────────────────────────────────────────
46
-
47
- def find_db() -> str:
48
- """Locate Things 3 SQLite database."""
49
- patterns = [
50
- os.path.expanduser("~/Library/Group Containers/*/Things/Database5/main.sqlite"),
51
- os.path.expanduser("~/Library/Group Containers/*/ThingsData-*/Things/Database5/main.sqlite"),
52
- ]
53
- for p in patterns:
54
- matches = glob.glob(p)
55
- if matches:
56
- return matches[0]
57
- raise FileNotFoundError("Things 3 database not found. Make sure Things 3 is installed and has been opened at least once.")
58
-
59
- def get_tasks(area: str = None, tag: str = None, limit: int = 30) -> list[dict]:
60
- """Get pending tasks from Things 3."""
61
- db_path = find_db()
62
- conn = sqlite3.connect(db_path)
63
- conn.row_factory = sqlite3.Row
64
- cur = conn.cursor()
65
-
66
- query = """
67
- SELECT t.uuid, t.title, t.notes, t.dueDate, t.deadline,
68
- p.title as project, a.title as area
69
- FROM TMTask t
70
- LEFT JOIN TMTask p ON t.project = p.uuid
71
- LEFT JOIN TMArea a ON t.area = a.uuid
72
- WHERE t.status = 0 AND t.trashed = 0 AND t.type = 0
73
- """
74
- params = []
75
- if area:
76
- query += " AND a.title LIKE ?"
77
- params.append(f"%{area}%")
78
- if tag:
79
- query += """ AND t.uuid IN (
80
- SELECT task FROM TMTaskTag tt
81
- JOIN TMTag tg ON tt.tag = tg.uuid
82
- WHERE tg.title LIKE ?)"""
83
- params.append(f"%{tag}%")
84
- query += f" ORDER BY t.dueDate ASC NULLS LAST LIMIT {limit}"
85
-
86
- rows = cur.execute(query, params).fetchall()
87
- conn.close()
88
- return [dict(r) for r in rows]
89
-
90
- def get_today() -> list[dict]:
91
- """Get tasks scheduled for today or overdue."""
92
- db_path = find_db()
93
- today_ts = int(datetime.combine(date.today(), datetime.min.time()).timestamp())
94
- conn = sqlite3.connect(db_path)
95
- conn.row_factory = sqlite3.Row
96
- cur = conn.cursor()
97
- rows = cur.execute("""
98
- SELECT t.uuid, t.title, t.notes, t.dueDate, t.deadline
99
- FROM TMTask t
100
- WHERE t.status = 0 AND t.trashed = 0 AND t.type = 0
101
- AND t.startDate IS NOT NULL AND t.startDate <= ?
102
- ORDER BY t.dueDate ASC NULLS LAST
103
- """, (today_ts,)).fetchall()
104
- conn.close()
105
- return [dict(r) for r in rows]
106
-
107
- # ── Write: Things URL scheme ────────────────────────────────────────────────
108
-
109
- def add_task(
110
- title: str,
111
- notes: str = "",
112
- when: str = "", # "today", "tomorrow", "evening", "anytime", "someday", or "YYYY-MM-DD"
113
- deadline: str = "", # "YYYY-MM-DD"
114
- tags: list[str] = None,
115
- list_name: str = "", # project or area name
116
- checklist: list[str] = None,
117
- heading: str = "",
118
- ) -> str:
119
- """Add a task to Things 3 using the URL scheme."""
120
- params = {"title": title}
121
- if notes: params["notes"] = notes
122
- if when: params["when"] = when
123
- if deadline: params["deadline"] = deadline
124
- if tags: params["tags"] = ",".join(tags)
125
- if list_name: params["list"] = list_name
126
- if heading: params["heading"] = heading
127
- if checklist:
128
- params["checklist-items"] = "\n".join(checklist)
129
-
130
- encoded = urllib.parse.urlencode(params)
131
- url = f"things:///add?{encoded}"
132
- subprocess.run(["open", url])
133
- return url
134
-
135
- def add_project(title: str, notes: str = "", area: str = "", when: str = "") -> None:
136
- """Create a new project in Things 3."""
137
- params = {"title": title}
138
- if notes: params["notes"] = notes
139
- if area: params["area"] = area
140
- if when: params["when"] = when
141
- encoded = urllib.parse.urlencode(params)
142
- subprocess.run(["open", f"things:///add-project?{encoded}"])
143
- ```
144
-
145
- ## Usage Examples
146
-
147
- ```python
148
- # Add a simple task
149
- add_task("Buy groceries")
150
-
151
- # Task with date and tags
152
- add_task(
153
- "Quarterly report",
154
- notes="See shared doc in Drive",
155
- when="2026-03-15",
156
- deadline="2026-03-20",
157
- tags=["work", "report"],
158
- list_name="Q1 Planning"
159
- )
160
-
161
- # Task with checklist
162
- add_task(
163
- "Deploy v2.0",
164
- checklist=["Run test suite", "Update CHANGELOG", "Tag release", "Deploy to prod", "Notify team"],
165
- list_name="Engineering",
166
- when="today"
167
- )
168
-
169
- # Read today's tasks
170
- tasks = get_today()
171
- print(f"📋 Today ({len(tasks)} tasks):")
172
- for t in tasks:
173
- print(f" □ {t['title']}")
174
-
175
- # Read tasks in a project
176
- tasks = get_tasks(area="Work")
177
- for t in tasks:
178
- print(f" □ {t['title']} [{t.get('project', 'no project')}]")
11
+ open "things:///add?title=Buy+groceries&when=today&tags=personal"
12
+ open "things:///add?title=Deploy+v2&when=2026-03-15&deadline=2026-03-20&list=Engineering"
13
+ open "things:///add-project?title=Q1+Planning&notes=..."
14
+ # Checklist items: &checklist-items=Step+1%0AStep+2%0AStep+3 (newline-separated, URL-encoded)
179
15
  ```
180
16
 
181
- ## `when` Values
17
+ ## `when` values
182
18
 
183
19
  | Value | Meaning |
184
20
  |-------|---------|
@@ -189,11 +25,23 @@ for t in tasks:
189
25
  | `someday` | Someday |
190
26
  | `YYYY-MM-DD` | Specific date |
191
27
 
192
- ## Error Handling
28
+ ## Read tasks (SQLite - requires Full Disk Access)
29
+
30
+ ```bash
31
+ THINGS_DB=$(find ~/Library/Group\ Containers -name "main.sqlite" -path "*Things*" 2>/dev/null | head -1)
32
+ sqlite3 "$THINGS_DB" "SELECT title, dueDate FROM TMTask WHERE status=0 AND trashed=0 AND type=0 ORDER BY dueDate ASC LIMIT 20;"
33
+ ```
34
+
35
+ ## Workflow
36
+
37
+ 1. **Adding tasks** → use the `things:///add` URL scheme (no permissions needed)
38
+ 2. **Reading tasks** → query the SQLite database with `executeCommand`
39
+ 3. **With checklists** → pass `checklist-items` URL param, newline-separated, URL-encoded
40
+
41
+ ## Errors
193
42
 
194
43
  | Error | Fix |
195
44
  |-------|-----|
196
45
  | DB not found | Open Things 3 first; try `find ~/Library -name "*.sqlite3" 2>/dev/null \| grep -i things` |
197
- | Permission denied on DB | System Settings → Privacy & Security → Full Disk Access → add Terminal |
46
+ | Permission denied on DB | System Settings → Privacy → Full Disk Access → add Terminal |
198
47
  | URL scheme not working | Ensure Things 3 is installed (`ls /Applications/Things3.app`) |
199
- | Task not appearing | Things syncs on open; click "Things" in dock to bring to front |
package/skills/tmux.md CHANGED
@@ -13,7 +13,7 @@ triggers: tmux, terminal session, background session, tmux window, tmux pane, sp
13
13
  ## Check tmux is Available
14
14
 
15
15
  ```bash
16
- which tmux && tmux -V || echo "tmux not found install with: brew install tmux"
16
+ which tmux && tmux -V || echo "tmux not found - install with: brew install tmux"
17
17
  ```
18
18
 
19
19
  ## Session Management
@@ -71,51 +71,6 @@ tmux capture-pane -t mywork -p -S -500 > /tmp/session_output.txt
71
71
  tmux capture-pane -t mywork -p | tail -5
72
72
  ```
73
73
 
74
- ## Python Helpers
75
-
76
- ```python
77
- #!/usr/bin/env python3
78
- import subprocess, time
79
-
80
- def tmux_run(session: str, command: str, wait_seconds: float = 0.5) -> str:
81
- """Send a command to a tmux session and capture output."""
82
- subprocess.run(["tmux", "send-keys", "-t", session, command, "Enter"])
83
- time.sleep(wait_seconds)
84
- result = subprocess.run(
85
- ["tmux", "capture-pane", "-t", session, "-p", "-S", "-100"],
86
- capture_output=True, text=True
87
- )
88
- return result.stdout.strip()
89
-
90
- def tmux_session_exists(session: str) -> bool:
91
- result = subprocess.run(["tmux", "has-session", "-t", session], capture_output=True)
92
- return result.returncode == 0
93
-
94
- def tmux_get_output(session: str, lines: int = 50) -> str:
95
- result = subprocess.run(
96
- ["tmux", "capture-pane", "-t", session, "-p", "-S", f"-{lines}"],
97
- capture_output=True, text=True
98
- )
99
- return result.stdout.strip()
100
-
101
- def tmux_ensure_session(session: str) -> None:
102
- """Create session if it doesn't exist."""
103
- if not tmux_session_exists(session):
104
- subprocess.run(["tmux", "new-session", "-d", "-s", session, "-x", "220", "-y", "50"])
105
- print(f"Created tmux session: {session}")
106
- else:
107
- print(f"Using existing session: {session}")
108
-
109
- # Example: run a dev server in tmux and monitor it
110
- tmux_ensure_session("devserver")
111
- output = tmux_run("devserver", "npm run dev", wait_seconds=2.0)
112
- print(f"Server output:\n{output}")
113
-
114
- # Later: check if server is still running
115
- current = tmux_get_output("devserver", lines=10)
116
- print(f"Current output:\n{current}")
117
- ```
118
-
119
74
  ## Split Panes (Parallel Work)
120
75
 
121
76
  ```bash
@@ -138,51 +93,6 @@ tmux split-window -v -t monitor:0.1
138
93
  tmux send-keys -t monitor:0.2 "watch -n 2 df -h" Enter
139
94
  ```
140
95
 
141
- ## Monitor a Long-Running Task
142
-
143
- ```python
144
- #!/usr/bin/env python3
145
- """Start a task in tmux and poll until it's done."""
146
- import subprocess, time, re
147
-
148
- def run_and_monitor(session: str, command: str, done_pattern: str, timeout: int = 300):
149
- """
150
- Run command in tmux and wait until output matches done_pattern.
151
- Returns final output.
152
- """
153
- # Ensure session exists
154
- subprocess.run(["tmux", "new-session", "-d", "-s", session, "-x", "220", "-y", "50"],
155
- capture_output=True) # ignore error if exists
156
-
157
- subprocess.run(["tmux", "send-keys", "-t", session, command, "Enter"])
158
- print(f"Started: {command}")
159
-
160
- start = time.time()
161
- while time.time() - start < timeout:
162
- time.sleep(3)
163
- result = subprocess.run(
164
- ["tmux", "capture-pane", "-t", session, "-p", "-S", "-50"],
165
- capture_output=True, text=True
166
- )
167
- output = result.stdout
168
- if re.search(done_pattern, output, re.IGNORECASE):
169
- print(f"✅ Done (matched: {done_pattern})")
170
- return output
171
-
172
- print(f"⚠️ Timed out after {timeout}s")
173
- return subprocess.run(["tmux", "capture-pane", "-t", session, "-p", "-S", "-50"],
174
- capture_output=True, text=True).stdout
175
-
176
- # Example
177
- output = run_and_monitor(
178
- session="build",
179
- command="npm run build",
180
- done_pattern=r"build complete|error|failed",
181
- timeout=120
182
- )
183
- print(output[-500:]) # show last 500 chars
184
- ```
185
-
186
96
  ## Useful Shortcuts Reference
187
97
 
188
98
  ```
package/skills/trello.md CHANGED
@@ -1,183 +1,58 @@
1
1
  ---
2
2
  name: trello
3
- description: Manage Trello boards, lists, and cards using the Trello REST API. Use when the user asks to create a Trello card, move a card, list boards, add a checklist, archive a card, or automate Trello workflows. Requires TRELLO_API_KEY and TRELLO_TOKEN environment variables.
3
+ description: Manage Trello boards, lists, and cards via the Trello REST API. Use when the user asks to create a Trello card, move a card, list boards, add a checklist, or automate Trello workflows. Requires TRELLO_API_KEY and TRELLO_TOKEN.
4
4
  triggers: trello, kanban, trello card, trello board, create card, move card, trello list, trello checklist, trello archive, trello label
5
+ metadata: {"daemora": {"emoji": "📋"}}
5
6
  ---
6
7
 
7
- ## When to Use
8
-
9
- ✅ Create/update/move/archive cards, list boards and cards, add checklists, labels, due dates, comments, attach files, automate Trello workflows
10
-
11
- ❌ Real-time push notifications (use Trello webhooks + a server endpoint) — use polling for monitoring instead
12
-
13
8
  ## Setup
14
9
 
15
- ```bash
16
- # Get your API key: https://trello.com/app-key
17
- # Generate token from that page
10
+ Get API key: https://trello.com/app-key (then generate token from that page)
18
11
 
12
+ ```bash
19
13
  export TRELLO_API_KEY="your_api_key"
20
14
  export TRELLO_TOKEN="your_token"
21
15
 
22
- # Quick test
23
- curl -s "https://api.trello.com/1/members/me/boards?key=${TRELLO_API_KEY}&token=${TRELLO_TOKEN}" \
16
+ # Quick test: list boards
17
+ curl -s "https://api.trello.com/1/members/me/boards?fields=name,id&key=$TRELLO_API_KEY&token=$TRELLO_TOKEN" \
24
18
  | python3 -c "import sys,json; [print(b['name'], '→', b['id']) for b in json.load(sys.stdin)]"
25
19
  ```
26
20
 
27
- ## Python Helper (use for all Trello calls)
21
+ ## Key API endpoints
28
22
 
29
- ```python
30
- #!/usr/bin/env python3
31
- import os, json, urllib.request, urllib.parse
23
+ Base: `https://api.trello.com/1` - always append `key=$TRELLO_API_KEY&token=$TRELLO_TOKEN`
32
24
 
33
- API_KEY = os.environ["TRELLO_API_KEY"]
34
- TOKEN = os.environ["TRELLO_TOKEN"]
35
- BASE = "https://api.trello.com/1"
25
+ | Operation | Method | Endpoint |
26
+ |-----------|--------|----------|
27
+ | List boards | GET | `/members/me/boards?fields=name,id` |
28
+ | Lists on board | GET | `/boards/{id}/lists?filter=open` |
29
+ | Cards on board | GET | `/boards/{id}/cards?fields=name,idList,due` |
30
+ | Cards in list | GET | `/lists/{id}/cards` |
31
+ | Create card | POST | `/cards` |
32
+ | Move card | PUT | `/cards/{id}` with `idList` param |
33
+ | Archive card | PUT | `/cards/{id}` with `closed=true` |
34
+ | Add comment | POST | `/cards/{id}/actions/comments` |
35
+ | Add checklist | POST | `/checklists` with `idCard` param |
36
36
 
37
- def trello(method: str, path: str, **kwargs) -> dict | list:
38
- """Make a Trello API call. kwargs become query params for GET, form data for POST/PUT."""
39
- params = {"key": API_KEY, "token": TOKEN, **kwargs}
40
- url = f"{BASE}{path}"
41
- if method == "GET":
42
- url += "?" + urllib.parse.urlencode(params)
43
- req = urllib.request.Request(url)
44
- else:
45
- data = urllib.parse.urlencode(params).encode()
46
- req = urllib.request.Request(url, data=data, method=method)
47
- with urllib.request.urlopen(req) as resp:
48
- return json.loads(resp.read())
49
- ```
37
+ ## Create card example
50
38
 
51
- ## List Boards
52
-
53
- ```python
54
- boards = trello("GET", "/members/me/boards", fields="name,id,url,closed")
55
- open_boards = [b for b in boards if not b.get("closed")]
56
- for b in open_boards:
57
- print(f"📋 {b['name']} (id: {b['id']})")
58
- ```
59
-
60
- ## Get Lists on a Board
61
-
62
- ```python
63
- board_id = "BOARD_ID_HERE"
64
- lists = trello("GET", f"/boards/{board_id}/lists", filter="open")
65
- for lst in lists:
66
- print(f" 📁 {lst['name']} (id: {lst['id']})")
67
- ```
68
-
69
- ## Get Cards on a Board / List
70
-
71
- ```python
72
- # All cards on a board
73
- board_id = "BOARD_ID_HERE"
74
- cards = trello("GET", f"/boards/{board_id}/cards", fields="name,idList,due,desc,labels")
75
- for card in cards:
76
- print(f" 🃏 {card['name']} (due: {card.get('due', 'none')})")
77
-
78
- # Cards in a specific list
79
- list_id = "LIST_ID_HERE"
80
- cards = trello("GET", f"/lists/{list_id}/cards", fields="name,due,desc")
81
- ```
82
-
83
- ## Create a Card
84
-
85
- ```python
86
- new_card = trello("POST", "/cards",
87
- idList="LIST_ID_HERE",
88
- name="Task: Fix login bug",
89
- desc="## Description\nThe login form fails when email contains special characters.\n\n## Steps\n1. Go to /login\n2. Enter: user+test@example.com\n3. See error",
90
- due="2026-03-15T09:00:00.000Z", # ISO 8601
91
- pos="top" # or "bottom" or a position number
92
- )
93
- print(f"✅ Card created: {new_card['name']} → {new_card['url']}")
94
- ```
95
-
96
- ## Move Card to Another List
97
-
98
- ```python
99
- card_id = "CARD_ID_HERE"
100
- target_list_id = "TARGET_LIST_ID"
101
- trello("PUT", f"/cards/{card_id}", idList=target_list_id)
102
- print("✅ Card moved")
103
- ```
104
-
105
- ## Add Checklist to Card
106
-
107
- ```python
108
- card_id = "CARD_ID_HERE"
109
-
110
- # Create checklist
111
- checklist = trello("POST", "/checklists", idCard=card_id, name="Acceptance Criteria")
112
-
113
- # Add items
114
- for item in ["Tests pass", "Code reviewed", "Deployed to staging", "QA sign-off"]:
115
- trello("POST", f"/checklists/{checklist['id']}/checkItems", name=item)
116
-
117
- print(f"✅ Checklist created with {4} items")
118
- ```
119
-
120
- ## Add Comment to Card
121
-
122
- ```python
123
- card_id = "CARD_ID_HERE"
124
- trello("POST", f"/cards/{card_id}/actions/comments",
125
- text="Investigated this issue — root cause is in auth.js:142. PR coming shortly.")
126
- ```
127
-
128
- ## Add Label to Card
129
-
130
- ```python
131
- # First get or create a label
132
- board_id = "BOARD_ID_HERE"
133
- labels = trello("GET", f"/boards/{board_id}/labels")
134
- bug_label = next((l for l in labels if l.get("name") == "Bug"), None)
135
-
136
- if bug_label:
137
- card_id = "CARD_ID_HERE"
138
- trello("POST", f"/cards/{card_id}/idLabels", value=bug_label["id"])
139
- ```
140
-
141
- ## Archive (Close) a Card
142
-
143
- ```python
144
- card_id = "CARD_ID_HERE"
145
- trello("PUT", f"/cards/{card_id}", closed="true")
146
- print("✅ Card archived")
39
+ ```bash
40
+ curl -s -X POST "https://api.trello.com/1/cards" \
41
+ -d "key=$TRELLO_API_KEY&token=$TRELLO_TOKEN" \
42
+ -d "idList=LIST_ID&name=Task+title&desc=Description&due=2026-03-15T09:00:00.000Z&pos=top"
147
43
  ```
148
44
 
149
- ## Full Workflow: Create Card from Task Description
150
-
151
- ```python
152
- def add_task_to_trello(title: str, description: str, board_name: str = None, list_name: str = "To Do"):
153
- """Find the right board/list and create a card."""
154
- boards = trello("GET", "/members/me/boards", fields="name,id")
155
- board = next((b for b in boards if board_name and board_name.lower() in b["name"].lower()), boards[0])
156
-
157
- lists = trello("GET", f"/boards/{board['id']}/lists", filter="open")
158
- target_list = next((l for l in lists if list_name.lower() in l["name"].lower()), lists[0])
45
+ ## Move card example
159
46
 
160
- card = trello("POST", "/cards",
161
- idList=target_list["id"],
162
- name=title,
163
- desc=description,
164
- pos="top"
165
- )
166
- return card["url"]
167
-
168
- url = add_task_to_trello(
169
- "Research competitor pricing",
170
- "## Goal\nAnalyze top 5 competitor pricing pages\n\n## Deliverable\nComparison spreadsheet",
171
- board_name="Marketing"
172
- )
173
- print(f"Card created: {url}")
47
+ ```bash
48
+ curl -s -X PUT "https://api.trello.com/1/cards/CARD_ID" \
49
+ -d "key=$TRELLO_API_KEY&token=$TRELLO_TOKEN&idList=TARGET_LIST_ID"
174
50
  ```
175
51
 
176
- ## Error Handling
52
+ ## Errors
177
53
 
178
54
  | Error | Fix |
179
55
  |-------|-----|
180
- | 401 Unauthorized | Check `TRELLO_API_KEY` and `TRELLO_TOKEN` are set and valid |
181
- | 404 Not Found | Board/list/card ID is wrong; list boards/lists first to get correct IDs |
182
- | 429 Rate Limited | Trello allows ~300 req/10s; add `time.sleep(0.1)` between bulk calls |
183
- | Card won't move | Verify target list is on same board as card |
56
+ | 401 Unauthorized | Check `TRELLO_API_KEY` and `TRELLO_TOKEN` are valid |
57
+ | 404 Not Found | Board/list/card ID is wrong - list boards/lists first to get correct IDs |
58
+ | 429 Rate Limited | Add 100ms delay between bulk calls |