Add YouTrack pull/push sync with project settings and time tracking.
CI / test-and-build (push) Failing after 1m19s
CI / demo (push) Skipped

Wire manual Y menu and CLI sync for issues and work items, persist
per-project credentials, and keep local edits until an explicit push.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-24 00:17:51 +02:00
co-authored by Cursor
parent 19ae1f1b62
commit 98d2253f44
23 changed files with 2874 additions and 44 deletions
+94
View File
@@ -0,0 +1,94 @@
package views
import (
"fmt"
"strings"
"unicode"
)
// SyncAction is a YouTrack sync menu choice.
type SyncAction string
const (
SyncActionPull SyncAction = "pull"
SyncActionPush SyncAction = "push"
)
// SyncOption is one row in the sync menu.
type SyncOption struct {
Action SyncAction
Label string
Shortcut rune
}
// AllSyncOptions is the ordered sync menu.
var AllSyncOptions = []SyncOption{
{Action: SyncActionPull, Label: "Pull from YouTrack", Shortcut: 'p'},
{Action: SyncActionPush, Label: "Push to YouTrack", Shortcut: 'u'},
}
// SyncMenu renders a pull/push picker overlay.
type SyncMenu struct {
Cursor int
Width int
Styles StatusMenuStyles
}
// NewSyncMenu creates a sync action menu.
func NewSyncMenu(width int, styles StatusMenuStyles) SyncMenu {
return SyncMenu{Cursor: 0, Width: width, Styles: styles}
}
// MoveDown moves the selection down.
func (m *SyncMenu) MoveDown() {
if m.Cursor < len(AllSyncOptions)-1 {
m.Cursor++
}
}
// MoveUp moves the selection up.
func (m *SyncMenu) MoveUp() {
if m.Cursor > 0 {
m.Cursor--
}
}
// JumpToShortcut selects the option whose shortcut matches r.
func (m *SyncMenu) JumpToShortcut(r rune) bool {
r = unicode.ToLower(r)
for i, opt := range AllSyncOptions {
if opt.Shortcut == r {
m.Cursor = i
return true
}
}
return false
}
// Selected returns the highlighted action.
func (m SyncMenu) Selected() SyncAction {
if m.Cursor < 0 || m.Cursor >= len(AllSyncOptions) {
return SyncActionPull
}
return AllSyncOptions[m.Cursor].Action
}
// View renders the sync menu.
func (m SyncMenu) View() string {
var lines []string
lines = append(lines, m.Styles.Title.Render("YouTrack"))
for i, opt := range AllSyncOptions {
entry := fmt.Sprintf("%c %s", opt.Shortcut, opt.Label)
if i == m.Cursor {
lines = append(lines, m.Styles.Selected.Render("▸ "+entry))
} else {
lines = append(lines, m.Styles.Normal.Render(" "+entry))
}
}
body := strings.Join(lines, "\n")
w := m.Width
if w < 20 {
w = 20
}
return m.Styles.Box.Width(min(44, w-2)).Render(body)
}