@llodev/pm-tasks-asana 0.0.0-pr-22-9002a06

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 ADDED
@@ -0,0 +1,282 @@
1
+ # @llodev/pm-tasks-asana
2
+
3
+ ## 1.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#20](https://github.com/llodev/skills/pull/20) [`78106ab`](https://github.com/llodev/skills/commit/78106ab42ed317ba865906d25701ade4532396ec) Thanks [@lloliveiradev](https://github.com/lloliveiradev)! - **pm-tasks v1.9.0 — Headless runtime + Plan-execution mode (Option E)**
8
+
9
+ Three coordinated minor bumps shipping the agent-agnostic headless runtime
10
+ and the F15 plan-execution mode. Skill-driven flows are unchanged; this
11
+ release adds a programmatic entry point and a new mode the calling agent
12
+ can opt into when it has a plan file to execute.
13
+
14
+ **`@llodev/pm-tasks-core@1.8.0 → 1.9.0`** (minor)
15
+
16
+ - New: `@llodev/pm-tasks-core/runtime` subpath exposes `createCoreRuntime`,
17
+ the `Transport` interface, 7 verb handlers, `RuntimeContext`, and the
18
+ full set of request/response types. Pure: no MCP imports, no transport
19
+ implementation — adapters provide the wiring.
20
+ - New: `@llodev/pm-tasks-core/plan-execution` module exports
21
+ `requireConfig` + `ConfigRequiredError`, `discoverPlanTasks` +
22
+ `resolvePlanRef` + `parseH3Titles` + `filenameToSlug` (discovery
23
+ helpers — `PlanRef` accepts `string[]` titles, a `.md` path with H3
24
+ parsing, or a bare slug), `onTaskStart` + `onTaskComplete` (boundary
25
+ hooks with best-effort dispatch, `ALREADY_IN_STATE` → skipped
26
+ classification, process-local idempotency memo keyed by
27
+ `${taskId}|${commitSha}`), and `__resetHookCacheForTests` (test escape
28
+ hatch).
29
+ - New: `pm-tasks-core/references/plan-execution.md` documents the full
30
+ contract (triggers, discovery semantics, hook classification table,
31
+ failure modes, `ConfigRequiredError` shape).
32
+ - New: `pm-tasks-core/references/agent-agnostic-lint.md` + a stand-alone
33
+ `scripts/checks/agent-agnostic-lint.mjs` that bans `superpowers`,
34
+ `sdd`, `Claude Code`, `Claude-only`, `Claude assumes` in SKILL body
35
+ content while allowlisting `claude-code` in `compatibility.agents`
36
+ frontmatter, vendor product names (`claude.ai Asana`,
37
+ `claude-ai-asana-mcp`), and other context-aware exemptions.
38
+ - Internal: tarball size budget raised 14.1 → 18.5 kB across Phase 4
39
+ and Phase 5 (covering the plan-execution module + hook helpers + full
40
+ JSDoc on every exported symbol).
41
+
42
+ **`@llodev/pm-tasks-asana@1.5.0 → 1.6.0`** (minor)
43
+
44
+ - New: `@llodev/pm-tasks-asana/adapter` subpath exposes
45
+ `createAdapter({ configPath, mcp, session?, language? })` returning a
46
+ `Runtime`. The `mcp: McpCaller` callback is the caller's only
47
+ obligation — receives a fully-qualified `mcp__claude_ai_Asana__*` tool
48
+ name + args object, returns the MCP server's raw response.
49
+ - New: `pm-tasks-asana/src/transport-asana.ts` implements the
50
+ `Transport` interface against `mcp__claude_ai_Asana__*` tools.
51
+ Asana-specific deltas: `closeListOrSectionId` ignored at the transport
52
+ layer (Asana has no list-on-close concept), ISO-8601 → `YYYY-MM-DD`
53
+ conversion via `isoToDueOn` helper for `taskDueDateSet`,
54
+ `INVALID_REQUEST` short-circuit BEFORE the MCP call on malformed
55
+ `dueAt`, single-assignee model for `taskAssigneeAdd`.
56
+ - SKILL.md routing table gained a Plan-execution row; Phase 7 narrative
57
+ section forward-references the new `references/plan-execution.md`
58
+ doc in `pm-tasks-core`.
59
+
60
+ **`@llodev/pm-tasks-trello@1.5.0 → 1.6.0`** (minor)
61
+
62
+ - New: `@llodev/pm-tasks-trello/adapter` subpath exposes
63
+ `createAdapter({ configPath, mcp, session?, language? })` returning a
64
+ `Runtime`. Same shape and contract as the Asana adapter.
65
+ - New: `pm-tasks-trello/src/transport-trello.ts` implements the
66
+ `Transport` interface against `mcp__trello__*` tools. Trello-specific
67
+ behavior: `taskClose` archives the card AND optionally moves it to
68
+ `closeListOrSectionId` when provided (Trello's "Done" list pattern).
69
+ - SKILL.md routing table gained a Plan-execution row mirroring the
70
+ Asana adapter; identical Phase 7 narrative section.
71
+
72
+ **Skill-judge gate**
73
+
74
+ Modified `pm-tasks-asana/SKILL.md` and `pm-tasks-trello/SKILL.md`
75
+ (Phase 4.3) added the Plan-execution mode routing row + Phase 7 section.
76
+ Measured drift sits within the documented noise band ([-2, +2]); the
77
+ agent-agnostic-lint rule shipped in this release codifies the
78
+ allowlist that earlier scoring assumed. If drift is within tolerance,
79
+ bypass with `SKIP_SKILL_JUDGE_GATE=1 make release-version`.
80
+
81
+ **Breaking changes**
82
+
83
+ None. All Phase 1-5 additions are pure surface area additions:
84
+
85
+ - `/runtime` and `/adapter` subpaths are NEW exports (no prior consumers)
86
+ - `/plan-execution` helpers are NEW exports
87
+ - Existing skill-driven flows + the 7-verb CRUD contract on the autonomous
88
+ path are unchanged
89
+
90
+ **Migration**
91
+
92
+ No migration needed. Existing consumers continue to import from the
93
+ package root. New code can opt into the runtime by importing from
94
+ `@llodev/pm-tasks-{trello,asana}/adapter` or the helpers from
95
+ `@llodev/pm-tasks-core/runtime` and `@llodev/pm-tasks-core/plan-execution`.
96
+
97
+ ### Patch Changes
98
+
99
+ - Updated dependencies [[`78106ab`](https://github.com/llodev/skills/commit/78106ab42ed317ba865906d25701ade4532396ec)]:
100
+ - @llodev/pm-tasks-core@1.9.0
101
+
102
+ ## 1.5.0
103
+
104
+ ### Minor Changes
105
+
106
+ - 1200b4e: v1.8.0 — Observability v1. pm-tasks-core-doctor CLI validates workspace config / autonomous allowlist / audit writability / (when probes are injected) MCP & network reach BEFORE the first publish attempt fails noisily. Adapter init bins expose `--doctor` for per-tool checks (C-TRL-1..3 + C-ASN-1..3, gated by auth env). Smart audit-log rotation (size + age + multi-tool, atomic, idempotent, gzipped archives, keep-N) replaces the rudimentary shell script; new `rotate-audit.mjs` CLI emits structured JSON status. Pre-release gate now blocks on doctor errors. Closes roadmap §2.4 O1 + O3.
107
+
108
+ ### Patch Changes
109
+
110
+ - Updated dependencies [1200b4e]
111
+ - @llodev/pm-tasks-core@1.8.0
112
+
113
+ ## 1.4.0
114
+
115
+ ### Minor Changes
116
+
117
+ - v1.5.0 — Adapters TypeScript migration, @llodev/pm-tasks-testkit, 7th canonical verb `task.move`, E2E canary.
118
+
119
+ **Core (`@llodev/pm-tasks-core` 1.4.0 → 1.5.0)** — adds the 7th canonical verb `task.move` to the public contract.
120
+
121
+ - New verb `task.move({ cardId, targetList })` formalizes the WIP transition that the autonomous-mode lifecycle has always required but no canonical verb expressed. `targetList` accepts the enum `"open" | "wip" | "done"` plus raw list IDs.
122
+ - `task.move` is INDEPENDENT of `task.close`: move only repositions, close moves AND sets the completion flag. Useful in adapters where the visual transition and the closed-flag are separate operations (Asana section change vs. `completed: true`).
123
+ - Schema regex updated to recognize `task.move` as canonical (no namespace prefix required).
124
+ - `pm-tasks-core/schemas/adapter-manifest.schema.json` + `references/contract.md` + `references/crud-vocabulary.md` + `references/autonomous-mode.md` updated.
125
+ - Contract-check (`scripts/checks/contract-check.mjs`) `CANONICAL_VERBS` Set updated; regression test added.
126
+ - Additive only — existing consumers calling the original 6 verbs continue to work unchanged.
127
+
128
+ **Adapters (`@llodev/pm-tasks-asana` 1.3.1 → 1.4.0, `@llodev/pm-tasks-trello` 1.3.1 → 1.4.0)** — full TypeScript migration mirroring core's v1.4.0 pattern.
129
+
130
+ - Source moved from `scripts/init.mjs` to `src/bin/init.ts` (strict TS).
131
+ - Build pipeline: `tsc` produces `dist/bin/init.{js,d.ts,js.map,d.ts.map}`. `dist/bin/init.js` is `chmod +x` so the bin works on install.
132
+ - `package.json` exports map points at compiled `dist/`; `bin` field exposes `npx @llodev/pm-tasks-<tool> init`.
133
+ - i18n parity tests migrated from `node:test` to Vitest 2.x (2/2 tests per adapter).
134
+ - Both adapters now declare `task.move` in their `manifest.json` `verbs` array (7 canonical verbs) and document the MCP mapping in `references/operations.md`:
135
+ - Trello: `mcp__trello__move_card({ cardId, idList })` resolving `"wip"`/`"done"`/`"open"` from `lists.<alias>` in `.trello.json`.
136
+ - Asana: `mcp__claude_ai_Asana__update_tasks` with `memberships: [{ project, section }]` resolving via `defaults.<state>SectionAlias` in `.asana.json`.
137
+ - Trello autonomous overlay (`pm-tasks-trello/references/autonomous.md`) updated to invoke canonical `task.move` at task start and before task close.
138
+ - Backwards compatibility: consumers keep importing `@llodev/pm-tasks-core/init-lib` via the subpath; resolution now hits the v1.5.0 dist.
139
+
140
+ **Testkit (`@llodev/pm-tasks-testkit` 0.0.0 → 0.1.0)** — new package, first release.
141
+
142
+ - Pure TypeScript library. 7 in-memory fakes covering every canonical verb: `task.create`, `task.move`, `task.close`, `task.comment.add`, `task.due-date.set`, `task.assignee.add`, `checklist.check`.
143
+ - `createFakeAdapter({ idGenerator?, clock? })` returns an adapter-shaped object with all 7 verbs plus introspection (`getTask`, `getAllTasks`, `reset`).
144
+ - `peerDependencies`: `@llodev/pm-tasks-core ^1.4.0`.
145
+ - Useful for testing custom skills/adapters without hitting real MCP servers; 14/14 vitest tests cover each fake's behavior and idempotency.
146
+
147
+ **Pipeline hardening — E2E canary** — `scripts/checks/canary-e2e.mjs` packs all 4 packages, npm-installs them in a clean sandbox, and exercises core/asana/trello/testkit smoke checks. Wired into `make e2e`, a new `.github/workflows/e2e.yml` (PR + manual dispatch), and as a gate in `release.yml`. Runs in ~3.4 s; catches tarball drift the snapshot test alone misses.
148
+
149
+ **Skill-judge:** non-functional change (TS migration of adapter source + additive task.move documentation in SKILL.md verb lists). Expect Δ ≈ 0; ratchet baseline with updated `capturedAt` per v1.5.0 release per NOISE_BAND policy.
150
+
151
+ ### Patch Changes
152
+
153
+ - Updated dependencies []:
154
+ - @llodev/pm-tasks-core@1.5.0
155
+
156
+ ## 1.3.1
157
+
158
+ ### Patch Changes
159
+
160
+ - chore: reorganize repo structure
161
+
162
+ Pure chore release. Zero runtime change. Splits across:
163
+
164
+ - Reorganized root `scripts/` into `checks/`, `snapshots/`, `shell/` subfolders.
165
+ - Dropped `SKILL.{pt-BR,es-ES}.md` translations (agents read canonical EN-only per agentskills.io spec).
166
+ - Moved localized READMEs to `docs/i18n/` in root + each package (anticipates 5+ locales without cluttering package roots).
167
+ - Updated validators (`validate-localized-paths.mjs`) and tarball snapshot for new paths.
168
+
169
+ Skill-judge: bypassed with `SKIP_SKILL_JUDGE_GATE=1` — only SKILL.md change was decorative readme-selector block removal; expected delta = 0 (cosmetic-only). Per NOISE_BAND policy [-2, +2] bypass allowed.
170
+
171
+ Meta package `@llodev/pm-tasks` not bumped — no tarball content changes, only peerDeps inheritance.
172
+
173
+ - Updated dependencies []:
174
+ - @llodev/pm-tasks-core@1.3.2
175
+
176
+ ## 1.3.0
177
+
178
+ ### Minor Changes
179
+
180
+ - Introduce adapter `manifest.json` + custom-verbs extension API. Each adapter now declares which canonical verbs it implements plus any tool-specific verbs under a `<tool>.*` namespace. `contract-check.mjs` validates the manifest against `adapter-manifest.schema.json`, enforces the `<tool>.<verb>` namespace prefix, and cross-checks each declared verb against the adapter's `SKILL.md`. The same gate now permits additive-only changes to `contract.md` as non-major (deletions still require major). No runtime behaviour changes for existing consumers.
181
+
182
+ ### Patch Changes
183
+
184
+ - Updated dependencies []:
185
+ - @llodev/pm-tasks-core@1.3.0
186
+
187
+ ## 1.2.1
188
+
189
+ ### Patch Changes
190
+
191
+ - [`9f14fe0`](https://github.com/llodev/skills/commit/9f14fe099d8e44fdc01aeb96cdde24aa4fa14527) - CI: release workflow now runs `validate` + `contract:check` + `pre-release` (skill-judge gate) before publish, and a new `scripts/tarball-snapshot.test.mjs` (wired into `pnpm validate`) detects regressions in each package's `files` field. npm publishes use `NPM_CONFIG_PROVENANCE=true` for supply-chain attestation (requires `id-token: write` permission in the workflow). No runtime changes.
192
+
193
+ - Updated dependencies [[`9f14fe0`](https://github.com/llodev/skills/commit/9f14fe099d8e44fdc01aeb96cdde24aa4fa14527)]:
194
+ - @llodev/pm-tasks-core@1.2.1
195
+
196
+ ## 1.2.0
197
+
198
+ ### Minor Changes
199
+
200
+ - [`d8da409`](https://github.com/llodev/skills/commit/d8da409a0a08a481264f8bf64e7bf6a501a16793) - Add opt-in runtime attribution: agents can now stamp `commentPrefix`,
201
+ `autonomousCommentPrefix` and `descriptionFooter` on every `task.create` and
202
+ `task.comment.add`, with strings sourced from `pm-tasks-core/i18n` — fully
203
+ locale-aware. Disabled by default; enable via `attribution.enabled: true` in
204
+ config.json. Closes the v1.0 "Phase C" design item that was deferred from v1.1.
205
+
206
+ Skill-judge gate: measured drift is within the documented noise band
207
+ ([-2, +2]). Asana: 83 → 84 (Δ +1). Trello: 80 → 81 (Δ +1). No baseline
208
+ ratchet required; gate bypassed via `SKIP_SKILL_JUDGE_GATE=1`.
209
+
210
+ ### Patch Changes
211
+
212
+ - Updated dependencies [[`d8da409`](https://github.com/llodev/skills/commit/d8da409a0a08a481264f8bf64e7bf6a501a16793)]:
213
+ - @llodev/pm-tasks-core@1.2.0
214
+
215
+ ## 1.1.2
216
+
217
+ ### Patch Changes
218
+
219
+ - Document recurring gotchas observed during v1.0.x dogfood: Asana subtasks not inheriting custom fields, MCP `get_task` missing activity stories (UI is the source of truth), Trello query-string concat bug class, `create_card` ignoring `idMembers`, and `add_member_to_card` false-error returns.
220
+
221
+ ## 1.1.1
222
+
223
+ ### Patch Changes
224
+
225
+ - Ship pt-BR and es-ES translations of SKILL.md and README.md alongside the existing English originals. Adapters keep en-US as the canonical version; localized files follow the `<basename>.<lang-code>.md` convention. The package tarballs now include the localized files via the `files` field.
226
+
227
+ - Updated dependencies []:
228
+ - @llodev/pm-tasks-core@1.1.1
229
+
230
+ ## 1.1.0
231
+
232
+ ### Minor Changes
233
+
234
+ - Localize init prompts in en-US, pt-BR, es-ES. The first prompt of `npx @llodev/pm-tasks-{asana,trello} init` is now a language picker, and every subsequent prompt is rendered in the chosen locale. The selected locale is recorded as `locale` at the root of the config JSON. New helpers in `@llodev/pm-tasks-core/init-lib`: `promptLocale`, `loadStrings`, `interpolate`, `listLocales`, `registerI18nRoot`. JSON Schemas updated to accept `locale` as an enum. No breaking changes — existing configs without `locale` continue to validate and adapters keep emitting en-US prompts if the strings table is omitted.
235
+
236
+ ### Patch Changes
237
+
238
+ - Updated dependencies []:
239
+ - @llodev/pm-tasks-core@1.1.0
240
+
241
+ ## 1.0.2
242
+
243
+ ### Patch Changes
244
+
245
+ - Allow `defaults.escalateToAlias` (and `defaults.assigneeAlias` on Trello) in the config schemas. The v1.0.1 init scripts emit these keys, but the JSON Schemas still had `additionalProperties: false` rejecting them — making `npx @llodev/pm-tasks-{asana,trello} init` fail at the validate step with "must NOT have additional properties". Trello init also now emits `assigneeAlias: "me"` for parity with Asana.
246
+
247
+ ## 1.0.1
248
+
249
+ ### Patch Changes
250
+
251
+ - Fix `npx @llodev/pm-tasks-asana init` silently doing nothing. Two bugs collapsed the init flow: (a) the `bin` entry was named `pm-tasks-asana-init` (not matching the package name), so npx never resolved the binary; (b) the script's `import.meta.url === file://${process.argv[1]}` guard failed under npx's symlinked bin shim, so even when invoked the `run()` entry-point was skipped. The bin is now `pm-tasks-asana` and the entry-point runs unconditionally.
252
+ - Cross-platform global config path. The init prompt now honors `LLODEV_PM_TASKS_CONFIG_HOME` first, then `XDG_CONFIG_HOME` (macOS/Linux), then `%APPDATA%` (Windows), then `~/.config` as the last fallback. The prompt prints the absolute path before asking, so the destination is always visible.
253
+ - Slug aliases are now Unicode-aware (via `@llodev/pm-tasks-core@1.0.1`): `"Em execução"` → `"em-execucao"`, `"Média"` → `"media"` (was `"em-execu-o"` / `"m-dia"`).
254
+ - Drop the language-specific regex for inferring the "closed" section. The init now explicitly asks: "Which section is the default for newly-created tasks?" and "Which section means 'closed / done'?" with sensible defaults (first / last picked) and a skip option.
255
+ - Add an explicit escalation prompt. After the member list is collected, the init asks: "Pick the escalation contact (will receive escalation comments + add_member on critical cards)" and stores it as `defaults.escalateToAlias`. The chosen member is re-aliased to `"owner"` if no other member already holds that alias.
256
+ - Fallback for empty membership lists: if the PAT's scope didn't return any project members beyond `me`, the init now offers a manual entry (gid + name + alias) so single-collaborator projects can still wire an escalation contact.
257
+ - `multiSelect`: empty input now means "select all" instead of "select none".
258
+ - README clarifies the per-OS defaults and the env override.
259
+
260
+ ## 1.0.0
261
+
262
+ ### Major Changes
263
+
264
+ - [`a571ab1`](https://github.com/llodev/skills/commit/a571ab1537ea7d3fe61c7b89c5be0f08d01f3838) - First stable release of the pm-tasks-\* family.
265
+
266
+ - `@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.
267
+ - `@llodev/pm-tasks-trello` — Trello adapter on the canonical generic card. Paste-friendly output, MCP-driven publish, autonomous mode against a board allowlist.
268
+ - `@llodev/pm-tasks-asana` — Asana adapter with workspace/project/section + custom-field + subtask-inheritance support. Paste, MCP-driven publish, autonomous mode.
269
+
270
+ 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`.
271
+
272
+ ### Patch Changes
273
+
274
+ - Updated dependencies [[`a571ab1`](https://github.com/llodev/skills/commit/a571ab1537ea7d3fe61c7b89c5be0f08d01f3838)]:
275
+ - @llodev/pm-tasks-core@1.0.0
276
+
277
+ ## 0.1.0 (unreleased)
278
+
279
+ - Initial extraction from `plan-to-task-cards` Phase 5b (Asana).
280
+ - 6 CRUD verbs (create, checklist.check, close, due-date.set, assignee.add, comment.add).
281
+ - Parent task + subtasks model with custom-field inheritance.
282
+ - 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,100 @@
1
+ <!-- readme-selector:start -->
2
+ <p align="center">
3
+ <a href="https://github.com/llodev/skills/blob/main/pm-tasks/pm-tasks-asana/README.md"><img src="https://raw.githubusercontent.com/lloliveiradev/public-assets/main/images/usa.svg" width="30" alt="English"></a>&nbsp;&nbsp;
4
+ <a href="https://github.com/llodev/skills/blob/main/pm-tasks/pm-tasks-asana/docs/i18n/README.pt-BR.md"><img src="https://raw.githubusercontent.com/lloliveiradev/public-assets/main/images/brazil.svg" width="30" alt="Português"></a>&nbsp;&nbsp;
5
+ <a href="https://github.com/llodev/skills/blob/main/pm-tasks/pm-tasks-asana/docs/i18n/README.es-ES.md"><img src="https://raw.githubusercontent.com/lloliveiradev/public-assets/main/images/spain.svg" width="30" alt="Español"></a>
6
+ </p>
7
+ <!-- readme-selector:end -->
8
+
9
+ # @llodev/pm-tasks-asana
10
+
11
+ > Asana adapter for the `@llodev/pm-tasks-*` family — turn implementation plans into Asana parent tasks + subtasks and operate them via paste, MCP publish, or autonomous write-through.
12
+
13
+ [![npm](https://img.shields.io/npm/v/@llodev/pm-tasks-asana?label=npm&color=cb3837&logo=npm)](https://www.npmjs.com/package/@llodev/pm-tasks-asana)
14
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
15
+ [![Node.js >= 20](https://img.shields.io/badge/node-%3E%3D20-43853d?logo=node.js&logoColor=white)](https://nodejs.org)
16
+ [![Agent Skills spec](https://img.shields.io/badge/Agent_Skills-spec-7c5cff)](https://agentskills.io)
17
+
18
+ What you get:
19
+
20
+ - **Paste-ready output** — agent renders a generic card; you paste into Asana manually.
21
+ - **MCP publish** via the `claude.ai Asana` MCP server (OAuth, no PATs in JSON). Parent task + flat subtasks + custom fields + section placement in one batch.
22
+ - **CRUD operations** on existing tasks: `checklist.check`, `task.close`, `task.comment.add`, `task.due-date.set`, `task.assignee.add`.
23
+ - **Autonomous mode** — `[autonomous]` / `--auto` sentinel for write-through under allowlist + scope + rate limits. Multi-task loops mirror state to Asana in real time.
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ # npm (with skillpm or Claude Code marketplace)
29
+ npm i @llodev/pm-tasks-core @llodev/pm-tasks-asana
30
+
31
+ # Vercel CLI (install core manually too)
32
+ npx skills add llodev/skills/pm-tasks/pm-tasks-core
33
+ npx skills add llodev/skills/pm-tasks/pm-tasks-asana
34
+ ```
35
+
36
+ ## Setup the MCP
37
+
38
+ Asana uses OAuth via the `claude.ai Asana` MCP. If you've already connected your Asana account in Cursor or Claude Code settings, you're done.
39
+
40
+ For any MCP-capable agent (Claude Code, Cursor, Codex, Windsurf, Cline, Roo Code):
41
+
42
+ 1. Open the agent's MCP settings.
43
+ 2. Enable / register `claude.ai Asana` (or your agent's equivalent Asana MCP).
44
+ 3. Approve the OAuth flow in your browser.
45
+
46
+ In Claude Code, verify with `claude mcp list` — `claude.ai Asana` should appear as authenticated.
47
+
48
+ ## Setup the config
49
+
50
+ The `init` script runs **outside** the MCP, so it needs a Personal Access Token to enumerate your workspaces / projects / sections / custom fields. Generate one at https://app.asana.com/0/my-apps, then:
51
+
52
+ ```bash
53
+ export LLODEV_PM_TASKS_ASANA_PAT=...
54
+ npx @llodev/pm-tasks-asana init
55
+ ```
56
+
57
+ Walk through the prompts. Pick where the config should live:
58
+
59
+ - **local** → `./.asana.json` (recommended for project-scoped configs, can be committed).
60
+ - **global** → a platform default, customizable. Defaults:
61
+ - macOS / Linux (XDG): `$XDG_CONFIG_HOME/llodev/pm-tasks/asana.json`, falling back to `~/.config/llodev/pm-tasks/asana.json`.
62
+ - Windows: `%APPDATA%\llodev\pm-tasks\asana.json`.
63
+ - Override on any OS with `LLODEV_PM_TASKS_CONFIG_HOME=/your/path` — the file lands at `$LLODEV_PM_TASKS_CONFIG_HOME/asana.json`.
64
+
65
+ The `init` prompt prints the absolute path it will write to, so you always see exactly where the file goes.
66
+
67
+ > [!IMPORTANT]
68
+ > The PAT is **only** used by `init`. The MCP itself uses OAuth — never put tokens in the JSON.
69
+
70
+ ## Use
71
+
72
+ | Prompt example | What the agent does |
73
+ | ---------------------------------------------------------------- | --------------------------------------------------------------------- |
74
+ | `"publish this plan as Asana tasks"` | Phase 5 publish — parent + subtasks + custom fields in one batch |
75
+ | `"check subtask 3 on task X in Asana"` | Phase 6 CRUD — `checklist.check` on the subtask |
76
+ | `"close task Y"` | Phase 6 CRUD — `task.close` (moves to close section + sets completed) |
77
+ | `"comment on task X: shipped"` | Phase 6 CRUD — `task.comment.add` |
78
+ | `"[autonomous] create task in asana from plan @docs/plans/X.md"` | Phase 5b autonomous (requires `autonomous.enabled: true`) |
79
+
80
+ ## Asana-specific notes
81
+
82
+ > [!NOTE]
83
+ > **Subtasks are one level deep** — the adapter flattens nested checklists into a single subtask layer.
84
+
85
+ > [!WARNING]
86
+ > **Custom fields do NOT inherit by default** — list field IDs in `subtaskDefaults.inheritParentFields` so the adapter copies them from parent to subtasks at create time.
87
+
88
+ - **Assignee is a single field** — use `task.assignee.add` to add followers; the primary assignee replaces on conflict.
89
+ - **MCP `get_task` doesn't return activity stories** — the Asana UI activity feed is the source of truth for attribution audits. See [`anti-patterns/asana.md`](./anti-patterns/asana.md).
90
+
91
+ ## Documentation
92
+
93
+ - [`SKILL.md`](./SKILL.md) — phase routing + CRUD vocabulary.
94
+ - [`schemas/config.json`](./schemas/config.json) — `.asana.json` JSON Schema.
95
+ - [`anti-patterns/asana.md`](./anti-patterns/asana.md) — recurring gotchas.
96
+ - [`../pm-tasks-core/references/autonomous-mode.md`](../pm-tasks-core/references/autonomous-mode.md) — autonomous-mode contract.
97
+
98
+ ## License
99
+
100
+ MIT — see [LICENSE](./LICENSE).
package/SKILL.md ADDED
@@ -0,0 +1,198 @@
1
+ ---
2
+ name: pm-tasks-asana
3
+ description: >-
4
+ Asana adapter for the @llodev/pm-tasks-* family. Use when the user mentions
5
+ Asana, asks to "create Asana task", "publish to Asana", "post to Asana",
6
+ "publish", "add comment in Asana", or uses --publish-asana; OR for CRUD on
7
+ existing tasks (check subtask, close task, change due-date, assign person,
8
+ comment); OR when invoked autonomously by another agent with [autonomous] /
9
+ --auto sentinel. Asana hierarchy: workspace > project > section > parent task
10
+ > subtasks (one level), with custom fields and multi-assignee support. Modes:
11
+ paste-ready (no MCP needed), MCP publish (via claude.ai Asana MCP), autonomous
12
+ (write-through with allowlist). Implements 7 CRUD verbs (task.create,
13
+ task.move, checklist.check, task.close, task.due-date.set, task.assignee.add,
14
+ task.comment.add) from pm-tasks/pm-tasks-core/references/contract.md. Requires
15
+ @llodev/pm-tasks-core installed.
16
+ license: MIT
17
+ metadata:
18
+ version: 1.6.0
19
+ tags:
20
+ - agent-skill
21
+ - asana
22
+ - plan-to-tasks
23
+ - pm-tools
24
+ family: pm-tasks
25
+ role: adapter
26
+ tool: asana
27
+ compatibility:
28
+ agents:
29
+ - claude-code
30
+ - cursor
31
+ - codex
32
+ - windsurf
33
+ - cline
34
+ - roo-code
35
+ ---
36
+
37
+ # pm-tasks-asana
38
+
39
+ Adapter for Asana within the `@llodev/pm-tasks-*` family. Use the core skill's extraction phases, then apply Asana formatting and optionally publish/operate via the `claude.ai Asana` MCP server.
40
+
41
+ ## Routing
42
+
43
+ | Mode | Trigger | Path |
44
+ | -------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
45
+ | Paste-only | "format as Asana task" without MCP intent | Phase 3 (core) → Phase 4 (this skill, format only) → output paste blocks |
46
+ | MCP publish | "publish to Asana", "create on Asana", "--publish-asana" | Phase 3 → Phase 4 → Phase 5 (publish via MCP) |
47
+ | Autonomous | `[autonomous]` or `--auto` in prompt OR `LLODEV_PM_TASKS_AUTONOMOUS=1` | Phase 3 → Phase 4 → Phase 5b (write-through, no preview) |
48
+ | CRUD ops | "check subtask N on task X", "close task Y", "assign Alice to task Z", "comment on task X" | Phase 6 (operations, direct verb dispatch) |
49
+ | Plan-execution | Plan file path in prompt (`docs/plans/*.md`), OR `--plan-exec` flag, OR plan reference paired with `.asana.json` | Phase 7 (discover existing cards via `discoverPlanTasks`; calling agent dispatches per-task verbs at task boundaries) |
50
+
51
+ ## Asana model
52
+
53
+ Asana tasks have:
54
+
55
+ - **Name** (title, ≤80 chars for board view).
56
+ - **Description** (rich text; prefer `**Section**` bold labels — `##` headings render inconsistently).
57
+ - **Subtasks** — one level deep. Custom fields and assignee do NOT auto-propagate from parent; the adapter sets them explicitly per `subtaskDefaults.inheritParentFields` in `.asana.json`.
58
+ - **Sections** — group tasks within a project.
59
+ - **Custom fields** — per-project; API always uses option GIDs, never display names.
60
+ - **Multi-assignee** — Asana allows multiple followers; primary assignee is a single field. Use `task.assignee.add` to add followers.
61
+
62
+ ## Phase 4 — Asana formatting
63
+
64
+ Apply the generic card from core's [`../pm-tasks-core/references/generic-card.md`](../pm-tasks-core/references/generic-card.md). Then map to Asana:
65
+
66
+ - Title → task `name`.
67
+ - Sections of the generic card → bold `**Section**` labels inside `description` (not `##`).
68
+ - "Implementation Checklist" + "Verification Checklist" → subtasks (flatten any nested bullets; Asana supports one level only).
69
+ - Labels → custom field options (resolved via `.asana.json` `customFields[]`).
70
+ - Due date → `due_on` (YYYY-MM-DD).
71
+ - Assignee → `assignee` GID resolved from `.asana.json` `members[]` or `me` at publish time.
72
+
73
+ ## Phase 5 — MCP publish
74
+
75
+ **Prerequisites:** Asana MCP server (`claude.ai Asana`) connected in your agent. The MCP handles OAuth; the adapter never sees tokens. Configuration steps differ per agent — register the same Asana MCP endpoint your agent supports:
76
+
77
+ - **Claude Code**: `claude mcp add asana -s project -- npx -y claude-ai-asana-mcp` (or follow Anthropic's setup for the hosted `claude.ai Asana` connector).
78
+ - **Cursor / Windsurf / Cline / Roo Code**: add an entry to that agent's MCP settings JSON pointing at the same `claude-ai-asana-mcp` command (envelope identical to the Trello example in `pm-tasks-trello/references/mcp-config.md`).
79
+ - **Codex**: TOML entry under `[mcp_servers.asana]` in `~/.codex/config.toml`.
80
+ - **Other MCP-capable agents**: consult that agent's MCP docs; the server command and OAuth flow are constant.
81
+
82
+ Strict order: 5.1 read `.asana.json` (full file) → 5.2.5 resolve assignee + custom fields + per-subtask field map → 5.2 preview & approval → 5.3 publish via MCP → 5.4 error handling.
83
+
84
+ MCP publish sequence:
85
+
86
+ 1. **Parent task** — `create_tasks` with `name`, `notes` (description), `projects: [projectGid]`, `memberships: [{ project, section }]`, `assignee` (resolved GID), `due_on`, `custom_fields` (JSON string of `{fieldGid: optionGid}`).
87
+ 2. **Subtasks** — `create_tasks` per subtask with `parent: parentGid`, `name`, `assignee` (inherited or per-subtask), `custom_fields` matching `subtaskDefaults.inheritParentFields`.
88
+ 3. **Tags** (optional) — `addTag` per tag GID.
89
+ 4. **Confirm** — list parent + subtasks with permalinks.
90
+
91
+ ### Attribution (opt-in)
92
+
93
+ Before calling the MCP create tool, read `config.attribution`. If `enabled === true`, prefix the comment with the `commentPrefix` returned by `getAttribution()` and append the `descriptionFooter` to the end of `description`. In autonomous mode (`[autonomous]` sentinel), the `commentPrefix` automatically becomes the `autonomousCommentPrefix`. See [references/attribution.md in pm-tasks-core](../pm-tasks-core/references/attribution.md) (added in v1.2.0).
94
+
95
+ ## Phase 5b — Autonomous
96
+
97
+ Skip 5.2 preview & approval. Apply autonomous-mode contract from [`../pm-tasks-core/references/autonomous-mode.md`](../pm-tasks-core/references/autonomous-mode.md). Audit log entries per [`../pm-tasks-core/references/audit-log-format.md`](../pm-tasks-core/references/audit-log-format.md).
98
+
99
+ Asana-specific autonomous scope: `autonomous.scope.projects[]` + `autonomous.scope.sections[]` must include the target GIDs. Any custom-field write must be in `autonomous.allow` (`task.create` covers create-time field set; ongoing field changes are out of scope for v1.x).
100
+
101
+ ### Continuous operation in multi-task loops
102
+
103
+ Mandatory when the controller is executing a plan with multiple tasks autonomously. Mirror each task transition on the corresponding Asana task in real time — NEVER batch state updates at end-of-loop. See [`../pm-tasks-core/references/autonomous-mode.md`](../pm-tasks-core/references/autonomous-mode.md) § **Continuous operation across multi-task loops** for the full contract and anti-patterns.
104
+
105
+ Asana-specific verb mapping:
106
+
107
+ | Transition | Canonical verb + MCP call(s) |
108
+ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
109
+ | Task start | `task.move(cardId, "wip")` → `mcp__claude_ai_Asana__update_tasks { task: <gid>, memberships: [{ project: <projectGid>, section: <wipSectionGid> }] }` |
110
+ | Step complete (subtask) | `checklist.check` → `mcp__claude_ai_Asana__update_tasks { task: <subtaskGid>, completed: true }` |
111
+ | Task complete (full) | `task.move(cardId, "done")` → `mcp__claude_ai_Asana__update_tasks { task: <parentGid>, memberships: [{ project: <projectGid>, section: <doneSectionGid> }] }` then `task.comment.add` → `mcp__claude_ai_Asana__add_comment { task_id: <parentGid>, text: "🤖 [agent] Task complete. Commit: <SHA>. <branch>." }` then `task.close` → `mcp__claude_ai_Asana__update_tasks { task: <parentGid>, completed: true }` |
112
+ | Task failed | `task.comment.add` → `mcp__claude_ai_Asana__add_comment` with failure mode + `mcp__claude_ai_Asana__update_tasks { task: <gid>, add_followers: ["<escalateToAliasGid>"] }` for human escalation. Do NOT call `task.move(_, "done")` or `task.close`. |
113
+
114
+ Resolve `<wipSectionGid>` / `<doneSectionGid>` from `.asana.json` using `defaults.wipSectionAlias` / `defaults.doneSectionAlias` to look up the matching entry in `sections[]` by alias. Both must already be in `autonomous.scope.sections` — otherwise the verb returns `OUT_OF_SCOPE`.
115
+
116
+ **Asana caveat (per [`anti-patterns/asana.md`](./anti-patterns/asana.md)):** the MCP `get_task` doesn't return activity stories, so verifying the lifecycle programmatically is incomplete. The UI activity feed IS the human's audit log — keep it dense and accurate.
117
+
118
+ ## Phase 6 — CRUD operations (existing tasks)
119
+
120
+ For verbs other than `task.create`, jump directly to the operation. Verb → MCP tool mapping:
121
+
122
+ | Core verb | Asana MCP tool | Notes |
123
+ | ------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
124
+ | `task.create` | `create_tasks` | parent + subtasks per Phase 5 |
125
+ | `task.move` | `update_tasks` | `memberships: [{ project, section: <resolvedGid> }]`. Resolve `"wip"` / `"done"` / `"open"` via `defaults.wipSectionAlias` / `defaults.doneSectionAlias` / `defaults.openSectionAlias` in `.asana.json`. Raw section GIDs pass through. |
126
+ | `checklist.check` | `update_tasks` | for subtasks: `completed: true`; emulates checklist via subtask model |
127
+ | `task.close` | `update_tasks` | `completed: true` on parent |
128
+ | `task.due-date.set` | `update_tasks` | `due_on: "YYYY-MM-DD"` |
129
+ | `task.assignee.add` | `update_tasks` + `addFollower` | primary assignee replaces; additional are followers |
130
+ | `task.comment.add` | `add_comment` (story) | adds a comment story to the task; apply attribution prefix if enabled |
131
+
132
+ `<task-ref>` resolution: accept Asana permalinks (`https://app.asana.com/0/<project>/<task>`), bare GIDs, or aliases from `.asana.json` `taskAliases[]`.
133
+
134
+ ## Phase 7 — Plan-execution mode
135
+
136
+ When the calling agent passes a plan reference (a path to a markdown plan file, a plan slug, or an explicit list of expected task titles), this skill loads `.asana.json` via `requireConfig` and uses `discoverPlanTasks` (from `@llodev/pm-tasks-core`) to triage which tasks in the plan already exist as cards in scope. The skill returns the triage report `{ found, missing, ambiguous }` — the **calling agent** decides how to act on each bucket (create missing cards via Phase 4 + 5; disambiguate by picking the right ambiguous card; proceed with existing).
137
+
138
+ The skill does **not** assume any particular implementation strategy. It does not drive the calling agent's task loop, does not depend on any specific orchestration framework, and does not require any markup beyond what is already documented for `task.create` and the autonomous-mode contract. When the calling agent finishes a task and asks the skill to record progress, it invokes the standard verbs (`task.move`, `checklist.check`, `task.comment.add`, `task.close`) directly — the same path used by Phase 5b autonomous mode.
139
+
140
+ Full contract: triggers, discovery semantics, `ConfigRequiredError` handling, failure modes table, and the hook contract for Phase 5 are documented in [`pm-tasks/pm-tasks-core/references/plan-execution.md`](../pm-tasks-core/references/plan-execution.md) (added in v1.9.0).
141
+
142
+ ## Result envelope
143
+
144
+ Every verb returns the core contract shape (see [`../pm-tasks-core/references/contract.md`](../pm-tasks-core/references/contract.md) §Result envelope):
145
+
146
+ ```json
147
+ {
148
+ "ok": true,
149
+ "verb": "task.create",
150
+ "tool": "asana",
151
+ "ref": { "id": "<gid>", "url": "https://app.asana.com/0/<project>/<gid>", "alias": "<optional>" },
152
+ "details": {
153
+ /* Asana-specific (see table below) */
154
+ }
155
+ }
156
+ ```
157
+
158
+ Asana-specific `details` per verb:
159
+
160
+ | Verb | `details` fields |
161
+ | ------------------- | ------------------------------------------------------------------------ |
162
+ | `task.create` | `{ parentGid, subtaskGids[], projectGid, sectionGid?, customFields[]? }` |
163
+ | `task.move` | `{ taskGid, sectionGid, targetList }` |
164
+ | `checklist.check` | `{ subtaskGid, completed: true }` |
165
+ | `task.close` | `{ parentGid, completed: true }` |
166
+ | `task.due-date.set` | `{ taskGid, due_on }` |
167
+ | `task.assignee.add` | `{ taskGid, assignee, followers[]? }` (primary vs follower split) |
168
+ | `task.comment.add` | `{ taskGid, storyGid }` |
169
+
170
+ On failure: `{ ok: false, verb, tool, error: { code, message, retriable } }`. Common codes: `FORBIDDEN_VERB`, `OUT_OF_SCOPE`, `NOT_FOUND`, `RATE_LIMITED`, `PARTIAL_CREATE` (subtask failed mid-create — see [`../pm-tasks-core/references/contract.md`](../pm-tasks-core/references/contract.md) §Partial-create recovery).
171
+
172
+ ## Anti-patterns
173
+
174
+ See [`anti-patterns/asana.md`](anti-patterns/asana.md) — paste health, custom-field rules, GID requirements, partial-create handling.
175
+
176
+ ## Standalone fallback
177
+
178
+ If `@llodev/pm-tasks-core` is not installed: ask the user for minimum input (title + subtask names) and produce a paste-ready Asana task body from this content alone. Quality is degraded — no scope/audience/fidelity inference. Print: _"Install `@llodev/pm-tasks-core` for the full flow."_
179
+
180
+ ## Config
181
+
182
+ Lookup order: `<git-root>/.asana.json` → `~/.config/llodev/pm-tasks/asana.json` → abort with init instructions. Schema: [`schemas/config.json`](schemas/config.json). Secrets NEVER in JSON — Asana MCP holds OAuth; `init` uses `LLODEV_PM_TASKS_ASANA_PAT` env var only.
183
+
184
+ ## Init
185
+
186
+ ```
187
+ npx @llodev/pm-tasks-asana init
188
+ ```
189
+
190
+ See [`../pm-tasks-core/references/init-ux.md`](../pm-tasks-core/references/init-ux.md) for the shared flow. Asana init reads workspaces / projects / sections / custom fields via the Asana REST API using a Personal Access Token (env `LLODEV_PM_TASKS_ASANA_PAT`).
191
+
192
+ Pass `--doctor` to run workspace health checks before prompting:
193
+
194
+ ```sh
195
+ npx @llodev/pm-tasks-asana init --doctor
196
+ ```
197
+
198
+ Runs core checks (C-FS-1..3, C-CFG-1..4) plus Asana-specific probes (C-ASN-1..3, gated on `LLODEV_PM_TASKS_ASANA_PAT`). Full check matrix in [`pm-tasks/pm-tasks-core/references/doctor.md`](../pm-tasks-core/references/doctor.md).