areibman / areibman/bottleneck
Feature: Full issue management - change status, add labels, assign people, and more
- Dominant language
- TypeScript
- Stars
- 156
- Forks
- 21
- PR merge metrics
- No merged PRs in 30d
Description
## Feature Request
Add comprehensive issue management capabilities to allow users to perform all common issue operations directly from the app, including changing status, adding/removing labels, assigning people, setting milestones, and more.
## Description
Currently, users may need to go to GitHub to perform many issue management tasks. The app should provide full issue management capabilities similar to what's available on GitHub's web interface.
## Core Features
### 1. Issue Status Management
```typescript
interface IssueActions {
// Status changes
close: () => Promise;
reopen: () => Promise;
lock: (reason?: LockReason) => Promise;
unlock: () => Promise;
pin: () => Promise;
unpin: () => Promise;
markAsCompleted: () => Promise;
markAsNotPlanned: () => Promise;
convertToPR: () => Promise;
transferToRepo: (targetRepo: string) => Promise;
}
function IssueStatusControls({ issue }) {
return (
{/* Primary Status */}
Status
{issue.state === 'open' ? (
closeIssue(issue, 'completed')}
>
✅ Close as completed
closeIssue(issue, 'not_planned')}
>
❌ Close as not planned
) : (
reopenIssue(issue)}
>
🔄 Reopen issue
)}
{/* Lock/Unlock */}
{issue.locked ? (
unlockIssue(issue)}>
🔓 Unlock conversation
) : (
setShowLockDialog(true)}>
🔒 Lock conversation
{showLockDialog && (
lockIssue(issue, reason)}
reasons={[
'off-topic',
'too heated',
'resolved',
'spam'
]}
/>
)}
)}
{/* Pin/Unpin */}
issue.pinned ? unpinIssue(issue) : pinIssue(issue)}>
{issue.pinned ? '📌 Unpin issue' : '📍 Pin issue'}
);
}
```
### 2. Label Management
```typescript
interface LabelManager {
current: Label[];
available: Label[];
add: (labels: string[]) => Promise;
remove: (labels: string[]) => Promise;
create: (label: NewLabel) => Promise;
edit: (label: Label) => Promise;
}
function IssueLabelManager({ issue }) {
const [labels, setLabels] = useState(issue.labels);
const [showLabelPicker, setShowLabelPicker] = useState(false);
const [searchTerm, setSearchTerm] = useState('');
const [creating, setCreating] = useState(false);
return (
Labels
setShowLabelPicker(!showLabelPicker)}
>
⚙️
{labels.map(label => (
{label.name}
removeLabel(label)}
>
×
))}
setShowLabelPicker(true)}
>
+ Add label
{showLabelPicker && (
setSearchTerm(e.target.value)}
className="label-search"
/>
{/* Suggested Labels */}
Suggested
{suggestedLabels.map(label => (
))}
{/* All Labels */}
All Labels
{filteredLabels.map(label => (
))}
{/* Create New Label */}
setCreating(true)}>
+ Create new label
{creating && (
setCreating(false)}
/>
)}
Apply labels
setShowLabelPicker(false)}>Cancel
)}
);
}
```
### 3. Assignment Management
```typescript
interface AssigneeManager {
current: User[];
suggestions: User[];
recentAssignees: User[];
add: (users: User[]) => Promise;
remove: (users: User[]) => Promise;
requestReview: (user: User) => Promise;
}
function IssueAssignees({ issue }) {
const [assignees, setAssignees] = useState(issue.assignees);
const [showPicker, setShowPicker] = useState(false);
const [searchTerm, setSearchTerm] = useState('');
return (
Assignees
setShowPicker(!showPicker)}>
⚙️
{assignees.length === 0 ? (
No one assigned
) : (
assignees.map(user => (
{user.login}
removeAssignee(user)}>×
))
)}
setShowPicker(true)}
>
+ Add assignees
{showPicker && (
{
addAssignees(users);
setShowPicker(false);
}}
suggestions={[
{ title: 'Suggested', users: suggestedAssignees },
{ title: 'Recent', users: recentAssignees },
{ title: 'Team', users: teamMembers }
]}
/>
)}
{/* Quick Actions */}
assignToMe()}>
Assign myself
clearAllAssignees()}>
Clear assignees
);
}
```
### 4. Project and Milestone Management
```typescript
function IssueProjectsAndMilestones({ issue }) {
return (
{/* Projects */}
Projects
{issue.projects.map(project => (
{project.name}
{project.column}
removeFromProject(project)}>×
))}
addToProject(project, column)}
/>
{/* Milestone */}
Milestone
{issue.milestone ? (
{issue.milestone.title}
{issue.milestone.closed_issues}/{issue.milestone.open_issues + issue.milestone.closed_issues}
removeMilestone()}>×
) : (
No milestone
)}
);
}
```
### 5. Bulk Operations
```typescript
interface BulkOperations {
selectedIssues: Issue[];
actions: {
close: () => Promise;
reopen: () => Promise;
addLabels: (labels: string[]) => Promise;
removeLabels: (labels: string[]) => Promise;
assign: (users: User[]) => Promise;
setMilestone: (milestone: Milestone) => Promise;
archive: () => Promise;
};
}
function IssueBulkActions({ selectedIssues }) {
if (selectedIssues.length === 0) return null;
return (
{selectedIssues.length} issues selected
showBulkEditDialog('status')}>
📝 Change status
showBulkEditDialog('labels')}>
🏷️ Edit labels
showBulkEditDialog('assignees')}>
👥 Assign
showBulkEditDialog('milestone')}>
🎯 Set milestone
showBulkEditDialog('project')}>
📊 Add to project
Clear selection
);
}
```
### 6. Issue Templates and Quick Actions
```typescript
function IssueQuickActions({ issue }) {
return (
convertToDiscussion(issue)}>
💬 Convert to discussion
createBranchFromIssue(issue)}>
🌿 Create branch
generatePRTemplate(issue)}>
📝 Generate PR from issue
linkPR(issue)}>
🔗 Link pull request
duplicateIssue(issue)}>
📋 Duplicate issue
moveToRepo(issue)}>
📦 Transfer to another repo
createSubIssues(issue)}>
➕ Create sub-issues
exportIssue(issue)}>
💾 Export as markdown
);
}
```
### 7. Issue Sidebar (GitHub-like)
```typescript
function IssueSidebar({ issue }) {
return (
Notifications
Participants
Development
Timeline
showTimeline(issue)}>
View timeline
Danger Zone
deleteIssue(issue)}>
🗑️ Delete issue
);
}
```
### 8. API Implementation
```typescript
class IssueManager {
async updateIssue(issueNumber: number, updates: IssueUpdate) {
return await this.octokit.issues.update({
owner: this.owner,
repo: this.repo,
issue_number: issueNumber,
...updates
});
}
async addLabels(issueNumber: number, labels: string[]) {
return await this.octokit.issues.addLabels({
owner: this.owner,
repo: this.repo,
issue_number: issueNumber,
labels
});
}
async removeLabel(issueNumber: number, label: string) {
return await this.octokit.issues.removeLabel({
owner: this.owner,
repo: this.repo,
issue_number: issueNumber,
name: label
});
}
async addAssignees(issueNumber: number, assignees: string[]) {
return await this.octokit.issues.addAssignees({
owner: this.owner,
repo: this.repo,
issue_number: issueNumber,
assignees
});
}
async setMilestone(issueNumber: number, milestoneNumber: number) {
return await this.octokit.issues.update({
owner: this.owner,
repo: this.repo,
issue_number: issueNumber,
milestone: milestoneNumber
});
}
async lockIssue(issueNumber: number, reason?: string) {
return await this.octokit.issues.lock({
owner: this.owner,
repo: this.repo,
issue_number: issueNumber,
lock_reason: reason
});
}
}
```
### 9. Keyboard Shortcuts
```typescript
const issueKeyboardShortcuts = {
'c': 'Close/Open issue',
'l': 'Add labels',
'a': 'Add assignee',
'm': 'Set milestone',
'p': 'Add to project',
'shift+d': 'Duplicate issue',
'shift+t': 'Transfer issue',
'cmd+enter': 'Submit comment',
'e': 'Edit issue title/description',
'x': 'Select/deselect issue (in list view)',
'shift+x': 'Select range'
};
```
### 10. Real-time Updates
```typescript
class IssueRealtimeSync {
subscribeToIssue(issueNumber: number) {
// WebSocket subscription for real-time updates
this.ws.subscribe(`issue:${issueNumber}`, (event) => {
switch (event.type) {
case 'labeled':
this.handleLabelAdded(event);
break;
case 'unlabeled':
this.handleLabelRemoved(event);
break;
case 'assigned':
this.handleAssigned(event);
break;
case 'closed':
this.handleClosed(event);
break;
// ... other events
}
});
}
}
```
## Benefits
- **Full GitHub parity**: All issue management features in one place
- **Improved workflow**: No need to switch to GitHub for common tasks
- **Bulk operations**: Manage multiple issues efficiently
- **Better UX**: Optimized interface for issue management
- **Keyboard driven**: Fast operations with shortcuts
- **Real-time sync**: See changes as they happen
## Acceptance Criteria
- [ ] Can close and reopen issues
- [ ] Can add/remove labels
- [ ] Can create new labels
- [ ] Can assign/unassign people
- [ ] Can set/clear milestones
- [ ] Can add issues to projects
- [ ] Can lock/unlock conversations
- [ ] Can pin/unpin issues
- [ ] Bulk operations work for multiple issues
- [ ] Changes sync in real-time
- [ ] Keyboard shortcuts work
- [ ] Error handling for permission issues
- [ ] Undo/redo for recent actions
- [ ] All changes reflected immediately in UI
- [ ] Works with different repository permissions
## Future Enhancements
- Issue templates support
- Custom fields
- Time tracking
- Issue dependencies
- Automated workflows
- AI-powered label suggestions
- Issue analytics
- Saved filters and views
🤖 Generated with [Claude Code](https://claude.ai/code)
Contributor guide
No contributing guide indexed for this repository
Research direction
The issue names no repository files, tests, or entry points. Review the requested IssueStatusControls, IssueLabelManager, IssueAssignees, IssueProjectsAndMilestones, IssueBulkActions, and IssueQuickActions concepts, then narrow the work into a defined subset. Done is not specified beyond providing the listed GitHub-like operations.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- electron, typescript
- Domain
- developer-experience, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100