cyberuni/cyber-asana
Asana CLI and agent skills, with an opt-in MCP server
cyber-asana
0.21.1
Patch Changes
- d1929a4: Declare the
cyber-agent-harnessplugin dependency by name only, so the plugin installs from any marketplace instead of onlycyberplace.
0.21.0
Minor Changes
-
98c7797: Narrow the library entry to its intended public surface. Each domain now exports its use cases, its standalone functions, and its gateway types;
events,memberships,rules,status, andtask-templatesjoin the other domains (getEvents,listMemberships,triggerRule,listStatuses,listTaskTemplates, and the rest).Breaking for library consumers: the Asana SDK gateway adapters (
createAsana*Gateway), the CLI/MCP wiring (createRuntimeContext,registerCliCommands,registerMcpTools), the repo and global config helpers, andsetAmbientToken/getTokenOverrideare no longer exported. Use the standalone functions, or pass your own gateway tocreate*Api. -
b6b48aa: Ship
project-tracker.capabilities.asana, a reference that tells repobuddy'sissueandepicskills how to search, create, link, retype and close work in Asana: an issue is a task, an epic is a parent task with subtasks, and retyping happens in place. Initiative is not covered yet and is marked unsupported. Asana is a private tracker, so nothing from it is copied into a public one. -
ddb5587: Rate-limit every Asana request. A per-token limiter keeps the CLI, the MCP server and the library under Asana's limits, waits instead of failing, and retries a 429 after
Retry-After. SetASANA_PLANtofree(default) orpaid, orASANA_RATE_LIMIT_PER_MINUTEto set the budget yourself. -
954bffa: Remove the deprecated
ASANA_TOKENandASANA_WORKSPACEenvironment variables. OnlyASANA_ACCESS_TOKENandASANA_WORKSPACE_GIDare read; rename the old variables if you still set them.auth statusno longer listsASANA_TOKENas a credential source.The workspace fallback on
--workspace-gidnow goes through the same guard as the token, so an unexpanded${ASANA_WORKSPACE_GID}placeholder counts as unset instead of being sent as the GID. -
aa73944: Remove
config migrate-conventions. A repo config that still has aconventionsblock is still rejected; the error now says to move its keys into the frontmatter of.agents/references/cyber-asana.task-conventions.mdand delete the block. -
6b9fbbc: Make the skills self-contained.
init-asanano longer shipsscripts/cyber-asana.mjs, which reached out of the skill folder todist/cli.js. It resolves the CLI aspnpm exec cyber-asanawherecyber-asanais a direct dependency, then a global install, then a pinnednpx cyber-asana@<exact>. Skill README links to sibling skills are now GitHub URLs, and therepobuddycall inlink-pris pinned to an exact version. -
50304dd: Type the library's Asana-returning functions and gateway interfaces (
getTask,listProjects,TaskGateway,ProjectGateway, and more) against the realAsana.*shapes instead ofany.Taskalso gainscustom_fields,num_subtasks, andstart_at.Because Asana's
opt_fieldsparameter can omit any field exceptgid, every resource'snameandresource_typeare now optional on the type, matching what the API actually returns rather than assuming a field is always present. TypeScript consumers accessing.nameor.resource_typewithout a null check will need to add one.
Patch Changes
- 3d867ba: Fix three contradictions in the plugin skills: sprint-report no longer says a task can carry both story and task points, sync-asana-project names
ASANA_WORKSPACE_GIDinstead of the deprecated alias, and init-asana no longer says the config stores projects only. - 09adc77: Fix
events getdropping the sync token andhas_moreafter the first call. Asana returns them on the response body, not on the page object the SDK hands back, so every follow-up poll reported no token and an exhausted feed. - ad8d764: The short GID flags (
--project,--task,--parent,--workspace, and the rest) are now documented as supported aliases of their--<resource>-gidforms instead of "legacy" ones. Help reads(alias of --project-gid). Both spellings stay in 1.x.
0.20.0
Minor Changes
- beeddc9: Update runtime dependencies.
- 8647abd: Treat story points and task points as two field roles.
config discover-fieldsnow maps "Story Points"/"SP" tostory_pointsand "Task Points"/"TP" totask_points, so a project with both fields saves both without asking. The task conventions tell agents to classify each task as user-story work or a chore and set only the matching field — never the same value in both, and nothing on a parent task. The sprint report and standup skills total the two separately. - e3406eb: The
improve-descriptionskill is now the/cyber-asana:tidy-descriptioncommand. Its procedure moved into theasanaskill as the Tidy a description route (skills/asana/references/tidy-description.md), so a request to clean up a task or project description, or to recover whenhtml_notesfails with "XML is invalid", reaches it through theasanaskill. The command takes the same arguments:[task-gid] [emoji] [template:prd|bug|research] [tone] [sources]. If you invokedimprove-descriptionby name, use/cyber-asana:tidy-descriptionor ask theasanaskill instead.
Patch Changes
- 5e9faae: Ship the readme in the npm package so the npm page renders it. Readme links now point at absolute GitHub URLs.
- da63ed9: Task conventions: story points cover all work needed to deliver a story, including its tests and required refactors. A finished task records its actual effort split across both points fields.
- 892eb18:
task updateaccepts--no-completedto reopen a completed task, sendingcompleted: false. Omitting both--completedand--no-completedstill leaves completion untouched.
0.19.0
Minor Changes
- c84a66d: Update runtime dependencies.
Patch Changes
- 956a3ab: Resolve the
cyber-asana.task-conventionsreference through@cyberuni/agent-harnessinstead ofbuddy-agent-harness, and point the skills and the Claude Code plugin dependency at thecyber-agent-harnessplugin, which now ships thereferenceskill.
0.18.0
Minor Changes
- e5c4a0b: Update runtime dependencies.
- 2f766cd:
task get --with-stories [--since <iso-time>]returns the task and its stories (comments and change history) in one output as astoriesfield, paging through every story.--sincefilters stories bycreated_aton the client and implies--with-stories. The MCP toolasana_task_gettakes the same options aswith_storiesandsince.
Patch Changes
- 545bd03: Fix
task dependency add|removeandtask dependent add|remove(and the matching MCP tools): send dependency and dependent GIDs as plain strings, which Asana requires, instead of{ gid }objects that it rejects with "Not a valid GID type: object".
0.17.0
Minor Changes
- 04b47fe:
task createandasana_task_createnow applydefaults.sectionthemselves. When a task is created in the repo config's default project anddefaults.sectionis set, it is created directly in that section, in the same API call, so there is no separate placement step to forget or to fail on its own. A task created only in other projects is never placed, because the section belongs to the default project. Opt out per call with--no-default-section(CLI) ordefault_section: false(MCP). - b00118b: Update runtime dependencies.
- 509677d: Update runtime dependencies.
0.16.0
Minor Changes
-
455285b: Add the
asanaskill: the single entry point for Asana work. It routes a request to the skill that owns it (init-asana,config-asana,asana-standup,asana-sprint-report,link-pr-to-task,improve-description,create-tasks-from-code,sync-asana-project), creates tasks, and tracks session work — reusing an incomplete task of yours that describes the same outcome and commenting the branch and pull request on it, or creating one, per thecyber-asana.task-conventionsreference.Renamed: the
create-asana-taskskill is replaced by the/cyber-asana:create-taskcommand. Its procedure is unchanged and now lives in theasanaskill'sreferences/create-task.md, which both the command and theasanaskill follow. Codex has no plugin commands; there, and in skills-only installs, ask for a task and theasanaskill files it. If you installedcreate-asana-taskon its own withnpx skills add, installasanainstead. -
74ff399: Expose the API surface added in
asana3.2.0.- New
ai-studiocommands and MCP tools for AI Studio usage:ai-studio runs/asana_ai_studio_run_listlists each AI Studio run with its model and credits used, oldest first (poll forward with--start-at), andai-studio seats/asana_ai_studio_seat_listlists seat allocations (--state active|revoked). Both take--division-gid. Asana restricts these endpoints to service accounts in organizations licensed for AI Studio. task list,project list, andportfolio list(andasana_task_list,asana_project_list,asana_portfolio_list) accept a custom type filter (--custom-type <gid>/custom_type). An empty value selects items with no custom type.
- New
-
f200828: Breaking: task conventions (
task_name_format,description_template,default_tags) now come only from the frontmatter of thecyber-asana.task-conventionsreference, resolved the buddy-agent-harness way (repo.agents/references/, user~/.agents/references/, installed plugins, then the copy cyber-asana ships) and merged key by key. Theconventionsblock in.agents/cyber-asana.jsonis no longer read: a config that still has one fails to load, andconfig set/config showno longer accept or printconventions.*keys.Move an existing block with the new
cyber-asana config migrate-conventionscommand. It writes the keys into the frontmatter of.agents/references/cyber-asana.task-conventions.md(created withmerge: merge-sectionswhen absent; an existing file only gains frontmatter keys, and a key it already sets is refused) and then drops the block. It honors--json,--toon, and--dry-run. -
4514686: Fold the
create-tasks-from-codeskill into theasanarouter as the Import TODOs route, and add the/cyber-asana:import-todoscommand. Thecreate-tasks-from-codeskill is removed: ask theasanaskill to turn TODO/FIXME comments into tasks, or run/cyber-asana:import-todos. The procedure lives inskills/asana/references/import-todos.mdand now creates each task throughreferences/create-task.md, so imported tasks follow the repo's task conventions. -
3a1edb5: Fold the
link-pr-to-taskskill into theasanarouter as the Link a PR route, and add the/cyber-asana:link-prcommand. Thelink-pr-to-taskskill is removed: ask theasanaskill to link a pull request to its task, or run/cyber-asana:link-pr. The procedure lives inskills/asana/references/link-pr.mdand now works on every major git host — GitHub (including Enterprise), GitLab (including self-hosted), Bitbucket Cloud and Data Center, Azure DevOps, Gitea, and Forgejo/Codeberg — with that host's official CLI or REST API. When the host's CLI is missing or logged out it hands off to repobuddy'sinit-buddyskill if installed, or asks for the pull request URL. -
f4d1092: The plugin no longer starts the MCP server. It ships the CLI and skills only, and the MCP server is opt-in.
Breaking: a plugin install (Claude Code, Cursor, Codex, Copilot CLI) no longer registers the
cyber-asanaMCP server, and the package no longer publishesmcp.jsonor.mcp.json. The skills drive the CLI instead, which costs far fewer context tokens than 100+ MCP tool schemas. To keep the MCP tools, run theinit-asanaskill and accept its MCP step, or addnpx -y cyber-asana@<version> mcpto your client's MCP config yourself. Thecyber-asana mcpcommand, thecyber-asana/mcpexport, and every MCP tool are unchanged. -
68e08f7: The
asanaskill now plans the work before it creates a task. For a task request and for session tracking alike, it splits the request or the session (its conversation, commits, and diff) into units of work, looks each one up among the project's tasks, your incomplete tasks, and recently completed ones, and reuses a match instead of filing it twice. Two or more units that serve one outcome become subtasks of a parent task; a unit that waits on another gets a dependency. It shows the plan first and asks before creating more than one task or changing an existing one, and it marks a task complete only when you say so. The procedure lives in the skill'sreferences/plan-work.md; the grouping rules live in a new Grouping section of thecyber-asana.task-conventionsreference. -
815f9f8: Estimate story points without hard-coding a field GID. A project entry in
.agents/cyber-asana.jsoncan now record custom fields by role (fields.story_points: { gid, name }):cyber-asana config discover-fields [project]finds the project's story point field by name ("Story Points", "Task Points", "Points", "pts", "SP" on a number or dropdown field) and saves it. When several fields match, it lists them and saves none.cyber-asana config set-field <role> <field-gid>andconfig unset-field <role>register or clear one by hand.
The
cyber-asana.task-conventionsreference gains a Story points section: when a task's project has such a field, estimate or measure it with 1 point as the effort a senior staff engineer who knows the stack and the domain needs to fix a one-line bug, about one hour. -
b66e257: Ship the
cyber-asana.task-conventionsreference: when to use a task, a subtask, or a dependency; what to fill in for a task and its subtasks, custom fields, and comments; and how to track session work. Its frontmatter holds the task conventions the CLI and MCP apply. Agents load it by name, and a repository or user can override it section by section.
Patch Changes
- f894d94: Update the bundled
commanderto v15. CLI behavior is unchanged, except that a "too many arguments" usage error now also names the extra arguments (for exampleExpected 0 arguments but got 1: extra.). It still exits with code2. - 915a379: The
asanaskill's work planning now reads the session's pull or merge request on any major git host, through the host table inskills/asana/references/link-pr.md, instead of callingghdirectly. - 7ede5f2: Update runtime dependencies:
@modelcontextprotocol/sdk1.31.0,asana3.2.0,yaml2.9.1, andzod4.6.5.
0.15.0
Minor Changes
-
6ae5a52: Rename and broaden the
pin-asana-projectsskill toconfig-asana. It now covers adding, removing, and refreshing both projects and users, in either the committed repo config or the personal global registry, rather than only seeding projects into the repo config. Anyone installing the old skill by name (npx skills add cyberuni/cyber-asana --skill pin-asana-projects) should switch toconfig-asana. -
1ab48da: Add a global, cross-repo Asana project registry. Pass
--globaltoconfig show,list,path,resolve-project,add,remove, orsyncto read or write a personal registry outside any repo (default$XDG_CONFIG_HOME/cyber-asana/config.json, orCYBER_ASANA_GLOBAL_CONFIG), keyed by a normalized git remote URL (override with--repo <key>). Pass--mergedtoshow,list, orresolve-projectto union it with the repo config, with the repo config winning agidconflict. Also exportsresolveRepoKeyandloadEffectiveProjectsfor skill scripts to resolve the same merged view in-process. -
778e867: Mirror users into the global registry (
--globalonadd-user,resolve-user,remove-user,remove-alias, andlist-users), and add--mergedtoresolve-user/list-usersto union them with the repo config. Unlike projects, global users are a flat list — not filed per repo — since a person's identity doesn't change with which repo you're in.--assigneeon task create/update now falls back to the global registry when the repo config has no match (via the newresolveEffectiveAssignee/resolveEffectiveUserexports), trying the repo config to completion first so an alias that's unambiguous in each registry never becomes a false cross-scope collision. -
9ebd625: Give the repo config richer project entries and repo-wide defaults.
Project entries carry
aliases, apurposeline, and adefault: truemarker alongsidegidandname, so a project can be reached by keyword and an agent can tell what belongs in it. Two optional top-level blocks join them:defaults(assignee,section) andconventions(task_name_format,description_template,default_tags). The schema moves to version 2; a version 1 file still parses and is upgraded in place on the next write.Manage them with
config add --alias --purpose --default,config set-default,config remove-project-alias, andconfig set/config unsetfor the dotted keys.task createandasana_task_createresolve through the registry: a project may be given as a name or alias, and with none given the project markeddefault: trueis used, as isdefaults.assigneewhen no assignee is given. They also apply the repo's conventions —description_templateas the description when no notes are given, anddefault_tagswhen no tags are given. Updates are unaffected, so editing a task cannot silently reassign it.Both surfaces can now send tags on creation:
--tag <gid-or-name[,...]>on the CLI,tag_gidsonasana_task_create. A tag may be named rather than given by GID — a numeric value is used as-is, and anything else is resolved against the workspace's tags.Migration:
--projectontask createnow follows the same contract as--assignee: a numeric value is a GID, and anything else is looked up in the repo config.--project-gidis sent to Asana untouched, so pass it for a value that is neither numeric nor registered.
0.14.0
Minor Changes
- 92e8e18: Accept a comma-separated list in
config add-user --alias(--alias ali,al), alongside a repeated flag, so several aliases can be set in one value instead of being stored as a single alias containing commas. - 53694b9: Add
config remove-alias <alias...>to drop aliases from the repo user registry without removing the user. Aliases can be given as several arguments or a comma-separated list; an unregistered alias fails the command and leaves the config unchanged.
0.13.0
Minor Changes
-
30a03a0:
config add-user --search <query>finds a user by name or email through Asana's typeahead search and registers them, so you no longer need the GID up front. It registers a single hit, or the one hit whose name or email matches exactly. Otherwise it lists the candidates and writes nothing. -
199cd25: Add a repo-local user registry to
config:config add-user <user-gid> --alias <alias>fetches and stores a user's name and email,config resolve-userturns an alias, email, or name into a GID with no API call, andconfig list-users/config remove-usermanage the entries.config syncnow refreshes registered users too. -
6dcddfe: Every singular
getcommand now accepts--opt-fields, and everyasana_<resource>_getMCP tool acceptsopt_fields.user meandasana_user_meaccept them too. Use them to request fields that Asana leaves out of a single-resource read.When you leave the option out, each
getreturns the same fields as before.membership getdoes not take the option, because Asana's endpoint has noopt_fields. -
d6ef7d8:
--assigneeontask create,task update, andtask subtask create— and a newassigneeparameter on the matching MCP tools — now accepts an alias, email, or name registered withconfig add-user, in addition to a user GID orme. It resolves from the repo config with no API call and errors only when the value is unregistered or matches more than one user.--assignee-gidstill takes a GID as-is.
Patch Changes
- 4286acd:
user listandasana_user_listno longer fail with400 The result is too largeon large workspaces. They now page throughGET /users?workspace=(sorted by user ID), and so accept--limit,--all, and--max-pages(MCP:limit,fetch_all,max_pages) like other list commands.
0.12.1
Patch Changes
-
3db4e08: Bundle the CLI's dependencies into
dist/cli.js, and let the skills invoke it directly.An installed agent plugin is a copy of a source checkout, not an npm install, so its directory has no reliable
node_modules. That is why every skill had to shell out through a pinnednpx— the fetch was the only thing supplying the dependency tree. The publisheddist/cli.jsnow inlines each runtime dependency and runs with nonode_modulespresent at all.skills/init-asana/scripts/cyber-asana.mjslaunches that shipped CLI, and the Ensure cyber-asana CLI section now prefers it, falling back to a global install and then to pinnednpxexactly as before. The fallback is deliberate: resolving the launcher path is model behaviour, not a guarantee.The library entries (
.and./mcp) are unchanged. Their dependencies stay external so a consumer that also uses zod or the MCP SDK shares one copy instead of getting a private inlined duplicate.
0.12.0
Minor Changes
-
0a839d6: Close the cross-surface gaps in the goals and status domains:
goal create/goal updategain--start-onand--html-notes,goal updategains--clear-due-onand--clear-start-on, andstatus listgains--created-since. The matching MCP parameters land onasana_goal_create,asana_goal_update, andasana_status_list.Asana has always taken
start_onandhtml_noteson both goal write endpoints andcreated_sinceon the status listing, but none of the three had a CLI or MCP surface. A goal's date range could only be half-set, a goal description could only be plain text, and asking what was posted since the last check-in meant paging the whole status history and filtering locally.Both goal dates are nullable, so unsetting one needs its own input —
--clear-due-onand--clear-start-onsend an explicit null, the waytask updatealready does. Naming a date together with its clear flag, or--notestogether with--html-notes, is a usage error caught locally before any request is sent.The custom-fields domain was swept for the same class of gap and is clean: every parameter the Asana SDK accepts on its six implemented operations is already reachable from both surfaces.
-
7f3cfb3:
section createandasana_section_createaccept a placementAsana's create-section endpoint has always taken
insert_before/insert_after, but cyber-asana only sent the name — a new column had to be created and then moved. Both surfaces now expose the same placement flagssection movealready had:cyber-asana section create "In Review" --project-gid <gid> --insert-after <section-gid>Naming both placements is a usage error, caught before any request is sent.
-
cca9677: Close cross-surface gaps in the stories, attachments, and tags domains — fields Asana accepts on endpoints this package already wraps, but that reached neither the CLI nor the MCP tool.
story create/story update(and thecommentaliases) gain--pin,--unpin, and--sticker <name>;asana_story_create/asana_story_updategainis_pinnedandsticker_name. Pinning or stickering is an edit in its own right, soupdateno longer needs a replacement body.--pinwith--unpin, and a sticker outside Asana's twelve, are usage errors caught before any request is sent.tag creategains--follower <gid[,gid...]>andasana_tag_creategainsfollower_gids. Asana takesfollowersonly at creation, soupdatedeliberately has no counterpart.tag updategains--clear-color(clear_colorover MCP) for Asana's nullable tag colour, mirroringtask update --clear-due-on.asana_attachment_creategainsconnect_to_app, andattachment creategains--connect-to-app. Asana honours it only on an external--urlattachment, so pairing it with a file upload is a local usage error.asana_tag_deleteis now idempotent, like the CLI and every other delete in the package. Its response body changes from{ ok: true, deleted: "<gid>" }to the shared{ deleted, resource, gid, already_absent }record.
-
33d11a4: Close cross-surface gaps in the projects and project-templates domains.
project listgains--archived/--no-archived, andasana_project_listgainsarchived. The gateway had threaded Asana'sarchivedfilter all along; neither surface exposed it.project create/project updategain--archived(and--no-archivedon update), andasana_project_create/asana_project_updategainarchived. Asana has acceptedarchivedon both endpoints all along; no surface could archive a project.project create/project updategain--owner,project updategains--clear-owner, and the matching MCP tools gainowner/clear_owner. Asana's nullableownerhad no setter on any surface.project create/project updategain--default-access-level, and the matching MCP tools gaindefault_access_level. Its siblingprivacy_settingwas already exposed on both surfaces.project-template instantiategains--privacy-settingandasana_project_template_instantiategainsprivacy_setting. Both surfaces exposed only thepublicboolean, which Asana deprecated in favour ofprivacy_setting.project-template instantiategains--strict-datesandasana_project_template_instantiategainsis_strict. Asana'sis_strictturns an unfilled date variable into an error rather than a silently defaulted date; neither surface could ask for it.project-template instantiategains a repeatable--requested-roleandasana_project_template_instantiategainsrequested_roles.requested_dateswas already plumbed on both surfaces; its role counterpart was not.
-
d21d4df:
auth statusaccepts--client-idand--client-secretThe flags already reached
auth login,auth token, andauth logout, but the one command that explains which app registration wins could not see them.cyber-asana auth status --client-id <id>now reports the registration a given pair would resolve to, and which sources it shadows, before you authorize with it. -
e43fd55:
asana_portfolio_deleteis now idempotent, like every other delete in the package. It was the one delete calling Asana bare, so deleting a portfolio that was already gone handed the agent a 404 while the same retry oncyber-asana portfolio deletereported it as already deleted. The tool now returns the shared acknowledgement —{ deleted, resource, gid, already_absent }— instead of a fixedDeleted portfolio <gid>sentence. -
a13401c:
portfolio listgains an owner filter on both surfaces. Theownerparameter was already threaded through the portfolios gateway andapi.tsand sent to Asana, but neither caller could set it: the CLI now takes--owner-gid <gid>(legacy alias--owner) andasana_portfolio_listnow takesowner_gid. Asana honors the filter for service-account tokens; a regular personal access token lists only its own portfolios either way. When no owner is given, noownerkey is sent. -
fd8f7ba: Close the cross-surface field gaps in the
tasksdomain, so the CLI and MCP expose what Asana's task endpoints have always accepted.task creategains--start-onand--completed;asana_task_creategainsstart_onandcompleted. Both were settable on update but not create, even though Asana's create body takes them.task createandtask updategain--due-atand--start-at, andtask updategains--clear-due-atand--clear-start-at.task searchalready filtered on the due time, so tasks with one were findable but not writable. A date paired with its date-time twin is a usage error caught locally, because Asana documents the two forms as not to be used together.task updategains--clear-assignee(clear_assigneeonasana_task_update) —assigneeis nullable, but it was the one nullable field the command wrote with no clear counterpart, so unassigning a task was not expressible.task subtask createandasana_task_subtask_createnow take the same write fields as theirtask createsiblings — rich notes, start dates, resource subtype, custom fields, and followers all previously fell off at the gateway. Callers of the programmaticcreateSubtaskshould note its options change from camelCase ({ notes, assignee, dueOn }) to the sharedCreateTaskFieldsshape ({ notes, assignee, due_on, ... }).
0.11.0
Minor Changes
-
30d1430: Add
--start-onand--clear-start-ontotask update, and the matchingstart_on/clear_start_onparameters to theasana_task_updateMCP tool.Asana's task
start_onfield had no CLI or MCP surface on update, so setting a task's date range meant falling back to the raw API.cyber-asana task update <gid> --start-on 2026-09-01 --due-on 2026-10-31now sets both, and--clear-start-onsends an explicit null the way--clear-due-ondoes. Naming both--start-onand--clear-start-onis a usage error caught before any request is sent.
Patch Changes
- ec491c5: Ship a
SETUP.mdat the plugin root so an agent can finish plugin setup — the Asana personal access token and workspace GID its bundled MCP server reads from the environment — without re-deriving the MCP wiring the install already did. - b6622d2: Catch
npx -y cyber-asanaas an unpinned invocation. The catalog contract's pinning rule skipped over--yesbut not its-yabbreviation, so the shorter spelling passed unchecked in every skill and reference file. - a5565a6: Hold the plugin root's
SETUP.mdto the skill catalog contract: it must exist, sit on the publish allowlist, pin thenpx cyber-asanacommands it prescribes, and resolve every skill it hands off to — so a renamed or dropped skill breaks the build instead of leaving a dead handoff in the file a fresh install reads first.
0.10.0
Minor Changes
-
38054a4: Add the
improve-descriptionskill for cleaning up and rewriting task and project descriptions.The default pass is a copy-edit, not a rewrite: collapse runs of blank lines, fix typos and broken markup, turn dash lists into real lists — while preserving the author's message, wording, and section structure. Emoji, templates, tone changes, and research-backed citations are opt-in modifiers the user asks for, never applied by default.
The skill also documents Asana's rich-text HTML subset, verified against the live API, because the rejection is a bare
XML is invalidthat names neither the tag nor the position. Notable corrections to what the tags are commonly assumed to be:<h1>–<h3>and<pre>are supported;<h4>–<h6>,<p>,<br>, and<div>are not. Paragraphs are separated by literal newlines. A bare<body>wrapper is mandatory and may carry no attributes. A raw<or>is rejected even inside<code>and<pre>, and<code>nested inside<pre>fails with an opaque "unexpected error occurred".
Patch Changes
-
8dd0938: Fix
task get-many --opt-fieldsfailing with HTTP 400Parameters are not accepted in a batch action path.Each batch action built its
relative_pathas/tasks/<gid>?opt_fields=<fields>, and Asana's Batch API rejects a query string there. The requested fields now ride in the action's ownoptions.fieldsarray — where the Batch API schema puts output options — and the path stays bare. Without--opt-fieldsno query string was ever appended, which is why only the flag path failed.
0.9.0
Minor Changes
-
1178597: Distribute the agent plugin inside the npm package.
The published package root is now the plugin root: the tarball carries
plugin.json,mcp.json,skills/, and the Claude Code, Cursor, and Codex manifests alongsidedist/. Claude Code can install it from a marketplace with annpmsource —/plugin marketplace add cyberuni/cyber-asanathen/plugin install cyber-asana@cyberuni— instead of cloning the repo.The portable
plugin.jsonandmcp.jsonfollow Agent Plugins 1.0.0, whose manifest schema is closed; skills and MCP servers are discovered from fixed locations rather than declared inline.Note for spec-conformant clients:
mcp.jsonno longer sets anenvblock. The spec expands only${PLUGIN_ROOT}and${PLUGIN_DATA}, so the previous"ASANA_ACCESS_TOKEN": "${ASANA_ACCESS_TOKEN}"entry would arrive as that literal string and shadow the real token. ExportASANA_ACCESS_TOKEN(and optionallyASANA_WORKSPACE_GID) in the environment that launches the agent.Claude Code reads
.mcp.json, which still expands${VAR}, so a configured token keeps working unchanged.Independently, an environment value that is exactly an unexpanded reference —
${ASANA_ACCESS_TOKEN}— is now treated as unset rather than as a credential. Hosts that cannot expand a reference forward its text verbatim, and Claude Code does the same when the variable is unset and has no default. That literal previously outranked theASANA_TOKENfallback and turned a missing token into a401; a missing credential is now reported as missing.auth statusnames any variable in that state underUnexpanded, in both text and JSON output, so a variable that looks configured everywhere the user checks does not read as an unexplained "not authenticated".
0.8.0
Minor Changes
-
a485520: Add custom field discovery so the GIDs that task writes are keyed by are reachable.
custom-field list --workspace-gid <gid>/asana_custom_field_listlists a workspace's custom fields, andcustom-field get <gid>/asana_custom_field_getreturns one field including itsenum_optionsand their GIDs. Those are the valuestask createandtask updateexpect in--custom-fieldand--custom-fields-json.Reads only — defining or editing custom fields is not wrapped.
-
95d86c3: Add the custom field settings reads — which custom fields are attached to a given project, portfolio, goal, or team. New CLI subcommands
cyber-asana custom-field <project|portfolio|goal|team> <gid>and four MCP tools:asana_custom_field_list_for_project,asana_custom_field_list_for_portfolio,asana_custom_field_list_for_goal,asana_custom_field_list_for_team.custom-field listcovers every field in the workspace; this is the narrower answer, and usually the one you want before writingcustom_fieldson a task. Asana rejects a payload naming a field the project does not have, so a project-scoped list — each field with its type and enum options — answers "what can I set here, and with which values". The defaultopt_fieldsrequests exactly the field GID, name, resource subtype, and enum option GIDs and names.Reads only. Attaching or detaching a custom field lives on the Projects API and is administration; it stays out.
-
21944fd: Add the Asana change feed:
cyber-asana event list <resource-gid>and theasana_event_listMCP tool report what changed on a task, project, or goal since a sync token, instead of re-querying and diffing state.The sync token is returned in the response and passed back with
--sync/syncon the next call — nothing is cached locally. The first call (or one with an expired token) returns no events, a fresh token, andsync_reset: true; that is Asana's documented "start here" handshake, not an error. Whenhas_moreistrue, poll again immediately — Asana caps one token at 100 events. -
91609eb: Add project and team membership management over Asana's unified Memberships endpoint: CLI
cyber-asana membership list|get|create|update|deleteand MCP toolsasana_membership_list,asana_membership_get,asana_membership_create,asana_membership_update,asana_membership_delete. Agents can now answer "who is on this project?", check an assignee actually has access, and add or remove a member as part of an onboarding flow —user listonly ever gave the whole workspace.listfilters by parent (project, portfolio, goal, custom type, or custom field) and/or member (user or team); Asana needs--resource-subtype/resource_subtypewhen the parent is omitted, and the CLI reports any other combination as a usage error instead of a 400.updatechanges--access-levelonly — broader permission management is Asana's Roles API and stays out of scope.deleteis idempotent. -
83cfa6d: Add out-of-office entries, so automation can see who is away before it assigns work.
- CLI:
ooo list|get|create|update|delete, with--start-date/--end-datefilters on the list. - MCP:
asana_ooo_list,asana_ooo_get,asana_ooo_create,asana_ooo_update,asana_ooo_delete. --user-gid/user_giddefaults to the authenticated user, so an unscoped call reads your own calendar.user list,user get, anduser menow suggest the matchingooo listcall.
- CLI:
-
fd4a143: Start a project from a project template: new CLI resource
cyber-asana project-template list|get|instantiateand MCP toolsasana_project_template_list,asana_project_template_get,asana_project_template_instantiate.listscopes to a workspace or, with--team-gid/team_gid, to a team's templates;getshows the template'srequested_dates, the date variables instantiation fills in via--requested-date <gid>=<YYYY-MM-DD>/requested_dates.Asana builds the project asynchronously, so instantiation returns a job.
instantiatewaits for that job by default and reports the new project's GID under a bounded timeout (--timeout/timeout_seconds, default 60 seconds, polling once a second);--no-wait/wait: falsereturns the job GID for callers that poll themselves. A failed job and an expired wait are both errors — never a success with a missing project GID.Also wraps the Jobs API:
cyber-asana job get <gid>andasana_job_get, so the job behind any async operation — project or task instantiation today, duplication later — can be read directly. -
e63ed53: Add
rule triggerand theasana_rule_triggerMCP tool, firing an Asana automation rule that uses an "incoming web request" trigger. Takes--resource(the task GID) and--action-data-json(free-form variables the rule's action reads). Asana's402now surfaces as a plan limitation with exit code7instead of a generic failure. -
c7afec2: Add a deterministic status roll-up:
cyber-asana status overview <parent-gid>and theasana_status_overviewMCP tool. Given a project GID it returns that project's latest status update and its task counts; given a portfolio GID it also returns one entry per item. It takes a GID and never searches — discovery by keyword stays with the official Asana MCP'sget_status_overview.A portfolio roll-up costs
3 + 2NAPI calls for N items.--limit/limitcaps N (default 25) and a capped roll-up reportstruncatedrather than silently dropping items.--parent-type/parent_typeskips parent detection and saves one call. -
b8a6aac: Add task templates:
cyber-asana task-template list|get|instantiateand theasana_task_template_list,asana_task_template_get,asana_task_template_instantiateMCP tools. A recurring checklist — a release checklist, an incident postmortem — is created in one call instead of rebuilt subtask by subtask.listis project-scoped, as Asana requires.Asana instantiates a task asynchronously and answers with a job, so
instantiatepolls that job for up to--timeoutseconds (timeout_seconds, default 10) and returns it carryingnew_taskonce it succeeds.--no-wait/wait: falsereturns the pending job immediately; a timeout returns the last job seen rather than erroring, so you always get a job GID back. On the CLI afailedjob exits non-zero.Deleting a template is deliberately not wrapped — cyber-asana wraps using templates, not managing them.
-
19be9cc: Add cross-object search backed by Asana's typeahead endpoint, so a name can be turned into a GID without the official Asana MCP server: new MCP tool
asana_search_objectsand CLI commandcyber-asana search objects <resource-type> [query]. One resource type per call (actor,agent,custom_field,goal,portfolio,project,project_template,tag,task,team,user), a single page capped by--count/count(1–100, default 20), and no pagination — results are ordered by relevance/recency and are explicitly not exhaustive. -
05c636a: Read the OAuth app registration from
ASANA_API_CLIENT_IDandASANA_API_CLIENT_SECRET, which now take precedence overASANA_CLIENT_IDandASANA_CLIENT_SECRET.Asana's official hosted MCP server documents
ASANA_CLIENT_ID/ASANA_CLIENT_SECRETfor its own "MCP app" registration, whose tokens do not work against the REST API.cyber-asana auth loginneeds an "API app" instead, so the two can now be exported side by side.ASANA_CLIENT_ID/ASANA_CLIENT_SECRETstill work as a fallback, and--client-id/--client-secretstill win over both. -
f9f9928: Attach and remove files.
attachment createuploads a local file — streamed, not buffered — or links an external URL with--url, andattachment deleteremoves an attachment idempotently. The MCP server gainsasana_attachment_createandasana_attachment_delete; itsfilepath is resolved on the machine running the server.attachment listandasana_attachment_listnow accept--parent-gid/parent_gidfor a project or project brief as well as a task.--task-gidandtask_gidkeep working. -
789ef29: Report the OAuth app registration in
cyber-asana auth status: anAppline with the masked client id and its source, plusApp ignoredfor registrations it shadows (anappobject withclient_id_masked,source, andshadowedunder--json/--toon,nullwhen none resolves). The command stays offline.When Asana rejects the token request with
invalid_client, append a hint about the API-app / MCP-app distinction andASANA_API_CLIENT_IDto Asana's own message. -
4efc478: Read, edit, and delete comments.
story get|update|deleteandcomment get|update|deletejoin the CLI, with matchingasana_story_*andasana_comment_*MCP tools, so a comment posted by mistake can be corrected or withdrawn. Asana only permits changing comment stories you authored — a refusal now carries ahintexplaining that instead of a bare403— anddeleteis idempotent. -
ab49764: Add section reordering and section-scoped task placement.
cyber-asana section move <gid> --project <gid> --insert-before|--insert-after <section-gid>moves a section relative to another in the same project, andcyber-asana section task add <section-gid> <task-gid>places a task directly into a section. Both are also exposed over MCP asasana_section_moveandasana_section_task_add.
Patch Changes
- f340a16: Force
honoto a patched version. It ships to consumers transitively through@modelcontextprotocol/sdk, which has no release depending on a fixed version yet.
0.7.0
Minor Changes
-
65124ac: Make stored OAuth credentials ambient, and show the authorize URL during login.
Every command now resolves stored credentials before dispatch and refreshes the access token when it is within a minute of expiring, so
auth loginis something you do once rather than something each later command has to know about. Without this,auth loginwrote a credentials file that nothing ever read. Precedence is--token>ASANA_ACCESS_TOKEN>ASANA_TOKEN> stored credentials; a stored token occupies its own slot soauth statusstill names the true source instead of reporting it as--token.auth statusnow reports stored credentials, including the granting account and the expiry, and listscredentials.jsonamong the ignored sources when an env var shadows it.Credential resolution never fails a command: unreadable or unrefreshable credentials produce a warning on stderr rather than an exception, since
auth loginandauth logoutare exactly what you reach for when the credential is the problem.auth loginprints the authorize URL to stderr before opening the browser. On WSL and headless machines the opener is often a no-op, and without the URL the command simply appeared to hang. -
f62a57b: Accept
--client-idand--client-secretonauth login,auth token, andauth logout, so trying OAuth needs no edits to a shell profile orsettings.json. Precedence is flags >ASANA_CLIENT_ID/ASANA_CLIENT_SECRETsettings.json, resolved per field, andauth statuscontinues to name the source that won.A secret passed as an argument is captured by shell history and visible in the process list, so the flag help and the readme both point at the environment variables for ongoing use.
-
49c9941: Add
cyber-asana auth login— OAuth authorization through the browser.cyber-asana authenticates against your own Asana app rather than a shipped registration, so no third party's OAuth app sits between you and your data. Set
ASANA_CLIENT_ID/ASANA_CLIENT_SECRET(or write them tosettings.json) and runauth login. The consent redirect is caught by a one-shot server bound to127.0.0.1— the authorization code arrives in a query string, so nothing off the machine can reach the socket that receives it. PKCE andstateare used throughout.Credentials are stored under
$XDG_CONFIG_HOME/cyber-asana, split by owner:settings.jsonis yours and hand-edited,credentials.jsonis the CLI's and rewritten on every refresh. Both are0600.--no-storeruns the flow and prints the access token instead of saving it, for one-off shells and CI. It emits only the hour-long access token; the long-lived refresh token needs--include-refresh-token.--rawprints the bare token for$(...)substitution.A personal access token remains the documented happy path for a single user.
-
2b62e10: Add
auth login --manualfor the out-of-band flow,--redirect-urifor an explicitly registered redirect URL, and a clear message when the callback port is taken.Asana requires redirect URLs to be
httpsand documentsurn:ietf:wg:oauth:2.0:oobfor native and command-line apps, so a loopbackhttp://localhostURL is not always registrable — the flow then fails with "The redirect_uri parameter does not match a valid url for the application".--manualruns the documented path instead: Asana displays the code, you paste it, and no local port is opened. It also works over SSH.--redirect-uricovers apps registered against some other URL, and the callback listener binds the port named by that URL.A callback port already held by an earlier login previously crashed with an unhandled EADDRINUSE stack trace; it now explains what is holding the port and what to do about it.
-
31652d4: Add
cyber-asana auth status— a credential diagnostic that works when the credential does not.user meanswers "who am I to Asana?" and needs a working token to answer at all.auth statusanswers "what will this process authenticate with?", so it reads local state only, never calls the API, and exits0even when nothing is configured (authenticated: false). When an agent hits a401, this is the command that still responds.It names the winning source (
--token>ASANA_ACCESS_TOKEN>ASANA_TOKEN) and lists the sources being shadowed — a stale env var silently overriding a newer one was previously indistinguishable from a bad token. Tokens are shown masked, in text,--json, and--toon. -
7ea5eaf: Add
cyber-asana auth tokenandcyber-asana auth logout.auth tokenprints the stored access token for piping into other tools, and refreshes it first when it is within a minute of expiring — so what it prints is always usable, and the refreshed token is persisted rather than discarded. Text output is the bare token and nothing else, so$(...)substitution works.auth logoutrevokes the grant with Asana and then deletes the local credentials. The order is load-bearing: Asana revokes only refresh tokens, so once the file is gone there is nothing left to revoke with. If revocation fails the credentials are still deleted and the output says the grant may still be live, with the link to remove it.--localskips revocation, and logging out twice reports "not logged in" rather than failing.
0.6.0
Minor Changes
-
4d692be: Make the CLI and MCP server follow the 10 agent-friendly CLI principles:
- Token-efficient output — new
--toonflag emits compact TOON (~40% fewer tokens than JSON); opt-inCYBER_ASANA_MCP_FORMAT=toondoes the same for every MCP tool result. - Minimal default schemas — task lists request only
gid,name,completed,due_onby default (also fixes previously-blank Done/Due columns). - Content truncation — large task notes are truncated with a size hint;
--fullrestores the complete value. - Definitive empty states — empty results print
0 resultsinstead of(none). - Pre-computed aggregates & next steps — task lists print a count summary and follow-up command suggestions (text mode only).
- Structured errors & exit codes — errors are structured under
--json/--toonand map to distinct exit codes (auth, forbidden, not found, rate limited, config). - Content first — running with no arguments shows live data (the authenticated user) instead of help text.
- Consistent help — concise examples in top-level and per-resource
--help.
- Token-efficient output — new
-
c25ad46: Roll the agent-friendly output conventions out from the
tasksdomain to the whole CLI, and fix two contract violations.Fixes:
- Mutation acknowledgements honor
--json/--toon. Deletes and relationship mutations across tasks, projects, goals, tags, sections, status, and portfolios printed prose regardless of the requested format. All 15 sites now emit a structured payload. - The
Next offset:hint is gated to text mode, like the summary and next-step helpers.
Rollout:
- Minimal default schemas — every list command now requests a small default
field set when the caller gives no
--opt-fields. - Count summaries and next-step hints on every list command, not just tasks.
- Truncation applies to project notes, status update bodies, and comment
bodies, so the documented
--fullflag is meaningful beyond task notes. - Empty states name the entity —
0 tasks foundrather than a bare0 results. - Self-correcting usage errors — an unknown flag or subcommand now reports
the flags that command accepts plus a
--helppointer, and exits2. - Idempotent deletes — deleting something already gone succeeds and reports
already_absent: trueinstead of failing with a 404. - Usage examples in every resource group's
--help, andbin,description, andversionlines on the bare-invocation home view. - New
cyber-asana setup hookinstalls a SessionStart hook so an agent session opens with live Asana context. Installing twice is a no-op.
- Mutation acknowledgements honor
Patch Changes
- 8ca4d9f: Update dependencies. Notably
asanato 3.1.12 and@modelcontextprotocol/sdkto 1.30.0. - 3f3607c: Fix
story create --html-textreporting a locally-rejected payload asAsana rejected html_text: .... The local shape check now runs outside the try/catch that wraps the Asana call, so a payload that never left the machine keeps its local error message instead of being attributed to Asana. - 14d1e58: Fix
story listignoring--fullon theTextcolumn. The 60-character cut now goes through the sharedtruncate()helper, so truncated text carries a size hint and--fullprints the whole comment — matching every other free-text field in the package. - 949a8c4: Fix
task subtask listinclude flags (--assignee-email,--follower-emails,--num-subtasks,--custom-fields) suppressing the defaultgid,name,completed,due_onfield set, which left the Name/Done/Due columns blank. The flags now add to the field set — the caller's--opt-fieldswhen given, the default when not. - 877f348: Fix
task createsending followers twice. The followers already ride thePOST /taskscreate body, so the extraaddFollowersrequest was redundant — it is gone. Creating a task with followers now makes exactly one round trip and returns the create response instead of the follower-addition response.
0.5.0
Minor Changes
-
a27ba3d: feat: add status updates and portfolio items via REST
Adds an
asana_status_*toolset (list, get, create, delete) for status updates on projects, portfolios, and goals, plusasana_portfolio_item_listfor listing the items in a portfolio. Both are also available asstatusandportfolio itemsCLI commands. These close the official MCP gaps forget_status_overview,create_project_status_update, andget_items_for_portfolio. -
e3870df: feat(tasks): return html_notes by default and guide agents to use it
0.4.1
Patch Changes
- aff1e0d: Fix polynomial ReDoS vulnerability in HTML tag validation regex.
0.4.0
Minor Changes
- 042e2ca: Add
config listsubcommand as an alias forconfig show.
0.3.2
Patch Changes
- e1cc84d: Fix the preferred cyber-asana token variable name to
ASANA_ACCESS_TOKEN, while keepingASANA_TOKENas a deprecated fallback.
0.3.1
Patch Changes
- 35769bc: Prefer
ASANA_ASSESS_TOKENandASANA_WORKSPACE_GIDfor cyber-asana configuration, while keepingASANA_TOKENandASANA_WORKSPACEas deprecated fallbacks.
0.3.0
Minor Changes
- f9e1dcc: Add
url parseCLI command andasana_url_parseMCP tool to extract workspace and project GIDs from Asana URLs without API calls. - 5607d24: Add repo project registry (
.agents/cyber-asana.json) withconfigCLI commands for add, resolve, sync, and show. Generalize create-asana-task skill for task creation with or without URLs.
0.2.0
Minor Changes
-
952965b: Add batch task lookup by GID via Asana's
/batchAPI, including MCP support and manual system tests. -
6b3e45f: Add
task my-tasksCLI command andasana_task_my_tasksMCP tool to list the authenticated user's My Tasks. Supports--completed-since, pagination, and--opt-fields. -
2f073b2: Add pagination and
opt_fieldsoptions to list commands and MCP list tools. -
3a79c4e: Add
project searchandasana_project_searchsupport for searching projects by name pattern and Asana project search filters. -
51d5d86: Add
project countsCLI andasana_project_countsMCP support for project task counts. -
2f614ac: Add subtask list and create operations:
task subtask-list <task-gid>CLI command,task subtask-create <task-gid> <name>CLI command,asana_task_subtask_listMCP tool, andasana_task_subtask_createMCP tool. -
e2b5805: Add injectable
storiesandtagsgateways plus shared-client composition helpers for cleaner testing and runtime wiring. -
827b42a: Add default list page sizes and fetch-all pagination options with page caps.
-
dceb4f2: Add richer project create and update fields, including rich notes, privacy, default view, and explicit date clearing for projects and tasks.
-
0645b1c: Expand
task searchfilters to cover the full Asana search API:.notand.allvariants for assignee, project, section, and tag; portfolio, follower, created-by, assigned-by, liked-by, and commented-on-by filters; and date-range filters for due, start, created, completed, and modified dates. All new filters are exposed in both the CLI and the MCP tool. -
0061a5d: Add task create and update support for HTML notes, parent relationships, resource subtype, custom fields, follower management, and multi-project creation.
-
8469089: Add
create*Apifactory functions and*Gatewaytypes for all remaining domains (workspaces, sections, users, teams, attachments, portfolios, goals, tasks, projects).Each domain now exposes a
create*Api(gateway)factory that accepts an injected gateway, enabling use without the Asana SDK or real API calls. The Asana-backedcreateAsana*Gateway(client)factory is also exported for runtime composition. -
d72e6bc: Add
createRuntimeContext,registerCliCommands, andregisterMcpToolsfor composing a shared Asana client across all domain APIs. Fix CLI and MCP to report the package version frompackage.json. -
4850ead: Add tag update/delete, task-tag relationship commands, and formatted story comment support with
html_textvalidation.
0.1.0
Minor Changes
-
b6a34b0: Initial release of
cyber-asana— an Asana CLI and MCP server for AI agents.CLI (
cyber-asana <resource> <action>):- Covers all major Asana resources: workspaces, projects, tasks, sections, users, teams, portfolios, goals, tags, attachments, and stories
- Human-readable output by default (key-value for single items, table for lists); pass
--jsonfor raw API JSON ASANA_TOKENenv var for authentication;--tokenflag overrides per invocationASANA_WORKSPACEenv var for default workspace GID;--workspaceflag overrides per invocation- Clear setup instructions printed when
ASANA_TOKENis missing
MCP server (
node dist/mcp.js, stdio transport):- Exposes all CLI operations as MCP tools named
asana_<resource>_<action> - Drop-in for any MCP-compatible AI agent host