@llodev/pm-tasks-trello 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.
- package/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +50 -0
- package/SKILL.md +83 -0
- package/anti-patterns/tools.md +45 -0
- package/package.json +42 -0
- package/references/autonomous.md +45 -0
- package/references/format.md +113 -0
- package/references/mcp-config.md +225 -0
- package/references/operations.md +45 -0
- package/references/publish.md +107 -0
- package/schemas/config.json +130 -0
- package/scripts/init.mjs +175 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# @llodev/pm-tasks-trello
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- [`a571ab1`](https://github.com/llodev/skills/commit/a571ab1537ea7d3fe61c7b89c5be0f08d01f3838) - First stable release of the pm-tasks-\* family.
|
|
8
|
+
|
|
9
|
+
- `@llodev/pm-tasks-core` — Phases 1–3 extraction pipeline (input → sections → generic card), 6 CRUD verbs (`task.create`, `checklist.check`, `task.close`, `task.due-date.set`, `task.assignee.add`, `task.comment.add`), autonomous-mode contract (allowlist + scope + rate-limit + audit log), shared init UX library.
|
|
10
|
+
- `@llodev/pm-tasks-trello` — Trello adapter on the canonical generic card. Paste-friendly output, MCP-driven publish, autonomous mode against a board allowlist.
|
|
11
|
+
- `@llodev/pm-tasks-asana` — Asana adapter with workspace/project/section + custom-field + subtask-inheritance support. Paste, MCP-driven publish, autonomous mode.
|
|
12
|
+
|
|
13
|
+
Architecture, contract, and CRUD vocabulary documented in `docs/specs/2026-06-11-pm-tasks-design.md` and `docs/plans/2026-06-11-pm-tasks-v1.md`.
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [[`a571ab1`](https://github.com/llodev/skills/commit/a571ab1537ea7d3fe61c7b89c5be0f08d01f3838)]:
|
|
18
|
+
- @llodev/pm-tasks-core@1.0.0
|
|
19
|
+
|
|
20
|
+
## 0.1.0 (unreleased)
|
|
21
|
+
|
|
22
|
+
- Initial extraction from `plan-to-task-cards` Phase 5 (Trello).
|
|
23
|
+
- 6 CRUD verbs (create, checklist.check, close, due-date.set, assignee.add, comment.add).
|
|
24
|
+
- Autonomous mode behind `[autonomous]` sentinel + allowlist.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LLDev Information Solutions
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# @llodev/pm-tasks-trello
|
|
2
|
+
|
|
3
|
+
Trello adapter for the `@llodev/pm-tasks-*` family. Convert implementation plans into Trello cards (paste-ready or published via MCP) and operate them (`checklist.check`, `task.close`, `task.comment.add`, etc.).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# npm (with skillpm or Claude Code marketplace)
|
|
9
|
+
npm i @llodev/pm-tasks-core @llodev/pm-tasks-trello
|
|
10
|
+
|
|
11
|
+
# Vercel CLI (install core manually too)
|
|
12
|
+
npx skills add llodev/skills/pm-tasks-core
|
|
13
|
+
npx skills add llodev/skills/pm-tasks-trello
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Setup the MCP
|
|
17
|
+
|
|
18
|
+
Claude Code:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
claude mcp add trello -s project -- npx -y atlassian-trello-mcp
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Approve via `/mcp` in chat. Export env vars in your shell:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
export TRELLO_API_KEY=...
|
|
28
|
+
export TRELLO_TOKEN=...
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Cursor/Windsurf: see [`references/mcp-config.md`](references/mcp-config.md).
|
|
32
|
+
|
|
33
|
+
## Setup the config
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx @llodev/pm-tasks-trello init
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Walk through the prompts. Output: `.trello.json` (repo) or `~/.config/llodev/pm-tasks/trello.json` (global).
|
|
40
|
+
|
|
41
|
+
## Use
|
|
42
|
+
|
|
43
|
+
- `"publish this plan as Trello cards"` → publish flow
|
|
44
|
+
- `"check item 3 on task X in Trello"` → CRUD op
|
|
45
|
+
- `"close card Y"` → close
|
|
46
|
+
- `"[autonomous] create task in trello from plan @docs/plans/X.md"` → autonomous (requires `autonomous.enabled: true` in config)
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
MIT — see [LICENSE](./LICENSE).
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pm-tasks-trello
|
|
3
|
+
description: >-
|
|
4
|
+
Trello adapter for the @llodev/pm-tasks-* family. Use when the user mentions
|
|
5
|
+
Trello, asks to "create card", "publish to Trello", "post to Trello",
|
|
6
|
+
"publish", or uses --publish; OR for CRUD on existing cards (check
|
|
7
|
+
checklist item, close card, change due-date, add member, comment); OR when
|
|
8
|
+
invoked autonomously by another agent with [autonomous] / --auto sentinel.
|
|
9
|
+
Modes: paste-ready (no MCP needed), MCP publish (via atlassian-trello-mcp),
|
|
10
|
+
autonomous (write-through with allowlist). Implements 6 CRUD verbs
|
|
11
|
+
(task.create, checklist.check, task.close, task.due-date.set,
|
|
12
|
+
task.assignee.add, task.comment.add) from
|
|
13
|
+
pm-tasks/pm-tasks-core/references/contract.md. Requires @llodev/pm-tasks-core
|
|
14
|
+
installed.
|
|
15
|
+
license: MIT
|
|
16
|
+
metadata:
|
|
17
|
+
version: 1.0.0
|
|
18
|
+
tags:
|
|
19
|
+
- agent-skill
|
|
20
|
+
- trello
|
|
21
|
+
- plan-to-tasks
|
|
22
|
+
- pm-tools
|
|
23
|
+
family: pm-tasks
|
|
24
|
+
role: adapter
|
|
25
|
+
tool: trello
|
|
26
|
+
compatibility:
|
|
27
|
+
agents:
|
|
28
|
+
- claude-code
|
|
29
|
+
- cursor
|
|
30
|
+
- codex
|
|
31
|
+
- windsurf
|
|
32
|
+
- cline
|
|
33
|
+
- roo-code
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
# pm-tasks-trello
|
|
37
|
+
|
|
38
|
+
Adapter for Trello within the `@llodev/pm-tasks-*` family. Use the core skill's extraction phases, then apply Trello formatting and optionally publish/operate via the `atlassian-trello-mcp` MCP server.
|
|
39
|
+
|
|
40
|
+
## Routing
|
|
41
|
+
|
|
42
|
+
| Mode | Trigger | Path |
|
|
43
|
+
| ----------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
44
|
+
| Paste-only | "format as Trello card" without MCP intent | Phase 3 (core) → Phase 4 (this skill, format only) → output paste blocks |
|
|
45
|
+
| MCP publish | "publish to Trello", "create on Trello", "--publish" | Phase 3 → Phase 4 → Phase 5 (publish via MCP) |
|
|
46
|
+
| Autonomous | `[autonomous]` or `--auto` in prompt OR `LLODEV_PM_TASKS_AUTONOMOUS=1` | Phase 3 → Phase 4 → Phase 5b (write-through, no preview) |
|
|
47
|
+
| CRUD ops | "check item N on task X", "close card Y", "add Alice to task Z", "comment on task X" | Phase 6 (operations, direct verb dispatch) |
|
|
48
|
+
|
|
49
|
+
## Phase 4 — Trello formatting
|
|
50
|
+
|
|
51
|
+
**MANDATORY — READ ENTIRE FILE** [`references/format.md`](references/format.md) before producing any Trello-specific output. Then apply [`anti-patterns/tools.md`](anti-patterns/tools.md) § Trello.
|
|
52
|
+
|
|
53
|
+
## Phase 5 — MCP publish
|
|
54
|
+
|
|
55
|
+
**Prerequisites:** `atlassian-trello-mcp` configured (see [`references/mcp-config.md`](references/mcp-config.md)). Env vars `TRELLO_API_KEY` + `TRELLO_TOKEN` in shell.
|
|
56
|
+
|
|
57
|
+
Strict order: 5.1 config discovery → 5.2.5 resolve labels/member → 5.2 preview & approval → 5.3 publish via MCP → 5.4 error handling.
|
|
58
|
+
|
|
59
|
+
Full sequence in [`references/publish.md`](references/publish.md).
|
|
60
|
+
|
|
61
|
+
## Phase 5b — Autonomous
|
|
62
|
+
|
|
63
|
+
Skip 5.2 preview & approval. Apply autonomous-mode contract from [`pm-tasks/pm-tasks-core/references/autonomous-mode.md`](../pm-tasks-core/references/autonomous-mode.md). Tool-specific overlay in [`references/autonomous.md`](references/autonomous.md). Audit log entries per [`pm-tasks/pm-tasks-core/references/audit-log-format.md`](../pm-tasks-core/references/audit-log-format.md).
|
|
64
|
+
|
|
65
|
+
## Phase 6 — CRUD operations (existing cards)
|
|
66
|
+
|
|
67
|
+
For verbs other than `task.create`, jump directly to the operation. **MANDATORY — READ ENTIRE FILE** [`references/operations.md`](references/operations.md) which lists verb → MCP tool mapping and `<task-ref>` resolution for Trello URLs/IDs.
|
|
68
|
+
|
|
69
|
+
## Standalone fallback
|
|
70
|
+
|
|
71
|
+
If `@llodev/pm-tasks-core` is not installed: ask the user for minimum input (title + checklist items) and produce a paste-ready Trello card from this content alone. Quality is degraded — no scope/audience/fidelity inference. Print: *"Install `@llodev/pm-tasks-core` for the full flow."*
|
|
72
|
+
|
|
73
|
+
## Config
|
|
74
|
+
|
|
75
|
+
Lookup order: `<git-root>/.trello.json` → `~/.config/llodev/pm-tasks/trello.json` → abort with init instructions. Schema: [`schemas/config.json`](schemas/config.json). Secrets NEVER in JSON — only env vars / keychain.
|
|
76
|
+
|
|
77
|
+
## Init
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
npx @llodev/pm-tasks-trello init
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
See [`pm-tasks/pm-tasks-core/references/init-ux.md`](../pm-tasks-core/references/init-ux.md) for the shared flow.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Trello ingestion anti-patterns
|
|
2
|
+
|
|
3
|
+
Apply **only** when Phase 4 targets Trello (after reading [`../references/format.md`](../references/format.md)). These are paste/rendering pitfalls that **generic markdown** intuition gets wrong.
|
|
4
|
+
|
|
5
|
+
**Load this entire file** in Phase 4. **Do NOT load** for generic-only output.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Paste health and fallbacks (canonical)
|
|
10
|
+
|
|
11
|
+
Use when preview looks wrong **or** the user says paste mangled formatting. **Authoritative detail** also lives in [`../references/format.md`](../references/format.md).
|
|
12
|
+
|
|
13
|
+
| Target | Healthy paste | If it degrades → fallback |
|
|
14
|
+
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
15
|
+
| **Trello** | **No Markdown tables** anywhere in DESCRIPTION (timeline, tiers, grids) — bullets or `**Key:** value` lines only. Checklist lines **flat**, no nesting. | Flatten tables → bullets; reread § _Tables vs paste_ in [`../references/format.md`](../references/format.md). |
|
|
16
|
+
|
|
17
|
+
**Switching tools mid-thread:** re-run Phase 4 **exclusive** load for the **new** tool; do not carry Trello rules into another adapter (see **Cross-tool** below).
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Trello
|
|
22
|
+
|
|
23
|
+
**NEVER** leave **Markdown tables** in descriptions or timelines intended for Trello. **Why:** Trello descriptions do **not** render tables — users see garbage rows. Replace with bullets or a compact key-value bullet list.
|
|
24
|
+
|
|
25
|
+
**NEVER** use nested markdown (`- item\n - child`) inside **checklist items**. **Why:** Trello checklist lines are flat; nesting is misleading or stripped.
|
|
26
|
+
|
|
27
|
+
**NEVER** assume parent/child **card URLs** exist before cards are created. **Why:** links are placeholders until created; prefer explicit note: `"create child cards then replace #link placeholders"`.
|
|
28
|
+
|
|
29
|
+
**NEVER** exceed **~80 chars** on titles without warning. **Why:** truncates in board view; title rule in [`pm-tasks/pm-tasks-core/references/generic-card.md`](../../pm-tasks-core/references/generic-card.md) exists for this.
|
|
30
|
+
|
|
31
|
+
**NEVER** call `create_card` / `trello_create_checklist` before the user confirms the Phase 5 preview. **Why:** creates real board objects without approval.
|
|
32
|
+
|
|
33
|
+
**NEVER** pass label **names** to `create_card` — only **`idLabels`** (and **`idMembers`** with member IDs) resolved from `.trello.json`. **Why:** the API rejects names; wrong IDs attach to the wrong board.
|
|
34
|
+
|
|
35
|
+
**NEVER** guess `boardId`, `listId`, `labels[].id`, or `members[].id` when `.trello.json` exists. **Why:** stale or invented IDs put cards on the wrong board or column; refresh via the discovery flow in [`../references/mcp-config.md`](../references/mcp-config.md) § **MCP config discovery**.
|
|
36
|
+
|
|
37
|
+
**NEVER** auto-create labels with `trello_create_label` during publish unless the user explicitly asks. **Why:** pollutes the board palette; omit unknown names and report **omitted**.
|
|
38
|
+
|
|
39
|
+
**NEVER** commit the active token user (or any per-developer member id) into `members[]` in `.trello.json`. **Why:** the file is shared in git; resolve "me" at runtime via `trello_get_member({ memberId: "me" })` at Phase 5.2.5 — each developer's `TRELLO_TOKEN` resolves to themselves without editing the repo.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Cross-tool
|
|
44
|
+
|
|
45
|
+
**NEVER** apply one tool's quirks to another after switching targets mid-chat. **Why:** user said "actually use Jira/Asana" — re-read the matching adapter reference + **re-apply only that adapter's rules** instead of these.
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@llodev/pm-tasks-trello",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Trello adapter for the @llodev/pm-tasks-* family. Use when the user mentions Trello (create card, publish to Trello, post to Trello, --publish, check checklist item, close card, comment on card) or wants to publish a plan as Trello cards. Modes: paste-ready (no MCP needed), MCP publish (via atlassian-trello-mcp), autonomous (sentinel [autonomous] / --auto). Implements 6 CRUD verbs from @llodev/pm-tasks-core/references/contract.md. REQUIRES: @llodev/pm-tasks-core installed (skillpm / Claude Code marketplace cascade auto; Vercel CLI users install manually).",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://github.com/llodev/skills/tree/main/pm-tasks/pm-tasks-trello",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/llodev/skills.git",
|
|
10
|
+
"directory": "pm-tasks/pm-tasks-trello"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"SKILL.md",
|
|
14
|
+
"schemas",
|
|
15
|
+
"references",
|
|
16
|
+
"anti-patterns",
|
|
17
|
+
"scripts",
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"README.md",
|
|
20
|
+
"CHANGELOG.md"
|
|
21
|
+
],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"agent-skill",
|
|
24
|
+
"trello",
|
|
25
|
+
"plan-to-tasks",
|
|
26
|
+
"pm-tools",
|
|
27
|
+
"atlassian-trello-mcp"
|
|
28
|
+
],
|
|
29
|
+
"type": "module",
|
|
30
|
+
"bin": {
|
|
31
|
+
"pm-tasks-trello-init": "./scripts/init.mjs"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@llodev/pm-tasks-core": "^1.0.0"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=20"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Autonomous mode — Trello overlay
|
|
2
|
+
|
|
3
|
+
Supplements [`pm-tasks/pm-tasks-core/references/autonomous-mode.md`](../../pm-tasks-core/references/autonomous-mode.md) with Trello-specific guidance.
|
|
4
|
+
|
|
5
|
+
## Scope semantics
|
|
6
|
+
|
|
7
|
+
`autonomous.scope.boards` → values are Trello board IDs (24-char hex). Operations targeting any card outside these boards fail `OUT_OF_SCOPE`.
|
|
8
|
+
|
|
9
|
+
`autonomous.scope.lists` → values are list IDs. For `task.create`, the target list MUST be in this set. For `task.close`, the destination list (`closeListAlias` resolved) MUST be in this set.
|
|
10
|
+
|
|
11
|
+
## Allowlist defaults (suggested by `init`)
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
"autonomous": {
|
|
15
|
+
"enabled": false,
|
|
16
|
+
"allow": ["task.create", "checklist.check", "task.close", "task.comment.add"],
|
|
17
|
+
"scope": { "boards": [], "lists": [] },
|
|
18
|
+
"rateLimit": { "writesPerMinute": 30, "commentsPerMinute": 10 },
|
|
19
|
+
"auditLog": "~/.local/share/llodev/pm-tasks/trello/audit.log"
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Note `enabled: false` and empty `scope.*` — user must explicitly turn on AND populate scope. Init prints a warning explaining why.
|
|
24
|
+
|
|
25
|
+
## Audit log entries — verb-specific shape
|
|
26
|
+
|
|
27
|
+
```jsonl
|
|
28
|
+
{"ts":"...","verb":"task.create","tool":"trello","ok":true,"id":"<cardId>","url":"https://trello.com/c/<shortLink>","name":"...","clientToken":"...","scope":{"board":"<boardId>","list":"<listId>"},"session":"..."}
|
|
29
|
+
{"ts":"...","verb":"checklist.check","tool":"trello","ok":true,"id":"<cardId>","item":"<checkItem name>","session":"..."}
|
|
30
|
+
{"ts":"...","verb":"task.close","tool":"trello","ok":true,"id":"<cardId>","session":"..."}
|
|
31
|
+
{"ts":"...","verb":"task.due-date.set","tool":"trello","ok":true,"id":"<cardId>","due":"2026-07-01T00:00:00Z","session":"..."}
|
|
32
|
+
{"ts":"...","verb":"task.assignee.add","tool":"trello","ok":true,"id":"<cardId>","userAlias":"me","session":"..."}
|
|
33
|
+
{"ts":"...","verb":"task.comment.add","tool":"trello","ok":true,"id":"<cardId>","commentId":"<actionId>","clientToken":"...","session":"..."}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Rate-limit handling
|
|
37
|
+
|
|
38
|
+
`atlassian-trello-mcp` returns HTTP 429 with `Retry-After` header. Adapter inspects the error, returns `{ ok: false, code: "RATE_LIMITED", details: { retryAfterSeconds: <n> } }`. Skill does NOT auto-retry — caller decides.
|
|
39
|
+
|
|
40
|
+
## Failure recovery for partial `task.create`
|
|
41
|
+
|
|
42
|
+
If `create_card` succeeds but `trello_create_checklist` or `trello_create_check_item` fails:
|
|
43
|
+
|
|
44
|
+
- Return `{ ok: false, code: "MCP_ERROR" }` with `details: { partialCard: { id, url }, completedSteps: [...], failedStep: "..." }`.
|
|
45
|
+
- Caller can retry with same `clientToken` — idempotency check finds the partial card, only retries the failed steps.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Trello Formatting Reference
|
|
2
|
+
|
|
3
|
+
## Tables vs paste (critical)
|
|
4
|
+
|
|
5
|
+
Markdown **tables do not render** in Trello descriptions — pasted `|` grids show as unusable prose.
|
|
6
|
+
|
|
7
|
+
When filling **`DESCRIPTION`** (and any prose meant for paste): **flatten all tables** from the generic card (timeline, tier summary, deliverables grids, etc.) into **bullets** or lines like `**Optimistic:** 4h`, `**Realistic:** 6h`.
|
|
8
|
+
|
|
9
|
+
**Routing tables inside _this_ file** (checklist naming, labels) are **instructions for you** mapping blocks → structure. **Never** copy those tables into the card body.
|
|
10
|
+
|
|
11
|
+
Paste/rendering **NEVER** rules: [`../anti-patterns/tools.md`](../anti-patterns/tools.md) (Trello section).
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Card anatomy
|
|
16
|
+
|
|
17
|
+
Trello cards have: **title**, **description** (Markdown, free-text), **labels** (colored tags), **checklist(s)** (named, checkboxes), **due date**, and **attachments**. There is no native "ticket type" or hierarchy — parent/child cards are simulated with links.
|
|
18
|
+
|
|
19
|
+
## Title
|
|
20
|
+
|
|
21
|
+
- Max ~100 chars (Trello truncates in board view around 80).
|
|
22
|
+
- Format: `[Area] — PhaseN (summary)` — matches the generic format, no change needed.
|
|
23
|
+
|
|
24
|
+
## Description
|
|
25
|
+
|
|
26
|
+
Trello descriptions render Markdown. Use the generic description block as-is.
|
|
27
|
+
|
|
28
|
+
Additional Trello conventions:
|
|
29
|
+
|
|
30
|
+
- Add a **Spec/Plan links** block at the top if the files are in a git repo with a URL:
|
|
31
|
+
```
|
|
32
|
+
📄 [Spec](https://github.com/.../spec.md) · [Plan](https://github.com/.../plan.md)
|
|
33
|
+
```
|
|
34
|
+
- Separate sections with `---` (renders as horizontal rule).
|
|
35
|
+
- Trello does **not** render tables — replace the Deliverables table with a bullet list if you used one.
|
|
36
|
+
|
|
37
|
+
## Checklists
|
|
38
|
+
|
|
39
|
+
Trello supports **multiple named checklists on one card**. Split as follows:
|
|
40
|
+
|
|
41
|
+
| Generic block | Trello checklist name |
|
|
42
|
+
| ----------------- | ------------------------------- |
|
|
43
|
+
| Pre-flight | `Pre-flight` |
|
|
44
|
+
| Block A / Group 1 | `Implementation — [Block Name]` |
|
|
45
|
+
| Block B / Group 2 | `Implementation — [Block Name]` |
|
|
46
|
+
| ... | (one checklist per block) |
|
|
47
|
+
| Verification | `Verification` |
|
|
48
|
+
|
|
49
|
+
For small cards (≤10 tasks total), use a single `Checklist` instead of splitting.
|
|
50
|
+
|
|
51
|
+
Trello checklist item format: plain text, no markdown inside items.
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
Task 1 — scaffold workspace (config files)
|
|
55
|
+
Task 2 — extend env schema with Firebase vars (TDD)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Avoid nesting. Trello items are flat. **Do not** prefix items with `✅` / `[x]` in the paste payload — Trello's native checkboxes track state.
|
|
59
|
+
|
|
60
|
+
If you show a **sample** checklist to the user outside the paste block, plain `✅` is fine for readability.
|
|
61
|
+
|
|
62
|
+
## Labels
|
|
63
|
+
|
|
64
|
+
Trello labels are **board-scoped** and identified by **ID** in the API. When `.trello.json` exists, use only label **names** listed in `labels[]` — do not invent labels or use empty-name color stubs from the board.
|
|
65
|
+
|
|
66
|
+
For paste output, the agent infers candidate labels from the generic card content (file paths it touches, plan keywords, the LABELS block) and matches them against `labels[]` by `name` or `alias`. If a candidate has no match in `labels[]` (e.g. `phase-2`, `ddd`), omit it and list under **omitted** in the wrapper — do not call `trello_create_label` unless the user explicitly asks.
|
|
67
|
+
|
|
68
|
+
Apply **3–5** named labels max (board gets noisy beyond that).
|
|
69
|
+
|
|
70
|
+
## Due date
|
|
71
|
+
|
|
72
|
+
Set to the **realistic** estimate from the timeline section, starting from today.
|
|
73
|
+
|
|
74
|
+
If the estimate is a range (e.g. "4–6h"), set the due date at the end of the realistic window.
|
|
75
|
+
|
|
76
|
+
## Parent / child cards (large phases)
|
|
77
|
+
|
|
78
|
+
For phases with >15 tasks (like a large BC):
|
|
79
|
+
|
|
80
|
+
1. Create one **parent card** with the full description and a link to the plan.
|
|
81
|
+
2. Create one **child card per block**, titled `[Phase] — Block [X]: [Block Name]`.
|
|
82
|
+
3. In the parent card description, link to each child card:
|
|
83
|
+
```
|
|
84
|
+
### Sub-cards
|
|
85
|
+
- [Block A — Workspace + VOs](#link)
|
|
86
|
+
- [Block B — Core domain](#link)
|
|
87
|
+
- [Block C — Application](#link)
|
|
88
|
+
```
|
|
89
|
+
4. Add the `epic` or `parent` label to the parent card.
|
|
90
|
+
|
|
91
|
+
## Output format for Trello
|
|
92
|
+
|
|
93
|
+
When outputting for Trello, present the content as:
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
--- TRELLO CARD ---
|
|
97
|
+
|
|
98
|
+
TITLE:
|
|
99
|
+
[title here]
|
|
100
|
+
|
|
101
|
+
DESCRIPTION (paste into card description):
|
|
102
|
+
[description markdown]
|
|
103
|
+
|
|
104
|
+
CHECKLISTS:
|
|
105
|
+
[for each block, list: "Checklist name → items"]
|
|
106
|
+
|
|
107
|
+
LABELS: [comma-separated — names from .trello.json labels[] only]
|
|
108
|
+
|
|
109
|
+
MEMBER: [username, "me", or omit]
|
|
110
|
+
|
|
111
|
+
DUE DATE: [date or relative like "3 days from now"]
|
|
112
|
+
---
|
|
113
|
+
```
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# Trello MCP Configuration
|
|
2
|
+
|
|
3
|
+
The MCP server (`atlassian-trello-mcp`, run via `npx`) is identical across agents. Only the config file location and the JSON/TOML envelope differ. Env vars `TRELLO_API_KEY` + `TRELLO_TOKEN` must be set in the shell that launches the agent — they are inherited by the MCP child process.
|
|
4
|
+
|
|
5
|
+
## MCP setup — Claude Code
|
|
6
|
+
|
|
7
|
+
Project-scoped MCP servers live in `.mcp.json` at the repo root. Register via CLI:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
claude mcp add trello -s project -- npx -y atlassian-trello-mcp
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Then approve via `/mcp` in the chat. Claude Code does NOT interpolate `${env:VAR}` — env vars must be set in the parent shell.
|
|
14
|
+
|
|
15
|
+
## MCP setup — Cursor
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
// .cursor/mcp.json
|
|
19
|
+
{
|
|
20
|
+
"mcpServers": {
|
|
21
|
+
"trello": {
|
|
22
|
+
"command": "npx",
|
|
23
|
+
"args": ["-y", "atlassian-trello-mcp"],
|
|
24
|
+
"env": {
|
|
25
|
+
"TRELLO_API_KEY": "${env:TRELLO_API_KEY}",
|
|
26
|
+
"TRELLO_TOKEN": "${env:TRELLO_TOKEN}"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Cursor supports `${env:VAR}` interpolation.
|
|
34
|
+
|
|
35
|
+
## MCP setup — Codex
|
|
36
|
+
|
|
37
|
+
Codex reads MCP servers from `~/.codex/config.toml` (TOML, not JSON):
|
|
38
|
+
|
|
39
|
+
```toml
|
|
40
|
+
[mcp_servers.trello]
|
|
41
|
+
command = "npx"
|
|
42
|
+
args = ["-y", "atlassian-trello-mcp"]
|
|
43
|
+
|
|
44
|
+
[mcp_servers.trello.env]
|
|
45
|
+
TRELLO_API_KEY = "<your-key>"
|
|
46
|
+
TRELLO_TOKEN = "<your-token>"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
For env-var indirection, prefer storing the actual values in your OS keychain and templating them in via your shell init.
|
|
50
|
+
|
|
51
|
+
## MCP setup — Windsurf
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
// ~/.codeium/windsurf/mcp_config.json
|
|
55
|
+
{
|
|
56
|
+
"mcpServers": {
|
|
57
|
+
"trello": {
|
|
58
|
+
"command": "npx",
|
|
59
|
+
"args": ["-y", "atlassian-trello-mcp"],
|
|
60
|
+
"env": {
|
|
61
|
+
"TRELLO_API_KEY": "${env:TRELLO_API_KEY}",
|
|
62
|
+
"TRELLO_TOKEN": "${env:TRELLO_TOKEN}"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Same shape as Cursor; supports `${env:VAR}` interpolation.
|
|
70
|
+
|
|
71
|
+
## MCP setup — Cline and Roo Code
|
|
72
|
+
|
|
73
|
+
Both read from a JSON settings file under the VS Code user-data dir (Cline: `cline_mcp_settings.json`; Roo Code: `roo_code_mcp_settings.json` or `mcp_settings.json` depending on version). Same envelope as Cursor:
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"mcpServers": {
|
|
78
|
+
"trello": {
|
|
79
|
+
"command": "npx",
|
|
80
|
+
"args": ["-y", "atlassian-trello-mcp"],
|
|
81
|
+
"env": {
|
|
82
|
+
"TRELLO_API_KEY": "<your-key>",
|
|
83
|
+
"TRELLO_TOKEN": "<your-token>"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## MCP setup — other MCP-capable agents
|
|
91
|
+
|
|
92
|
+
The server is constant: `npx -y atlassian-trello-mcp` with env vars `TRELLO_API_KEY` + `TRELLO_TOKEN`. Consult your agent's MCP docs for the config file location and exact JSON/TOML envelope. The server name (`atlassian-trello-mcp`) and required env vars do not change.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Board config (`.trello.json`)
|
|
97
|
+
|
|
98
|
+
The adapter expects Trello metadata at the **repository root** in `.trello.json` (or `~/.config/llodev/pm-tasks/trello.json` as a global fallback). **Read the full file** before Phase 4 (formatting) and Phase 5 (publish).
|
|
99
|
+
|
|
100
|
+
The authoritative schema is [`../schemas/config.json`](../schemas/config.json) (JSON Schema 2020-12). The shape below mirrors that schema field-for-field.
|
|
101
|
+
|
|
102
|
+
### Top-level keys
|
|
103
|
+
|
|
104
|
+
| Key | Required | Purpose |
|
|
105
|
+
| -------------- | -------- | ------------------------------------------------------------------------------------------------ |
|
|
106
|
+
| `$schema` | optional | URL pointer for editor IDE hover validation. |
|
|
107
|
+
| `version` | **yes** | Const `"1"` — schema version of this file. |
|
|
108
|
+
| `workspace` | optional | Free-text workspace label (informational, not used by the API). |
|
|
109
|
+
| `boards[]` | **yes** | Catalog of accessible boards: `{ id, name?, alias, url? }`. |
|
|
110
|
+
| `lists[]` | **yes** | Catalog of board columns: `{ boardAlias, id, name?, alias }`. |
|
|
111
|
+
| `labels[]` | optional | Catalog of named labels: `{ boardAlias, id, name?, color?, alias }`. |
|
|
112
|
+
| `members[]` | optional | Catalog of board members: `{ id, username?, fullName?, alias }`. **Do NOT include a personal "me" entry** committed to git — resolve at runtime (see below). |
|
|
113
|
+
| `defaults` | optional | `{ boardAlias?, listAlias?, closeListAlias? }` — preferred defaults when the user omits them. |
|
|
114
|
+
| `taskAliases[]`| optional | Stable aliases for individual cards: `{ alias, id, url? }`. |
|
|
115
|
+
| `autonomous` | optional | Autonomous-mode envelope (see [`./autonomous.md`](./autonomous.md) for full spec). |
|
|
116
|
+
|
|
117
|
+
`alias` everywhere is `^[a-z0-9-]+$` — used by the agent as a stable, human-friendly key. `id` is the 24-char Trello object ID returned by the API.
|
|
118
|
+
|
|
119
|
+
### Example (matches the schema)
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"$schema": "https://llodev.github.io/skills/schemas/pm-tasks-trello.json",
|
|
124
|
+
"version": "1",
|
|
125
|
+
"workspace": "llodev",
|
|
126
|
+
"boards": [
|
|
127
|
+
{ "id": "6a2b574aefe6fe9621a3d5a7", "name": "Skills", "alias": "skills", "url": "https://trello.com/b/bR5bbtoH" }
|
|
128
|
+
],
|
|
129
|
+
"lists": [
|
|
130
|
+
{ "boardAlias": "skills", "id": "6a2b57600f0f20cbea2277f0", "name": "Backlog", "alias": "backlog" },
|
|
131
|
+
{ "boardAlias": "skills", "id": "6a2b576292aee4b8eeb82ed1", "name": "WIP", "alias": "wip" },
|
|
132
|
+
{ "boardAlias": "skills", "id": "6a2b5767058f005c9c063a8b", "name": "Done", "alias": "done" }
|
|
133
|
+
],
|
|
134
|
+
"labels": [
|
|
135
|
+
{ "boardAlias": "skills", "id": "6a2b574aefe6fe9621a3d5d2", "name": "core", "color": "green", "alias": "core" },
|
|
136
|
+
{ "boardAlias": "skills", "id": "6a2b574aefe6fe9621a3d5d7", "name": "adapter", "color": "blue", "alias": "adapter" }
|
|
137
|
+
],
|
|
138
|
+
"members": [
|
|
139
|
+
{ "id": "67d978bb...", "username": "alice", "fullName": "Alice Example", "alias": "alice" }
|
|
140
|
+
],
|
|
141
|
+
"defaults": {
|
|
142
|
+
"boardAlias": "skills",
|
|
143
|
+
"listAlias": "backlog",
|
|
144
|
+
"closeListAlias": "done"
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Secrets (`TRELLO_API_KEY`, `TRELLO_TOKEN`) NEVER live in this file. Use env vars or your OS keychain.
|
|
150
|
+
|
|
151
|
+
### Resolving "me" at runtime (NOT in committed config)
|
|
152
|
+
|
|
153
|
+
`me` is **whoever owns the Trello token in the environment** (`TRELLO_TOKEN`), not a fixed user in git.
|
|
154
|
+
|
|
155
|
+
At Phase **5.2.5**, call `trello_get_member` with `memberId: "me"` and use the returned `id` for `idMembers`. **Do not** add the current user to `members[]` in the committed file — each developer's token resolves to themselves.
|
|
156
|
+
|
|
157
|
+
Optional: if the resolved `id` is not in `members[]` (not on the board), **warn** in the preview but still allow assign if the API accepts it; otherwise **stop** and ask to invite the member to the board.
|
|
158
|
+
|
|
159
|
+
To assign someone else by default, omit it from the config and let the user pass it explicitly in the prompt, OR add an explicit member alias to `members[]` and reference it in the prompt by alias.
|
|
160
|
+
|
|
161
|
+
### List resolution
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
listAlias = userProvided ?? defaults.listAlias
|
|
165
|
+
listId = lists.find(l => l.alias === listAlias && l.boardAlias === boardAlias)?.id
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
If `listAlias` is provided but not in `lists[]`, **stop** and list valid aliases — do not invent list IDs.
|
|
169
|
+
|
|
170
|
+
### Board resolution
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
boardAlias = userProvided ?? defaults.boardAlias
|
|
174
|
+
boardId = boards.find(b => b.alias === boardAlias)?.id
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
If the user picks a board not in `boards[]`, refresh via MCP (`trello_get_user_boards`) and offer to merge into `.trello.json`.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## MCP config discovery (Phase 5.1)
|
|
182
|
+
|
|
183
|
+
Read `.trello.json` at the **project root** (full file, no range limits) before publish.
|
|
184
|
+
|
|
185
|
+
**If `.trello.json` is missing or has no `boards[]`:**
|
|
186
|
+
|
|
187
|
+
1. `trello_get_user_boards` (`filter: "open"`) — present board names, ask which board(s).
|
|
188
|
+
2. `get_lists` with each `boardId` — present list names; offer Backlog/WIP/Done as defaults if present.
|
|
189
|
+
3. `trello_get_board_labels` — merge **named** labels only into `labels[]` (skip `name: ""` stubs).
|
|
190
|
+
4. `trello_get_board_members` — fill `members[]` only with the board roster (NOT the active token user).
|
|
191
|
+
5. Offer to write `.trello.json` with resolved IDs — **never** add the active token's user to `members[]`.
|
|
192
|
+
|
|
193
|
+
**Never ask the user for 24-char IDs directly** — always resolve from names via MCP.
|
|
194
|
+
|
|
195
|
+
**Refreshing a stale config** when the board changed in the Trello UI: re-run the discovery tools above for the active board and offer to merge updates into `.trello.json` (preserve `defaults` choices unless the user asks to switch).
|
|
196
|
+
|
|
197
|
+
**MCP server:** `atlassian-trello-mcp` (see setup sections above); env `TRELLO_API_KEY` + `TRELLO_TOKEN`.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Resolve labels and member (Phase 5.2.5)
|
|
202
|
+
|
|
203
|
+
**Run before** the preview (Phase 5.2) so resolved values appear in the confirmation block.
|
|
204
|
+
|
|
205
|
+
1. Start with the labels mentioned in the generic card's **LABELS** block.
|
|
206
|
+
2. For each label name → look up `labels[].id` by matching `label.name` (exact) or `label.alias` against the entry in the card.
|
|
207
|
+
3. Build `idLabels: string[]` (dedupe).
|
|
208
|
+
4. Resolve member:
|
|
209
|
+
- `"me"` → `trello_get_member({ memberId: "me" })` → use returned `id` (and show `fullName` / `username` in preview).
|
|
210
|
+
- Explicit `alias` from `members[]` → use that entry's `id`.
|
|
211
|
+
- Omitted → no `idMembers`.
|
|
212
|
+
- User override in the preview wins.
|
|
213
|
+
5. Emit **LABELS / MEMBER (resolved)** in the conversational wrapper — not inside the paste-only card body.
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
LABELS (resolved):
|
|
217
|
+
core → 6a2b574aefe6fe9621a3d5d2
|
|
218
|
+
adapter → 6a2b574aefe6fe9621a3d5d7
|
|
219
|
+
MEMBER (resolved):
|
|
220
|
+
Alice Example (me, current token) → 67d978bb...
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
If a label is mentioned but absent from `labels[]` → **do not** add to `idLabels`; list it under **omitted** (optional: offer `trello_create_label` only on explicit user request).
|
|
224
|
+
|
|
225
|
+
If a member alias is referenced but not in `members[]` → **stop**; list valid aliases.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Trello CRUD operations (v1)
|
|
2
|
+
|
|
3
|
+
Maps each verb from [`pm-tasks/pm-tasks-core/references/crud-vocabulary.md`](../../pm-tasks-core/references/crud-vocabulary.md) to `atlassian-trello-mcp` tool calls.
|
|
4
|
+
|
|
5
|
+
## Verb → MCP tool
|
|
6
|
+
|
|
7
|
+
| Verb | MCP tool | Params (key ones) |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `task.create` | `create_card` | `listId`, `name`, `desc`, `pos`, `due`, `labelIds[]`, `memberIds[]`, optional checklist via follow-up `trello_create_checklist` + `trello_create_check_item` |
|
|
10
|
+
| `checklist.check` | `trello_update_check_item` | `cardId`, `checkItemId`, `state: "complete"` |
|
|
11
|
+
| `task.close` | `move_card` | `cardId`, `listId` = config `defaults.closeListAlias` resolved |
|
|
12
|
+
| `task.due-date.set` | `update_card` | `cardId`, `due` (ISO 8601 or `null`) |
|
|
13
|
+
| `task.assignee.add` | `trello_add_member_to_card` | `cardId`, `memberId` |
|
|
14
|
+
| `task.comment.add` | `trello_add_comment` | `cardId`, `text` (prefixed with `[ct:<clientToken>]` if provided) |
|
|
15
|
+
|
|
16
|
+
## `<task-ref>` resolution for Trello
|
|
17
|
+
|
|
18
|
+
Implementation steps in adapter's runtime logic:
|
|
19
|
+
|
|
20
|
+
1. **URL match** — pattern `^https?://trello\.com/c/([A-Za-z0-9]+)`. Group 1 is the short-link, usable as `cardId` in all MCP calls.
|
|
21
|
+
2. **Native ID** — 24-char hex (e.g. `6a2b574aefe6fe9621a3d5a7`) → use as-is.
|
|
22
|
+
3. **`taskAliases` lookup** — match `alias` in config, resolve to `id`/`url`.
|
|
23
|
+
4. **clientToken match (audit log)** — newest entry with matching `clientToken` in `~/.local/share/llodev/pm-tasks/trello/audit.log`.
|
|
24
|
+
5. **Name partial (audit log)** — case-insensitive substring on `name`, newest first, filter by `scope.boards`.
|
|
25
|
+
6. **Otherwise** → `{ ok: false, code: "REF_NOT_RESOLVED", candidates: [list of last 5 created in scope] }`.
|
|
26
|
+
|
|
27
|
+
## Idempotency
|
|
28
|
+
|
|
29
|
+
- `task.create` — checks card description for `[ct:<token>]` marker via `trello_get_list_cards` on target list.
|
|
30
|
+
- `checklist.check` — fetches checkItem state via `trello_get_check_item` first; skips MCP call if already `complete`.
|
|
31
|
+
- `task.close` — fetches current list via `get_card`; skips if already in `closeListAlias`.
|
|
32
|
+
- `task.due-date.set` — fetches `due` via `get_card`; skips if equal.
|
|
33
|
+
- `task.assignee.add` — fetches `members` of card via `get_card`; skips if memberId already in list.
|
|
34
|
+
- `task.comment.add` — fetches last 20 comments via `trello_get_card_actions`; skips if any starts with `[ct:<token>]`.
|
|
35
|
+
|
|
36
|
+
## Result envelope (Trello-specific `details`)
|
|
37
|
+
|
|
38
|
+
| Verb | `details` fields |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `task.create` | `{ shortLink, dateLastActivity, checklists: [{id,name,items: [{id,name}]}] }` |
|
|
41
|
+
| `checklist.check` | `{ checklistId, checkItemId }` |
|
|
42
|
+
| `task.close` | `{ previousListId, newListId }` |
|
|
43
|
+
| `task.due-date.set` | `{ previousDue, newDue }` |
|
|
44
|
+
| `task.assignee.add` | `{ memberId, username }` |
|
|
45
|
+
| `task.comment.add` | `{ commentId }` |
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Trello Publish Sequence
|
|
2
|
+
|
|
3
|
+
## Preview and approval (Phase 5.2)
|
|
4
|
+
|
|
5
|
+
Before any write MCP call, show:
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Ready to create on Trello:
|
|
9
|
+
|
|
10
|
+
Board : [boardName]
|
|
11
|
+
List : [listName]
|
|
12
|
+
Title : [card title]
|
|
13
|
+
Checklists:
|
|
14
|
+
Pre-flight (N items)
|
|
15
|
+
Block A — [name] (N items)
|
|
16
|
+
...
|
|
17
|
+
Verification (N items)
|
|
18
|
+
Labels: [names] → IDs: [idLabels summary or count]
|
|
19
|
+
Member: [fullName] ([username]) → [memberId]
|
|
20
|
+
Due date: [date or omitted]
|
|
21
|
+
|
|
22
|
+
Create now? [y] confirm · [n] cancel · [e] edit title/list/member/labels first
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- **y / confirm** → proceed to publish sequence
|
|
26
|
+
- **n / cancel** → stop; paste-ready output remains the artifact
|
|
27
|
+
- **e / edit** → adjust title/board/list/due date/member/labels, show preview again
|
|
28
|
+
|
|
29
|
+
**NEVER** call write MCP tools (`create_card`, `trello_create_checklist`, etc.) before confirmation.
|
|
30
|
+
|
|
31
|
+
When **Phase 5** runs (MCP create), add after § Resolve labels and member and **before** § Preview:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
CREATE_CARD (API):
|
|
35
|
+
idList : [listId]
|
|
36
|
+
idLabels : ["...", ...] (omit key if empty)
|
|
37
|
+
idMembers : ["..."] (omit key if unassigned)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Publish via MCP (Phase 5)
|
|
43
|
+
|
|
44
|
+
Invoked from [`../SKILL.md`](../SKILL.md) Phase 5 after § **Preview and approval** confirmation and § **Resolve labels and member** complete. **NEVER** call write MCP tools before confirmation.
|
|
45
|
+
|
|
46
|
+
**Description body:** use [`pm-tasks/pm-tasks-core/references/generic-card.md`](../../pm-tasks-core/references/generic-card.md) **Description** block **without** Out of scope / Next step (see [`../anti-patterns/tools.md`](../anti-patterns/tools.md)).
|
|
47
|
+
|
|
48
|
+
### Publish sequence
|
|
49
|
+
|
|
50
|
+
Execute in order; stop and report on first failure.
|
|
51
|
+
|
|
52
|
+
**Step 1 — Create the card**
|
|
53
|
+
|
|
54
|
+
Call `create_card` with:
|
|
55
|
+
|
|
56
|
+
- `name` = card title (from Phase 4)
|
|
57
|
+
- `desc` = card description (from Phase 4, Markdown; no tables — flatten per § Tables vs paste)
|
|
58
|
+
- `idList` = resolved `listId` from § List resolution
|
|
59
|
+
- `due` = due date if set (ISO 8601), otherwise omit
|
|
60
|
+
- `idLabels` = array from Phase 5.2.5 (omit key if empty)
|
|
61
|
+
- `idMembers` = `[memberId]` when assigned (omit key if none)
|
|
62
|
+
|
|
63
|
+
Capture returned `id` / `shortUrl` as `cardId`.
|
|
64
|
+
|
|
65
|
+
Prefer **assigning labels and member at create** — fewer API calls and atomic card state. Use `trello_add_label_to_card` / `trello_add_member_to_card` only when adding after create (e.g. user amended labels in **e** flow).
|
|
66
|
+
|
|
67
|
+
**Step 2 — Create checklists**
|
|
68
|
+
|
|
69
|
+
For each named checklist block (Pre-flight, one per implementation block, Verification):
|
|
70
|
+
|
|
71
|
+
1. `trello_create_checklist`: `{ cardId, name: "Pre-flight" }` → capture `checklistId`
|
|
72
|
+
2. Per item: `trello_create_check_item`: `{ checklistId, name: "plain text, no markdown" }`
|
|
73
|
+
|
|
74
|
+
Order: Pre-flight → implementation blocks in plan order → Verification last.
|
|
75
|
+
|
|
76
|
+
**Rate limit:** Trello allows 300 req/10s per key. A card with 8 checklists × 5 items ≈ 50 calls — within limits.
|
|
77
|
+
|
|
78
|
+
**Step 3 — Fallback labels/member (optional)**
|
|
79
|
+
|
|
80
|
+
Only if Step 1 omitted `idLabels` / `idMembers` but Phase 5.2.5 resolved new values after create:
|
|
81
|
+
|
|
82
|
+
- `trello_add_label_to_card` per `labelId`
|
|
83
|
+
- `trello_add_member_to_card` per `memberId`
|
|
84
|
+
|
|
85
|
+
**Step 4 — Confirm**
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
✓ Card created: [shortUrl from create_card]
|
|
89
|
+
Board : [board.name] → [listName]
|
|
90
|
+
Member: [fullName or "unassigned"]
|
|
91
|
+
Checklists: N created, M items total
|
|
92
|
+
Labels: [names applied] (omitted: [names with no match in labels[]])
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Error handling
|
|
96
|
+
|
|
97
|
+
| Failure | Action |
|
|
98
|
+
| ------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
99
|
+
| MCP not connected | Abort Phase 5; deliver paste-ready output (Phase 4); check MCP server config and `TRELLO_*` env vars |
|
|
100
|
+
| `boardId` / `listId` not resolved | Re-run § **MCP config discovery**; do not guess IDs |
|
|
101
|
+
| Label name not in `.trello.json` | Skip that label; continue; report under **omitted**; do not auto-create labels |
|
|
102
|
+
| Member not in `members[]` | Stop before create; list valid usernames |
|
|
103
|
+
| Card creation fails | Abort; report error; no checklists created |
|
|
104
|
+
| Checklist creation fails mid-sequence | Report card URL; which checklists succeeded vs "add manually" |
|
|
105
|
+
| Label/member fallback fails | Report card URL; list what was applied at create vs failed fallback |
|
|
106
|
+
|
|
107
|
+
**NEVER** leave a partial card without reporting what was and wasn't created.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://llodev.github.io/skills/schemas/pm-tasks-trello.json",
|
|
4
|
+
"title": "pm-tasks-trello config",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["version", "boards", "lists"],
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"$schema": { "type": "string" },
|
|
10
|
+
"version": { "const": "1" },
|
|
11
|
+
"workspace": { "type": "string" },
|
|
12
|
+
"boards": {
|
|
13
|
+
"type": "array",
|
|
14
|
+
"minItems": 1,
|
|
15
|
+
"items": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"required": ["id", "alias"],
|
|
18
|
+
"additionalProperties": false,
|
|
19
|
+
"properties": {
|
|
20
|
+
"id": { "type": "string", "minLength": 8 },
|
|
21
|
+
"name": { "type": "string" },
|
|
22
|
+
"alias": { "type": "string", "pattern": "^[a-z0-9-]+$" },
|
|
23
|
+
"url": { "type": "string", "format": "uri" }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"lists": {
|
|
28
|
+
"type": "array",
|
|
29
|
+
"items": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"required": ["boardAlias", "id", "alias"],
|
|
32
|
+
"additionalProperties": false,
|
|
33
|
+
"properties": {
|
|
34
|
+
"boardAlias": { "type": "string" },
|
|
35
|
+
"id": { "type": "string", "minLength": 8 },
|
|
36
|
+
"name": { "type": "string" },
|
|
37
|
+
"alias": { "type": "string", "pattern": "^[a-z0-9-]+$" }
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"labels": {
|
|
42
|
+
"type": "array",
|
|
43
|
+
"items": {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"required": ["boardAlias", "id", "alias"],
|
|
46
|
+
"additionalProperties": false,
|
|
47
|
+
"properties": {
|
|
48
|
+
"boardAlias": { "type": "string" },
|
|
49
|
+
"id": { "type": "string", "minLength": 8 },
|
|
50
|
+
"name": { "type": "string" },
|
|
51
|
+
"color": { "type": "string" },
|
|
52
|
+
"alias": { "type": "string", "pattern": "^[a-z0-9-]+$" }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"members": {
|
|
57
|
+
"type": "array",
|
|
58
|
+
"items": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"required": ["id", "alias"],
|
|
61
|
+
"additionalProperties": false,
|
|
62
|
+
"properties": {
|
|
63
|
+
"id": { "type": "string", "minLength": 8 },
|
|
64
|
+
"username": { "type": "string" },
|
|
65
|
+
"fullName": { "type": "string" },
|
|
66
|
+
"alias": { "type": "string", "pattern": "^[a-z0-9-]+$" }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"defaults": {
|
|
71
|
+
"type": "object",
|
|
72
|
+
"additionalProperties": false,
|
|
73
|
+
"properties": {
|
|
74
|
+
"boardAlias": { "type": "string" },
|
|
75
|
+
"listAlias": { "type": "string" },
|
|
76
|
+
"closeListAlias": { "type": "string" }
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"taskAliases": {
|
|
80
|
+
"type": "array",
|
|
81
|
+
"items": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"required": ["alias", "id"],
|
|
84
|
+
"additionalProperties": false,
|
|
85
|
+
"properties": {
|
|
86
|
+
"alias": { "type": "string", "pattern": "^[a-z0-9-]+$" },
|
|
87
|
+
"id": { "type": "string" },
|
|
88
|
+
"url": { "type": "string", "format": "uri" }
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"autonomous": {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"additionalProperties": false,
|
|
95
|
+
"properties": {
|
|
96
|
+
"enabled": { "type": "boolean" },
|
|
97
|
+
"allow": {
|
|
98
|
+
"type": "array",
|
|
99
|
+
"items": {
|
|
100
|
+
"enum": [
|
|
101
|
+
"task.create",
|
|
102
|
+
"checklist.check",
|
|
103
|
+
"task.close",
|
|
104
|
+
"task.due-date.set",
|
|
105
|
+
"task.assignee.add",
|
|
106
|
+
"task.comment.add"
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"scope": {
|
|
111
|
+
"type": "object",
|
|
112
|
+
"additionalProperties": false,
|
|
113
|
+
"properties": {
|
|
114
|
+
"boards": { "type": "array", "items": { "type": "string" } },
|
|
115
|
+
"lists": { "type": "array", "items": { "type": "string" } }
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"rateLimit": {
|
|
119
|
+
"type": "object",
|
|
120
|
+
"additionalProperties": false,
|
|
121
|
+
"properties": {
|
|
122
|
+
"writesPerMinute": { "type": "integer", "minimum": 1, "maximum": 600 },
|
|
123
|
+
"commentsPerMinute": { "type": "integer", "minimum": 1, "maximum": 600 }
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"auditLog": { "type": "string" }
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
package/scripts/init.mjs
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// pm-tasks-trello init — interactive config bootstrapper
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import {
|
|
7
|
+
promptScope,
|
|
8
|
+
promptYesNo,
|
|
9
|
+
multiSelect,
|
|
10
|
+
writeConfig,
|
|
11
|
+
validateConfig,
|
|
12
|
+
probeMCP,
|
|
13
|
+
printInstructions,
|
|
14
|
+
} from "@llodev/pm-tasks-core/init-lib";
|
|
15
|
+
|
|
16
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
17
|
+
|
|
18
|
+
async function loadSchema() {
|
|
19
|
+
const raw = await readFile(path.join(ROOT, "schemas", "config.json"), "utf8");
|
|
20
|
+
return JSON.parse(raw);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function trelloProbe() {
|
|
24
|
+
// The MCP runs in a different process. To probe within this script, call the
|
|
25
|
+
// Trello REST API directly with env vars TRELLO_API_KEY + TRELLO_TOKEN.
|
|
26
|
+
// We use this only to enumerate boards/lists/labels/members for the user.
|
|
27
|
+
const { TRELLO_API_KEY: KEY, TRELLO_TOKEN: TOKEN } = process.env;
|
|
28
|
+
if (!KEY || !TOKEN) throw new Error("auth: TRELLO_API_KEY or TRELLO_TOKEN missing");
|
|
29
|
+
const url = (p) => `https://api.trello.com/1${p}?key=${KEY}&token=${TOKEN}`;
|
|
30
|
+
const j = async (p) => {
|
|
31
|
+
const r = await fetch(url(p));
|
|
32
|
+
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
|
33
|
+
return r.json();
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
getBoards: () => j("/members/me/boards?filter=open&fields=id,name,url"),
|
|
37
|
+
getLists: (boardId) => j(`/boards/${boardId}/lists?filter=open&fields=id,name`),
|
|
38
|
+
getLabels: (boardId) => j(`/boards/${boardId}/labels?fields=id,name,color`),
|
|
39
|
+
getMembers: (boardId) => j(`/boards/${boardId}/members?fields=id,username,fullName`),
|
|
40
|
+
getMe: () => j("/members/me?fields=id,username,fullName"),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function aliasOf(name) {
|
|
45
|
+
return name
|
|
46
|
+
.toLowerCase()
|
|
47
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
48
|
+
.replace(/^-|-$/g, "");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function run() {
|
|
52
|
+
console.log("\n@llodev/pm-tasks-trello init\n");
|
|
53
|
+
|
|
54
|
+
const { path: outPath } = await promptScope("trello");
|
|
55
|
+
|
|
56
|
+
const probe = await probeMCP({
|
|
57
|
+
tool: "trello",
|
|
58
|
+
probeCommand: async () => {
|
|
59
|
+
const api = await trelloProbe();
|
|
60
|
+
await api.getMe();
|
|
61
|
+
return api;
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (probe.unauthenticated) {
|
|
66
|
+
printInstructions([
|
|
67
|
+
"Trello MCP detected, but credentials missing.",
|
|
68
|
+
"Set them in your shell and re-run:",
|
|
69
|
+
" export TRELLO_API_KEY=...",
|
|
70
|
+
" export TRELLO_TOKEN=...",
|
|
71
|
+
]);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!probe.mcpAvailable) {
|
|
76
|
+
printInstructions([
|
|
77
|
+
"Trello MCP not available. Configure it first:",
|
|
78
|
+
" claude mcp add trello -s project -- npx -y atlassian-trello-mcp",
|
|
79
|
+
"See references/mcp-config.md for details. Re-run this init afterward.",
|
|
80
|
+
]);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const api = probe.result;
|
|
85
|
+
const me = await api.getMe();
|
|
86
|
+
const boards = await api.getBoards();
|
|
87
|
+
|
|
88
|
+
const pickedBoards = await multiSelect(
|
|
89
|
+
"Available boards (select 1+):",
|
|
90
|
+
boards.map((b) => ({ label: `${b.name} (${b.id})`, value: b })),
|
|
91
|
+
);
|
|
92
|
+
if (!pickedBoards.length) {
|
|
93
|
+
console.error("no board selected, aborting");
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const out = {
|
|
98
|
+
$schema: "https://llodev.github.io/skills/schemas/pm-tasks-trello.json",
|
|
99
|
+
version: "1",
|
|
100
|
+
boards: [],
|
|
101
|
+
lists: [],
|
|
102
|
+
labels: [],
|
|
103
|
+
members: [{ id: me.id, username: me.username, fullName: me.fullName, alias: "me" }],
|
|
104
|
+
defaults: {},
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
for (const b of pickedBoards) {
|
|
108
|
+
const alias = aliasOf(b.name);
|
|
109
|
+
out.boards.push({ id: b.id, name: b.name, alias, url: b.url });
|
|
110
|
+
const lists = await api.getLists(b.id);
|
|
111
|
+
const pickedLists = await multiSelect(
|
|
112
|
+
`Lists in "${b.name}":`,
|
|
113
|
+
lists.map((l) => ({ label: l.name, value: l })),
|
|
114
|
+
);
|
|
115
|
+
for (const l of pickedLists) {
|
|
116
|
+
out.lists.push({ boardAlias: alias, id: l.id, name: l.name, alias: aliasOf(l.name) });
|
|
117
|
+
}
|
|
118
|
+
const labels = (await api.getLabels(b.id)).filter((x) => x.name);
|
|
119
|
+
for (const l of labels) {
|
|
120
|
+
out.labels.push({
|
|
121
|
+
boardAlias: alias,
|
|
122
|
+
id: l.id,
|
|
123
|
+
name: l.name,
|
|
124
|
+
color: l.color,
|
|
125
|
+
alias: aliasOf(l.name),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (out.boards.length === 1) {
|
|
131
|
+
out.defaults.boardAlias = out.boards[0].alias;
|
|
132
|
+
const backlog = out.lists.find((l) => /backlog|todo|to.do/i.test(l.name));
|
|
133
|
+
const done = out.lists.find((l) => /done|published|concluído/i.test(l.name));
|
|
134
|
+
if (backlog) out.defaults.listAlias = backlog.alias;
|
|
135
|
+
if (done) out.defaults.closeListAlias = done.alias;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const wantAuto = await promptYesNo(
|
|
139
|
+
"Enable autonomous mode? (adds an autonomous block with conservative defaults)",
|
|
140
|
+
);
|
|
141
|
+
if (wantAuto) {
|
|
142
|
+
out.autonomous = {
|
|
143
|
+
enabled: false,
|
|
144
|
+
allow: ["task.create", "checklist.check", "task.close", "task.comment.add"],
|
|
145
|
+
scope: { boards: out.boards.map((b) => b.id), lists: out.lists.map((l) => l.id) },
|
|
146
|
+
rateLimit: { writesPerMinute: 30, commentsPerMinute: 10 },
|
|
147
|
+
auditLog: "~/.local/share/llodev/pm-tasks/trello/audit.log",
|
|
148
|
+
};
|
|
149
|
+
printInstructions([
|
|
150
|
+
"autonomous block added with enabled:false.",
|
|
151
|
+
"Review scope.boards and scope.lists in the JSON before enabling.",
|
|
152
|
+
]);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const schema = await loadSchema();
|
|
156
|
+
const valid = await validateConfig(out, schema);
|
|
157
|
+
if (!valid.ok) {
|
|
158
|
+
console.error("invalid config:", JSON.stringify(valid.errors, null, 2));
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
await writeConfig(outPath, out);
|
|
163
|
+
printInstructions([
|
|
164
|
+
`Config written to ${outPath}.`,
|
|
165
|
+
"Try it in Claude Code: 'create a Trello card from this plan'.",
|
|
166
|
+
"Reminder: secrets belong in env vars or OS keychain — never in this JSON.",
|
|
167
|
+
]);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
171
|
+
run().catch((e) => {
|
|
172
|
+
console.error(e);
|
|
173
|
+
process.exit(1);
|
|
174
|
+
});
|
|
175
|
+
}
|