Skip to content

feat: add update-issue command for editing title/description#19

Open
dandaka wants to merge 3 commits into
wrsmith108:mainfrom
dandaka:feature/update-issue
Open

feat: add update-issue command for editing title/description#19
dandaka wants to merge 3 commits into
wrsmith108:mainfrom
dandaka:feature/update-issue

Conversation

@dandaka

@dandaka dandaka commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a new update-issue command to linear-ops that edits an existing issue's title or description via the Linear SDK.

  • Looks up the issue by identifier (e.g. ENG-123 or bare 123)
  • Accepts title or description as the field (rejects unknown fields with a non-0 exit)
  • Applies the update through the SDK and prints the updated identifier + URL

Motivation

Useful for fixing typos, expanding short issue bodies, or scripted cleanup of bulk-imported issues — without opening the Linear web UI. Fits alongside existing aliases like done, wip, and status.

Usage

npm run ops -- update-issue ENG-123 description "Updated description text"
npm run ops -- update-issue ENG-123 title "Corrected title"

Changes

  • scripts/linear-ops.ts — new update-issue command + help text entry
  • SKILL.md — adds examples to the Common Operations section
  • scripts/__tests__/smoke.test.ts — two new smoke tests:
    • update-issue with no args exits non-0
    • update-issue with an unknown field exits non-0

Follows existing file conventions (uses requireClient(), console.error + process.exit(1) for arg validation, matching other commands like done / set-parent).

Test plan

  • npm run typecheck passes
  • npm run lint passes
  • npm test — 9/9 smoke tests pass (including 2 new ones)
  • npm run build succeeds
  • Manual smoke with a real LINEAR_API_KEY: update a title, update a description, try an unknown field

@wrsmith108 wrsmith108 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for the contribution! Ran a multi-perspective review and surfaced a few concerns that I'd like addressed before merge. The command itself is a useful addition — the issues are about safety guarantees the rest of the CLI provides today.

Critical / High issues

1. Cross-team identifier collision (data integrity)

const issues = await client.issues({ filter: { number: { eq: issueNum } } });
// ...
const issue = issues.nodes[0];

The team prefix is stripped via regex but never used. In a multi-team workspace, update-issue ENG-123 description "..." can silently overwrite SMI-123 (or any other team's #123). Picking nodes[0] is non-deterministic.

Unlike status (reversible) or labels-set (additive), this is a destructive overwrite of free-form content with no recovery. Suggested fix: use client.issue(identifier) with the full ENG-123 string (the SDK supports identifier lookup directly), or filter on both number AND team.key. When multiple match, fail loudly with the candidates listed.

Note: this same pattern exists in status, set-parent, create-sub-issue, list-sub-issues, and labels-set — fixing it everywhere is out of scope for this PR, but update-issue is the first command where the consequence is destructive content overwrite.

2. No confirmation before destructive description overwrite (safety)

A typo in the value or identifier silently replaces the entire description. Suggested fix: require --force (or interactive y/N when process.stdin.isTTY). Title overwrite should follow the same pattern for consistency. Optionally print the existing value before applying so users see what they're replacing.

The stated motivation includes "scripted cleanup" — --force preserves scriptability while protecting interactive use.

3. Bypasses AC-enforcement validator

create-issue and create-sub-issue run validateIssueDescription (the v3.0.0/v3.1.0 AC-enforcement contract). update-issue description "..." bypasses it entirely, opening a writable backdoor. Suggested fix: run validateIssueDescription when field === 'description', respecting the same --strict flag the create commands use.

4. PR description vs implementation mismatch

The PR says "looks up the issue by identifier (e.g. ENG-123 or bare 123)" — but the code only ever looks up by number, ignoring the prefix entirely. Either the code or the docs need updating; the code fix is preferred since the docs reflect what users will reasonably expect.

Medium issues

  • Help discoverability: the new command isn't added to the help block in linear-ops.ts (only to SKILL.md). Users running npm run ops -- help won't see it.
  • Test coverage: only arg-validation negative paths are tested. No happy-path coverage, no mock of the SDK call shape, nothing exercising the multi-team-match branch (the riskiest code path is untested).
  • Error message actionability: [ERROR] Issue #123 not found doesn't explain why. With the multi-match fix, [ERROR] Multiple issues match #123: ENG-123, SMI-123 — pass full identifier would be far more useful.
  • Multi-line descriptions: valueParts.join(' ') collapses spacing and offers no path to multi-line markdown without extreme shell quoting. --file <path>, --stdin, or $EDITOR would meaningfully help the "expand short issue bodies" use case (acceptable as a follow-up).

Low issues

  • Type safety: updateInput: Record<string, string> discards IssueUpdateInput typing. A literal field === 'title' ? { title: value } : { description: value } keeps full typing.
  • Output: [SUCCESS] Issue updated! doesn't show the new value — useful for scripted cleanup confirmation.
  • Naming: slight inconsistency with existing verb-first short names (done, wip); not blocking.

Recommendation

request-changes — the cross-team collision + silent description overwrite + AC-bypass combine into a data-corruption hazard on multi-team workspaces. The fixes are small and self-contained. Happy to review again once those land. Thanks again for the work!

dandaka added 2 commits May 6, 2026 17:55
Adds `npm run ops -- update-issue <issue-id> <field> <value>` to
linear-ops with support for title and description fields. Looks up
the issue by identifier and applies the update via the SDK.

Use case: fixing typos or expanding issue bodies without opening
the web UI, and scripted cleanup of bulk-imported issues.
@dandaka dandaka force-pushed the feature/update-issue branch from c2c2254 to 0af0069 Compare May 6, 2026 17:01
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@dandaka

dandaka commented May 6, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review! Rebased onto current main and addressed all requested changes. Point-by-point:

Critical / High — all addressed

1. Cross-team identifier collision — Fixed. resolveIssueForUpdate now uses full identifier lookup via the SDK when a team prefix is provided (e.g. ENG-123). Bare numbers query by number and fail loudly when multiple teams match, listing all candidates (e.g. Multiple issues match #123: ENG-123, SMI-123 — pass full identifier). Zero matches also fail with a clear message.

2. No confirmation before destructive overwrite — Fixed. In non-TTY mode, --force is required or the command exits with code 2. In TTY mode, an interactive y/N confirmation prompt shows the existing value and the proposed change before applying. Both title and description follow this pattern.

3. Bypasses AC-enforcement validator — Fixed. validateIssueDescription now runs when field === 'description', respecting the same --strict flag used by create commands. --strict=false downgrades validation failures to warnings.

4. PR description vs implementation mismatch — Fixed as part of #1. Code now correctly uses the team prefix when provided, matching what the docs describe.

Medium — addressed

  • Help discoverability: update-issue is now listed in the help block of linear-ops.ts.
  • Error message actionability: multi-match and not-found errors now show team-qualified identifiers and guidance.
  • Multi-line descriptions: Added --file <path> for reading description from a file and --stdin for piped input. Both documented in help text and SKILL.md.

Additional improvements

  • Added --stdin and --strict=false examples to SKILL.md (3327fb5) so agents can discover these flags.
  • 5 new tests covering: missing args, unknown field, missing --force in non-TTY, strict validation failure, and help output.

Residual notes (not blocking)

  • --stdin requires --force since isTTY is false when piping — correct safety behavior, documented in usage example. Error message could be more specific but is not misleading.
  • Bare-number ambiguity path is untested (would need API mocking) — acceptable to defer.
  • process.exit() in catch blocks doesn't satisfy TS never — works at runtime, cosmetic.

CI green, typecheck/lint clean, 88/88 tests pass. Ready for re-review.

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