Add YouTrack pull/push sync with project settings and time tracking.
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:
@@ -134,6 +134,8 @@ func (s *JSONStore) Update(t Ticket) error {
|
||||
for i := range tickets {
|
||||
if tickets[i].ID == t.ID {
|
||||
t.CreatedAt = tickets[i].CreatedAt
|
||||
// Always bump UpdatedAt on local edits so NeedsPush sees dirty tickets.
|
||||
// Use MarkSynced after a successful remote sync to clear the dirty flag.
|
||||
t.UpdatedAt = time.Now().UTC()
|
||||
tickets[i] = t
|
||||
found = true
|
||||
@@ -146,6 +148,42 @@ func (s *JSONStore) Update(t Ticket) error {
|
||||
return s.writeUnlocked(t.ProjectID, tickets)
|
||||
}
|
||||
|
||||
// MarkSynced sets SyncedAt = UpdatedAt without changing UpdatedAt.
|
||||
func (s *JSONStore) MarkSynced(id string) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if id == "" {
|
||||
return fmt.Errorf("ticket id is required")
|
||||
}
|
||||
|
||||
projects, err := os.ReadDir(filepath.Join(s.dataDir, "projects"))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return fmt.Errorf("ticket %s: not found", id)
|
||||
}
|
||||
return fmt.Errorf("list projects: %w", err)
|
||||
}
|
||||
|
||||
for _, p := range projects {
|
||||
if !p.IsDir() {
|
||||
continue
|
||||
}
|
||||
tickets, err := s.readUnlocked(p.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range tickets {
|
||||
if tickets[i].ID != id {
|
||||
continue
|
||||
}
|
||||
tickets[i].SyncedAt = tickets[i].UpdatedAt
|
||||
return s.writeUnlocked(p.Name(), tickets)
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("ticket %s: not found", id)
|
||||
}
|
||||
|
||||
// Move relocates a ticket to another project directory.
|
||||
func (s *JSONStore) Move(id, toProjectID string) (Ticket, error) {
|
||||
s.mu.Lock()
|
||||
|
||||
Reference in New Issue
Block a user