feat: add issue create and comment (task 0004) #3
Reference in New Issue
Block a user
Delete Branch "task-0004-issue-create-and-comment"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements
.claude/tasks/0004-issue-create-and-comment.md.What was built
The first mutations —
issue createandissue comment— plus the shared machinery thatpr create(task 0010) and the issue edit/close slices will reuse:src/body-source.ts—--bodyvs--body-fileresolution (mutually exclusive).src/lookup.ts— label and milestone name→ID resolution, case-insensitive.parseFlags— arepeatableflag kind, accumulating into a separatelistsmap so--labelcan repeat without changing the type of the single-valuedflagsmap.fields.ts— thejoinedarray-join extractor andselectExtraFieldsfor--fields.issue createemitsissue: { number, title, state, url };issue comment <n>echoes the created comment from the POST response with the body cleaned and truncated at 800 chars.Deviations
GET /labels, but that endpoint pages (default 30), so a repo with more labels than one page would fail to resolve a perfectly valid name. It now pages at 50 until exhausted, with a 1000-label runaway guard.issue commentalso accepts--full(not in the spec's flag list). The shared truncation hint literally reads "use--fullto see complete body"; without the flag, that hint names a command that errors out. Documented inissue comment --help.issue viewsuggestion after commenting on a PR.issue commentis deliberately permissive toward PR numbers, butissue viewtype-guards them — so the obvious next-step suggestion would have been a command guaranteed to fail. The PR case is detected from the response'spull_request_url(no extra call).pr viewbecomes the right suggestion once task 0009 lands.--fieldsis additive, not a replacement: the spec calls these "extra fields available via--fields", so the defaults always render and--fieldsappends.Verification
127 unit/integration tests pass; coverage 95.1% statements / 89.9% branches, above the configured thresholds.
Whether Gitea's milestone
?name=filter is itself case-insensitive is the one assumption fixtures cannot settle, so the end-to-end tier now seeds a mixed-case label and milestone and passes both in a different case. If live behaviour differs from the fixtures, CI fails rather than a user discovering it.Review
Risk
Overall: MEDIUM
parseFlagsand the fixture server sit under all commands and all existing tests.--body-filereads a caller-supplied path (expected for a local CLI).Unaddressed findings
src/commands/issue.tsis now ~540 lines holding four subcommands. Left as-is: tasks 0005–0007 add five more, and a split intosrc/commands/issue/<subcommand>.tsis better done as its own refactor than smuggled into a feature task. Flagged in the task's Implementation Notes.Introduce the first mutations, along with the shared machinery the later issue and PR mutation slices reuse. - `issue create` with --title/--body/--body-file/--assignee/--label/ --milestone/--fields, emitting `issue: { number, title, state, url }` - `issue comment <n>`, echoing the created comment from the POST response with the body cleaned and truncated at 800 chars - body-source resolution (--body vs --body-file), label and milestone name->ID lookup, repeatable flags, and the `joined`/`selectExtraFields` field extractors Label lookup pages until exhausted, since a repo with more labels than one page would otherwise fail to resolve a valid name. The end-to-end tier seeds a mixed-case label and milestone and passes both in a different case, so the case-insensitive lookup is verified against live Gitea rather than only against fixtures.