Skip to content

feat: add --assignee flag to task search#10

Open
nick-preda wants to merge 1 commit intotriptechtravel:mainfrom
nick-preda:feat/assignee-flag
Open

feat: add --assignee flag to task search#10
nick-preda wants to merge 1 commit intotriptechtravel:mainfrom
nick-preda:feat/assignee-flag

Conversation

@nick-preda
Copy link
Copy Markdown

Problem

There is no way to search tasks by assignee across the workspace. To find all tasks assigned to a team member, users must either know specific list IDs or manually filter JSON output — neither is practical from the CLI.

Solution

Add an --assignee flag to task search that accepts a name, username, or numeric ID.

Assignee resolution

The flag resolves the input against workspace members in this order:

  1. Numeric ID — used directly (with display name lookup for the status message)
  2. Exact username — case-insensitive match
  3. Substring — case-insensitive partial match on username
$ clickup task search --assignee Michela
  searching tasks assigned to Michela Dall'Armi (ID 42547184)...

$ clickup task search --assignee 42547184
  searching tasks assigned to Michela Dall'Armi (ID 42547184)...

Invalid names produce a clear error:

$ clickup task search --assignee INVENTATO
no workspace member matching "INVENTATO"

Combined with query

When both --assignee and a query are provided, tasks are fetched by assignee first, then filtered client-side by name/description:

$ clickup task search "bug" --assignee Michela

Standalone usage

When only --assignee is provided (no query), all tasks for that person are returned:

$ clickup task search --assignee Michela
$ clickup task search --assignee Michela --json

The command now accepts 0 or 1 positional args (was exactly 1). Validation ensures at least a query or --assignee is provided.

Implementation details

  • resolveAssigneeID fetches workspace members via GetTeams and resolves input to a resolvedMember{Username, ID}
  • searchByAssignee uses the existing fetchTeamTasks with assignees[]={id} param (up to 10 pages)
  • --assignee takes priority over --space/--folder to avoid silent flag conflicts
  • Uses strconv.Atoi (not fmt.Sscanf) for strict numeric detection

Test plan

  • --assignee Michela — resolves by partial name, lists tasks
  • --assignee 42547184 — resolves by numeric ID, shows display name
  • --assignee "mich" — substring match works
  • --assignee INVENTATO — clear error message
  • No args at all — error: "either a search query or --assignee is required"
  • --assignee Michela --json — valid JSON output
  • "persi" --assignee Michela — query + assignee combined filtering
  • go test -race ./... — no data races

🤖 Generated with Claude Code

Resolves assignee by name, username, or numeric ID (case-insensitive
substring match). Fetches up to 10 pages of tasks assigned to that
person via the team-task API. If a query is also provided, results
are filtered client-side by name/description.

Examples:
  clickup task search --assignee Michela
  clickup task search "bug" --assignee 42547184

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@isaacrowntree isaacrowntree left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review: --assignee flag for task search

Note (v0.21.0): Main has been significantly refactored since this PR was opened. You will need to rebase against main. Key changes:

  • search.go: dedupScored refactored (index-based), searchTaskComments uses bounded concurrency (5-worker pool), all hardcoded URLs replaced with client.URL()/client.BaseURL()
  • Test infrastructure available at internal/testutil/ — new features should include tests

Core approach is solid — using fetchTeamTasks with assignees[] param is the right call. A few things to address before merging:

Must Fix

Rebase against main. search.go has significant refactors (dedup algorithm, concurrent comment search, URL replacement). This PR will have merge conflicts.

No tests for new code. resolveAssigneeID has three distinct match paths (numeric, exact username, substring) — this is very testable. Update: internal/testutil/ is now available on main with TestFactory, httptest.Server, and RunCommand helpers. Use these for integration tests. Extract the matching logic into a standalone function that takes []resolvedMember + input string, and cover:

  • exact numeric ID match
  • numeric ID not in member list (see below)
  • exact username match (case-insensitive)
  • substring username match
  • ambiguous substring (multiple matches)
  • no match

--assignee silently ignores --space/--folder. Because the assignee check runs before the space/folder check, combining them silently drops the scope filter. Either return an error when both are provided, or wire the assignees[]=ID param into searchViaSpaces.

Use client.URL()/client.BaseURL() for all raw HTTP calls. Hardcoded https://api.clickup.com/api/v2/... URLs have been removed from main. This PR needs to follow the same pattern.

Should Fix

Reuse searchLevel instead of reimplementing the pagination loop. searchByAssignee with a query duplicates the page-fetch + filterTasks + comment-search pattern that searchLevel already handles. When opts.query != "", you could just call:

return searchLevel(ctx, client, teamID, strings.ToLower(opts.query),
    assigneeParam, 10, opts.comments, ios)

Numeric ID fallback trusts unvalidated input. If a numeric ID isn't found in the member list, resolveAssigneeID silently returns it as-is. This should error — if the user gave an ID not in the workspace, it's likely a mistake.

Ambiguous substring matches are silent. If "mi" matches both "michela" and "mike", first wins with no indication. Collect all substring matches and error with "ambiguous match, did you mean: michela, mike?" if >1.

Nice to Have

  • cmd.Use still says search <query> but the arg is now optional — update to search [query]
  • resolveAssigneeID re-reads config via f.Config() to get workspace ID, but teamID is already resolved in doSearch — pass it through to avoid the redundant call
  • No-query path tags all results as matchSubstring which is semantically misleading — consider a matchAssignee kind

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants