Skip to content

feat(issues): add watchers, relations, custom fields, richer filters#103

Merged
aarondpn merged 7 commits into
mainfrom
wt/happy-babbage-cdfac2
May 19, 2026
Merged

feat(issues): add watchers, relations, custom fields, richer filters#103
aarondpn merged 7 commits into
mainfrom
wt/happy-babbage-cdfac2

Conversation

@aarondpn
Copy link
Copy Markdown
Owner

Summary

Closes #77. Expands the issues command group to cover the rest of the Redmine REST surface so common workflows no longer fall back to redmine api or the web UI.

New CLI commands

  • redmine issues watchers list|add|remove — watcher lifecycle on an issue.
  • redmine issues relations list|add|remove — issue relations (blocks/precedes/etc).

New flags on existing commands

  • issues create: --start-date, --due-date, --watcher, --custom-field.
  • issues update: --start-date, --due-date, --custom-field, --private-notes (requires --note).
  • issues list: --author, --priority, --category, --parent, --subproject, --include-subprojects, --is-private, and a raw --filter key=value escape hatch (for date ranges, custom fields cf_X=…, subject text ~login, etc).

Under the hood

  • IssueCreate / IssueUpdate gain start_date, due_date, watcher_user_ids, custom_field_values, private_notes.
  • New RelationService covers POST/DELETE /issues/{id}/relations.json and GET/DELETE /relations/{id}.json.
  • New IssueService.AddWatcher / RemoveWatcher / ListWatchers covering /issues/{id}/watchers.json.
  • parseCustomFieldValues lifted into cmdutil.ParseCustomFieldValues so issues and projects share one implementation.
  • 7 new MCP tools auto-generated from mcpgen annotations: list_issue_watchers, add_issue_watcher, remove_issue_watcher, list_issue_relations, get_issue_relation, create_issue_relation, delete_issue_relation.
  • Existing list_issues, create_issue, update_issue MCP schemas widen with the new optional fields.

Test plan

  • go test ./... — all packages pass (~30s).
  • golangci-lint run — zero issues.
  • go vet ./... and go vet -tags=e2e ./e2e/... — clean.
  • Unit tests cover new ops validators, list filter wiring, watcher and relation subcommands.
  • e2e suite (make e2e-up && make e2e-test && make e2e-down) against Redmine 6.1 — new tests added in e2e/issues_extended_test.go (dates, private notes, watcher lifecycle, relation lifecycle); pipeline will exercise these.

Docs

  • English (docs/src/content/docs/commands/issues.mdx) and Chinese (docs/src/content/docs/zh-cn/commands/issues.mdx) updated with the new flags, the watchers and relations sections, the --filter escape-hatch tip, and the relation-type reference table.
  • skills/redmine-cli/SKILL.md updated to mention the new subcommands.

Compatibility

No breaking changes — every addition is optional. Existing scripts and MCP clients keep working unchanged.

aarondpn added 7 commits May 19, 2026 17:55
Closes #77. Expands the issues command to cover the rest of the Redmine
REST surface that previously required `redmine api` or the web UI.

New CLI commands:
- `issues watchers list|add|remove` — manage watchers on an issue.
- `issues relations list|add|remove` — manage issue relations.

New flags on existing commands:
- `issues create`: `--start-date`, `--due-date`, `--watcher`, `--custom-field`.
- `issues update`: `--start-date`, `--due-date`, `--custom-field`, `--private-notes`.
- `issues list`: `--author`, `--priority`, `--category`, `--parent`,
  `--subproject`, `--include-subprojects`, `--is-private`, `--filter`
  (raw Redmine filter escape hatch).

Internals:
- `IssueCreate` / `IssueUpdate` gain start_date, due_date, watcher_user_ids,
  custom_field_values, private_notes (update only).
- New `RelationService` (`/issues/{id}/relations.json`, `/relations/{id}.json`).
- New `IssueService.AddWatcher` / `RemoveWatcher` / `ListWatchers`.
- `parseCustomFieldValues` lifted into `cmdutil.ParseCustomFieldValues` so
  issues and projects share one implementation.
- 7 new MCP tools auto-generated: list/add/remove_issue_watcher,
  list/get/create/delete_issue_relation.

Tests: unit tests for new ops validators, list filter wiring, watcher and
relation subcommands, plus four e2e tests covering create-with-dates,
private notes, the watcher lifecycle, and the relation lifecycle.

Docs: English + zh-cn `issues.mdx` updated with new flags, the `watchers`
and `relations` sections, the `--filter` escape-hatch tip, and the relation
type reference table.
- models.Journal gains PrivateNotes so issues get --journals carries the
  flag through to JSON output. Without this, the field returned by
  Redmine was dropped during Go decoding.
- e2e MCP golden refreshed to include the three new read-only issue
  tools: get_issue_relation, list_issue_relations, list_issue_watchers.
- update.go: drop the redundant ResolveProject roundtrip when resolving
  --category / --version by name; the issue payload already carries the
  numeric project ID, which Redmine accepts wherever the path takes an
  identifier. Saves one API call per affected update.
- cmdutil: extract ParseKeyValuePairs and reuse it from
  ParseCustomFieldValues and from list.go's --filter handling, deleting
  the duplicate parser.
- ops/relations: replace the 2-entry IssueRelationTypesSupportingDelay
  map with a slice + slices.Contains and delete the bespoke keys()
  helper. Same change for validateRelationType.
- filter_resolution: collapse resolveIssueAssigneeFilter and
  resolveIssueAuthorFilter into a shared resolveIssueUserFilter; the
  named wrappers stay as thin call sites.
- watcher.go / relation.go: render lists via cmdutil.RenderCollection so
  the JSON/CSV/table switch is gone from both new commands.
- list.go: tighten the --query-id rationale comment.
- TestIssues_CustomFieldOnCreate: creates an issue with
  --custom-field "E2E Severity=High" and asserts the custom field round
  trips on the response. Closes the gap where only project create/update
  exercised the --custom-field flag end to end.
- TestIssues_RelationPrecedesWithDelay: creates a 'precedes' relation
  with --delay 5 and asserts Redmine returns the delay. The earlier
  TestIssues_RelationLifecycle only covered 'blocks', which has no delay.
- bootstrap-redmine.sh: set is_for_all=true on the seeded
  'E2E Severity' custom field (and reset its tracker/filter/required
  flags on every bootstrap), so the field auto-applies to ephemeral
  projects created by the e2e fixture without requiring per-project
  attachment via the API.
The seeded "E2E Severity" custom field is now is_for_all=true so it
attaches to every newly-created issue. Its numeric ID and value shape
vary by bootstrap and aren't the schema signal the golden snapshot is
meant to catch, so scrub it alongside the other per-run fields.
The two-positional commands `issues watchers add <issue-id> <user>` and
`issues watchers remove <issue-id> <user>` previously registered a
`RegisterFlagCompletionFunc("user", …)` against a --user flag that
doesn't exist, so tab-completing the user argument did nothing.

Replace the dead-code registration with a ValidArgsFunction that returns
the same CompleteUsers list when completing the second positional and
otherwise falls back to no completion. The first slot (issue-id) is
numeric and unbounded, so it stays uncompleted.
CompleteUsers used to early-return nil completions when listing users
failed (403 for non-admin sessions), so the 'me' shorthand was
unavailable to tab even though the resolver accepts it without an admin
endpoint hit. Hoist 'me' before the API call so non-admin shells still
get a useful suggestion instead of an empty list.
@aarondpn aarondpn merged commit 16eb937 into main May 19, 2026
6 checks passed
@aarondpn aarondpn deleted the wt/happy-babbage-cdfac2 branch May 19, 2026 17:11
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.

Expand issue command coverage for watchers, relations, custom fields, and missing filters

1 participant