feat: add update-issue command for editing title/description#19
feat: add update-issue command for editing title/description#19dandaka wants to merge 3 commits into
Conversation
wrsmith108
left a comment
There was a problem hiding this comment.
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
helpblock inlinear-ops.ts(only toSKILL.md). Users runningnpm run ops -- helpwon'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 founddoesn't explain why. With the multi-match fix,[ERROR] Multiple issues match #123: ENG-123, SMI-123 — pass full identifierwould 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$EDITORwould meaningfully help the "expand short issue bodies" use case (acceptable as a follow-up).
Low issues
- Type safety:
updateInput: Record<string, string>discardsIssueUpdateInputtyping. A literalfield === '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!
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.
c2c2254 to
0af0069
Compare
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Thanks for the thorough review! Rebased onto current main and addressed all requested changes. Point-by-point: Critical / High — all addressed1. Cross-team identifier collision — Fixed. 2. No confirmation before destructive overwrite — Fixed. In non-TTY mode, 3. Bypasses AC-enforcement validator — Fixed. 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
Additional improvements
Residual notes (not blocking)
CI green, typecheck/lint clean, 88/88 tests pass. Ready for re-review. |
Summary
Adds a new
update-issuecommand tolinear-opsthat edits an existing issue'stitleordescriptionvia the Linear SDK.ENG-123or bare123)titleordescriptionas the field (rejects unknown fields with a non-0 exit)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, andstatus.Usage
Changes
scripts/linear-ops.ts— newupdate-issuecommand + help text entrySKILL.md— adds examples to the Common Operations sectionscripts/__tests__/smoke.test.ts— two new smoke tests:update-issuewith no args exits non-0update-issuewith an unknown field exits non-0Follows existing file conventions (uses
requireClient(),console.error+process.exit(1)for arg validation, matching other commands likedone/set-parent).Test plan
npm run typecheckpassesnpm run lintpassesnpm test— 9/9 smoke tests pass (including 2 new ones)npm run buildsucceedsLINEAR_API_KEY: update a title, update a description, try an unknown field