@mndrk/agx 2.4.3 → 2.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/cloud-runtime/standalone/apps/local/.next/BUILD_ID +1 -1
  2. package/cloud-runtime/standalone/apps/local/.next/build-manifest.json +2 -2
  3. package/cloud-runtime/standalone/apps/local/.next/prerender-manifest.json +3 -3
  4. package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.html +2 -2
  5. package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.rsc +1 -1
  6. package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  7. package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  9. package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  10. package/cloud-runtime/standalone/apps/local/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  12. package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.html +2 -2
  13. package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.rsc +2 -2
  14. package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  15. package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  16. package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  17. package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  18. package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  19. package/cloud-runtime/standalone/apps/local/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  20. package/cloud-runtime/standalone/apps/local/.next/server/app/agents/[id]/page_client-reference-manifest.js +1 -1
  21. package/cloud-runtime/standalone/apps/local/.next/server/app/agents/page_client-reference-manifest.js +1 -1
  22. package/cloud-runtime/standalone/apps/local/.next/server/app/agents.html +2 -2
  23. package/cloud-runtime/standalone/apps/local/.next/server/app/agents.rsc +2 -2
  24. package/cloud-runtime/standalone/apps/local/.next/server/app/agents.segments/_full.segment.rsc +2 -2
  25. package/cloud-runtime/standalone/apps/local/.next/server/app/agents.segments/_head.segment.rsc +1 -1
  26. package/cloud-runtime/standalone/apps/local/.next/server/app/agents.segments/_index.segment.rsc +2 -2
  27. package/cloud-runtime/standalone/apps/local/.next/server/app/agents.segments/_tree.segment.rsc +2 -2
  28. package/cloud-runtime/standalone/apps/local/.next/server/app/agents.segments/agents/__PAGE__.segment.rsc +1 -1
  29. package/cloud-runtime/standalone/apps/local/.next/server/app/agents.segments/agents.segment.rsc +1 -1
  30. package/cloud-runtime/standalone/apps/local/.next/server/app/api/tasks/[id]/nodes/[nodeId]/comments/route.js +1 -1
  31. package/cloud-runtime/standalone/apps/local/.next/server/app/api/tasks/[id]/nodes/[nodeId]/comments/route.js.nft.json +1 -1
  32. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/auth/route.js +3 -3
  33. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/auth/route.js.nft.json +1 -1
  34. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/callback/route.js +3 -3
  35. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/callback/route.js.nft.json +1 -1
  36. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/groups/route.js +3 -3
  37. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/groups/route.js.nft.json +1 -1
  38. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/items/[id]/activity/route.js +3 -3
  39. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/items/[id]/activity/route.js.nft.json +1 -1
  40. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/items/[id]/recap/route.js +3 -3
  41. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/items/[id]/recap/route.js.nft.json +1 -1
  42. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/items/[id]/route.js +4 -4
  43. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/items/[id]/route.js.nft.json +1 -1
  44. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/items/route.js +3 -3
  45. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/items/route.js.nft.json +1 -1
  46. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/mcp-setup/route.js +3 -3
  47. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/mcp-setup/route.js.nft.json +1 -1
  48. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/options/route.js +3 -3
  49. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/options/route.js.nft.json +1 -1
  50. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/runs/[id]/route.js +1 -1
  51. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/runs/[id]/route.js.nft.json +1 -1
  52. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/runs/route.js +3 -3
  53. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/runs/route.js.nft.json +1 -1
  54. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/runs/scripted/route.js +1 -1
  55. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/runs/scripted/route.js.nft.json +1 -1
  56. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/status/route.js +4 -4
  57. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/status/route.js.nft.json +1 -1
  58. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/token/route.js +3 -3
  59. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/token/route.js.nft.json +1 -1
  60. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/token-receive/route.js +3 -3
  61. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/token-receive/route.js.nft.json +1 -1
  62. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/worker/route.js +6 -6
  63. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/[tracker]/worker/route.js.nft.json +1 -1
  64. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/connections/route.js +7 -2
  65. package/cloud-runtime/standalone/apps/local/.next/server/app/api/trackers/connections/route.js.nft.json +1 -1
  66. package/cloud-runtime/standalone/apps/local/.next/server/app/index.html +2 -2
  67. package/cloud-runtime/standalone/apps/local/.next/server/app/index.rsc +2 -2
  68. package/cloud-runtime/standalone/apps/local/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  69. package/cloud-runtime/standalone/apps/local/.next/server/app/index.segments/_full.segment.rsc +2 -2
  70. package/cloud-runtime/standalone/apps/local/.next/server/app/index.segments/_head.segment.rsc +1 -1
  71. package/cloud-runtime/standalone/apps/local/.next/server/app/index.segments/_index.segment.rsc +2 -2
  72. package/cloud-runtime/standalone/apps/local/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  73. package/cloud-runtime/standalone/apps/local/.next/server/app/page_client-reference-manifest.js +1 -1
  74. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/automations/page_client-reference-manifest.js +1 -1
  75. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/env-vars/page_client-reference-manifest.js +1 -1
  76. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/folders/page_client-reference-manifest.js +1 -1
  77. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/graph/[taskId]/page_client-reference-manifest.js +1 -1
  78. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/objectives/[objectiveId]/page_client-reference-manifest.js +1 -1
  79. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/objectives/page_client-reference-manifest.js +1 -1
  80. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
  81. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/[teamId]/agents/[agentId]/page_client-reference-manifest.js +1 -1
  82. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/[teamId]/page_client-reference-manifest.js +1 -1
  83. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/adopt/page_client-reference-manifest.js +1 -1
  84. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/new/page_client-reference-manifest.js +1 -1
  85. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/page_client-reference-manifest.js +1 -1
  86. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/teams/replace/page_client-reference-manifest.js +1 -1
  87. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/terminal/page_client-reference-manifest.js +1 -1
  88. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/thread/[threadId]/page_client-reference-manifest.js +1 -1
  89. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/tracking/[tracker]/page_client-reference-manifest.js +1 -1
  90. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/tracking/connect/page_client-reference-manifest.js +1 -1
  91. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/[slug]/tracking/page_client-reference-manifest.js +1 -1
  92. package/cloud-runtime/standalone/apps/local/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  93. package/cloud-runtime/standalone/apps/local/.next/server/app/projects.html +2 -2
  94. package/cloud-runtime/standalone/apps/local/.next/server/app/projects.rsc +2 -2
  95. package/cloud-runtime/standalone/apps/local/.next/server/app/projects.segments/_full.segment.rsc +2 -2
  96. package/cloud-runtime/standalone/apps/local/.next/server/app/projects.segments/_head.segment.rsc +1 -1
  97. package/cloud-runtime/standalone/apps/local/.next/server/app/projects.segments/_index.segment.rsc +2 -2
  98. package/cloud-runtime/standalone/apps/local/.next/server/app/projects.segments/_tree.segment.rsc +2 -2
  99. package/cloud-runtime/standalone/apps/local/.next/server/app/projects.segments/projects/__PAGE__.segment.rsc +1 -1
  100. package/cloud-runtime/standalone/apps/local/.next/server/app/projects.segments/projects.segment.rsc +1 -1
  101. package/cloud-runtime/standalone/apps/local/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  102. package/cloud-runtime/standalone/apps/local/.next/server/app/setup.html +2 -2
  103. package/cloud-runtime/standalone/apps/local/.next/server/app/setup.rsc +2 -2
  104. package/cloud-runtime/standalone/apps/local/.next/server/app/setup.segments/_full.segment.rsc +2 -2
  105. package/cloud-runtime/standalone/apps/local/.next/server/app/setup.segments/_head.segment.rsc +1 -1
  106. package/cloud-runtime/standalone/apps/local/.next/server/app/setup.segments/_index.segment.rsc +2 -2
  107. package/cloud-runtime/standalone/apps/local/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
  108. package/cloud-runtime/standalone/apps/local/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +1 -1
  109. package/cloud-runtime/standalone/apps/local/.next/server/app/setup.segments/setup.segment.rsc +1 -1
  110. package/cloud-runtime/standalone/apps/local/.next/server/app/status/page_client-reference-manifest.js +1 -1
  111. package/cloud-runtime/standalone/apps/local/.next/server/app/status.html +2 -2
  112. package/cloud-runtime/standalone/apps/local/.next/server/app/status.rsc +2 -2
  113. package/cloud-runtime/standalone/apps/local/.next/server/app/status.segments/_full.segment.rsc +2 -2
  114. package/cloud-runtime/standalone/apps/local/.next/server/app/status.segments/_head.segment.rsc +1 -1
  115. package/cloud-runtime/standalone/apps/local/.next/server/app/status.segments/_index.segment.rsc +2 -2
  116. package/cloud-runtime/standalone/apps/local/.next/server/app/status.segments/_tree.segment.rsc +2 -2
  117. package/cloud-runtime/standalone/apps/local/.next/server/app/status.segments/status/__PAGE__.segment.rsc +1 -1
  118. package/cloud-runtime/standalone/apps/local/.next/server/app/status.segments/status.segment.rsc +1 -1
  119. package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__0fbbaf2d._.js +1 -1
  120. package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__10a90219._.js +3 -3
  121. package/cloud-runtime/standalone/apps/local/.next/server/chunks/{[root-of-the-server]__2ab23e45._.js → [root-of-the-server]__2eb13697._.js} +2 -2
  122. package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__3a038592._.js +3 -3
  123. package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__498d8b2d._.js +3 -0
  124. package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__68b692c4._.js +9 -9
  125. package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__7fcbb0c2._.js +2 -2
  126. package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__ba72d342._.js +5 -5
  127. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_1a4cb196._.js +1 -1
  128. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_5e5f36c4._.js +1 -1
  129. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_9f0fd451._.js +3 -0
  130. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_e1f4d9ad._.js +1 -1
  131. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_f8b3abae._.js +1 -1
  132. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_lib_orchestrator_chat-processor_ts_46384f36._.js +12 -12
  133. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/[root-of-the-server]__b8484b0e._.js +1 -1
  134. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_5eebb5cb._.js +1 -1
  135. package/cloud-runtime/standalone/apps/local/.next/server/chunks/ssr/apps_local_components_projects_ProjectObjectivesWorkspace_tsx_751ab3d3._.js +7 -7
  136. package/cloud-runtime/standalone/apps/local/.next/server/middleware-manifest.json +5 -5
  137. package/cloud-runtime/standalone/apps/local/.next/server/pages/404.html +2 -2
  138. package/cloud-runtime/standalone/apps/local/.next/server/pages/500.html +2 -2
  139. package/cloud-runtime/standalone/apps/local/.next/server/server-reference-manifest.js +1 -1
  140. package/cloud-runtime/standalone/apps/local/.next/server/server-reference-manifest.json +1 -1
  141. package/cloud-runtime/standalone/apps/local/.next/static/chunks/7148feb61274d431.css +1 -0
  142. package/cloud-runtime/standalone/apps/local/.next/static/chunks/8b91e7692a899f00.js +20 -0
  143. package/cloud-runtime/standalone/apps/local/.next/static/chunks/{6c278ae4535b6d5c.js → 9e2208153f276f8c.js} +1 -1
  144. package/cloud-runtime/standalone/apps/local/.next/static/chunks/d590384ce3045a12.js +5 -0
  145. package/cloud-runtime/standalone/apps/local/app/api/trackers/connections/route.ts +46 -2
  146. package/cloud-runtime/standalone/apps/local/app/projects/[slug]/tracking/connect/page.tsx +41 -11
  147. package/cloud-runtime/standalone/apps/local/components/projects/ProjectObjectivesWorkspace.tsx +204 -49
  148. package/cloud-runtime/standalone/apps/local/hooks/useTrackerConnections.ts +29 -1
  149. package/cloud-runtime/standalone/apps/local/lib/agent-process-registry.ts +8 -2
  150. package/cloud-runtime/standalone/apps/local/lib/tracker/connections.ts +46 -15
  151. package/cloud-runtime/standalone/apps/local/src/prompt-scheduler/objective-worker.ts +7 -0
  152. package/cloud-runtime/standalone/apps/local/src/prompt-scheduler/task-worker.ts +7 -0
  153. package/cloud-runtime/standalone/apps/local/worker/index.js +7 -2
  154. package/package.json +1 -1
  155. package/cloud-runtime/standalone/apps/local/.next/server/chunks/[root-of-the-server]__847236fb._.js +0 -3
  156. package/cloud-runtime/standalone/apps/local/.next/server/chunks/apps_local_80b34b78._.js +0 -3
  157. package/cloud-runtime/standalone/apps/local/.next/static/chunks/cecf3e8c47c785f5.js +0 -5
  158. package/cloud-runtime/standalone/apps/local/.next/static/chunks/e72724fd8225ec39.js +0 -20
  159. package/cloud-runtime/standalone/apps/local/.next/static/chunks/f6d31036588ddfe4.css +0 -1
  160. /package/cloud-runtime/standalone/apps/local/.next/static/{4MnUE2NHztybGBWFbcBo5 → 3-6VaSnGGXdV-2eV44RDK}/_buildManifest.js +0 -0
  161. /package/cloud-runtime/standalone/apps/local/.next/static/{4MnUE2NHztybGBWFbcBo5 → 3-6VaSnGGXdV-2eV44RDK}/_clientMiddlewareManifest.json +0 -0
  162. /package/cloud-runtime/standalone/apps/local/.next/static/{4MnUE2NHztybGBWFbcBo5 → 3-6VaSnGGXdV-2eV44RDK}/_ssgManifest.js +0 -0
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { use, useState } from "react";
4
4
  import Link from "next/link";
5
- import { ArrowLeft, CheckCircle2, LogOut } from "lucide-react";
5
+ import { ArrowLeft, CheckCircle2, LogOut, Star } from "lucide-react";
6
6
  import TrackerSetup from "@/components/tracking/TrackerSetup";
7
7
  import { TrackerIcon } from "@/components/tracking/TrackerIcon";
8
8
  import { useProjects } from "@/hooks/useProjects";
@@ -63,8 +63,15 @@ export default function TrackingConnectPage({
63
63
  const project = projects.find((p) => p.slug === slug);
64
64
  const projectId = project?.id ?? "";
65
65
 
66
- const { connections, loading: connectionsLoading, removeConnection } = useTrackerConnections(projectId);
66
+ const {
67
+ connections,
68
+ defaultTracker,
69
+ loading: connectionsLoading,
70
+ removeConnection,
71
+ setDefaultTracker,
72
+ } = useTrackerConnections(projectId);
67
73
  const connectedTypes = new Set(connections.filter((c) => c.connected).map((c) => c.type));
74
+ const connectedCount = connectedTypes.size;
68
75
 
69
76
  const [selected, setSelected] = useState<string | null>(null);
70
77
 
@@ -132,13 +139,17 @@ export default function TrackingConnectPage({
132
139
  <div className="grid grid-cols-1 sm:grid-cols-2 gap-4 w-full max-w-lg">
133
140
  {TRACKER_TYPES.map((tracker) => {
134
141
  const isAlreadyConnected = connectedTypes.has(tracker.type);
142
+ const isDefault = defaultTracker === tracker.type;
143
+ const canMakeDefault = isAlreadyConnected && connectedCount > 1 && !isDefault;
135
144
  return (
136
145
  <div
137
146
  key={tracker.type}
138
147
  className={
139
148
  "relative flex flex-col items-center gap-4 p-8 rounded-xl border transition-all duration-200 group " +
140
149
  (isAlreadyConnected
141
- ? "border-green-500/20 bg-green-500/5"
150
+ ? (isDefault
151
+ ? "border-yellow-500/40 bg-yellow-500/5"
152
+ : "border-green-500/20 bg-green-500/5")
142
153
  : "border-border hover:border-primary/50 hover:bg-accent/50 hover:shadow-md cursor-pointer active:scale-[0.98]")
143
154
  }
144
155
  onClick={() => !isAlreadyConnected && setSelected(tracker.type)}
@@ -154,19 +165,38 @@ export default function TrackingConnectPage({
154
165
  <div className="flex flex-col items-center gap-1">
155
166
  <span className="text-base font-semibold">{tracker.label}</span>
156
167
  {isAlreadyConnected ? (
157
- <span className="text-xs font-medium text-green-600">Connected</span>
168
+ <span className="inline-flex items-center gap-1 text-xs font-medium text-green-600">
169
+ Connected
170
+ {isDefault && (
171
+ <span className="inline-flex items-center gap-0.5 rounded-full bg-yellow-500/10 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-yellow-700">
172
+ <Star className="h-3 w-3 fill-yellow-500 text-yellow-500" />
173
+ Default
174
+ </span>
175
+ )}
176
+ </span>
158
177
  ) : (
159
178
  <span className="text-xs text-muted-foreground">Click to connect</span>
160
179
  )}
161
180
  </div>
162
181
  {isAlreadyConnected && (
163
- <button
164
- onClick={(e) => { e.stopPropagation(); removeConnection(tracker.type); }}
165
- className="flex items-center gap-1.5 text-xs text-muted-foreground hover:text-destructive transition-colors"
166
- >
167
- <LogOut className="h-3.5 w-3.5" />
168
- Disconnect
169
- </button>
182
+ <div className="flex flex-col items-center gap-1.5">
183
+ {canMakeDefault && (
184
+ <button
185
+ onClick={(e) => { e.stopPropagation(); void setDefaultTracker(tracker.type); }}
186
+ className="flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors"
187
+ >
188
+ <Star className="h-3.5 w-3.5" />
189
+ Set as default
190
+ </button>
191
+ )}
192
+ <button
193
+ onClick={(e) => { e.stopPropagation(); removeConnection(tracker.type); }}
194
+ className="flex items-center gap-1.5 text-xs text-muted-foreground hover:text-destructive transition-colors"
195
+ >
196
+ <LogOut className="h-3.5 w-3.5" />
197
+ Disconnect
198
+ </button>
199
+ </div>
170
200
  )}
171
201
  </div>
172
202
  );
@@ -92,8 +92,9 @@ interface ProjectAgentSummary {
92
92
  routing_order: number;
93
93
  }
94
94
 
95
- interface ObjectiveLinearIssueSummary {
95
+ interface ObjectiveTrackerItemSummary {
96
96
  id: string;
97
+ trackerType: string;
97
98
  identifier: string;
98
99
  title: string;
99
100
  url: string | null;
@@ -103,6 +104,12 @@ interface ObjectiveLinearIssueSummary {
103
104
  labels?: string[];
104
105
  }
105
106
 
107
+ interface TrackerConnectionInfo {
108
+ type: string;
109
+ displayName: string;
110
+ connected: boolean;
111
+ }
112
+
106
113
  const HEALTH_META: Record<
107
114
  ProjectObjectiveHealth,
108
115
  { label: string; chipClass: string; toneClass: string }
@@ -326,13 +333,18 @@ function buildObjectiveChatPrefix(
326
333
  teamName: string | null,
327
334
  projectId: string,
328
335
  projectSlug: string,
329
- appOrigin: string | null
336
+ appOrigin: string | null,
337
+ trackerInfo: {
338
+ available: { type: string; displayName: string }[];
339
+ configured: { type: string; displayName: string }[];
340
+ defaultType: string | null;
341
+ }
330
342
  ): string {
331
343
  const basePath = `/api/projects/${projectId}/objectives/${objective.id}`;
332
344
  const objectiveRoute = appOrigin ? `${appOrigin}${basePath}` : basePath;
333
345
  const scheduledTasksRoute = `${objectiveRoute}/scheduled-tasks`;
334
- const linearIssuesRoute = `${objectiveRoute}/linear-issues`;
335
346
  const notesRoute = `${objectiveRoute}/notes`;
347
+ const trackersBase = appOrigin ? `${appOrigin}/api/trackers` : `/api/trackers`;
336
348
 
337
349
  const objectiveFilePath = `~/.agx/projects/${projectSlug}/objectives/${objective.key}.md`;
338
350
  const notesDir = `~/.agx/projects/${projectSlug}/objectives/${objective.key}/notes/`;
@@ -354,7 +366,7 @@ function buildObjectiveChatPrefix(
354
366
  `## Objective file (source of truth)\n\nThis objective is stored as a frontmatter markdown file at:\n\`${objectiveFilePath}\`\n\nFile format:\n- YAML frontmatter between \`---\` delimiters contains all metadata (title, teamId, key, status, progress, scheduledTaskIds, threadId, chatSessionVersion, createdAt, updatedAt).\n- \`## Activities\` section contains the activity timeline; each activity is a \`### Title\` block with metadata lines (\`- **id:**\`, \`- **source:**\`, \`- **created:**\`, \`- **body:**\`) and optional \`#### Replies\` sub-section.\n\nNotes are stored as separate files in \`${notesDir}\`. Each note is a markdown file with YAML frontmatter (id, title, objectiveId, createdAt, updatedAt) and a markdown body.\n\nWhen updating the objective, you can edit this file directly. Rules:\n- NEVER remove or break the \`---\` frontmatter delimiters.\n- NEVER change the \`id\` or \`createdAt\` fields.\n- Always update \`updatedAt\` to the current ISO timestamp when making changes.\n- After any edit, call \`GET ${validateRoute}\` to verify the file is still valid.\n- If validation fails, fix the errors immediately before doing anything else.`,
355
367
 
356
368
  "Scheduled tasks live in the shared scheduled-task list and are filtered by this objective label.",
357
- "Your job is to help the team develop the strategy needed to reach the goal, including the right combination of objective notes, scheduled tasks, and Linear tickets.",
369
+ "Your job is to help the team develop the strategy needed to reach the goal, including the right combination of objective notes, scheduled tasks, and tracker tickets.",
358
370
  "Use the current session history to build on prior reasoning. Only reset and start from scratch when the user explicitly starts a new session.",
359
371
  "Use this thread to pressure-test strategy, suggest better tactics, rewrite the objective when asked, propose the right operational cadence, and take concrete follow-up actions when the user wants them applied.",
360
372
  "When suggesting edits, prefer editing the frontmatter file directly or using the notes API. When the user asks you to make a change, edit the file, validate, then confirm.",
@@ -367,9 +379,32 @@ function buildObjectiveChatPrefix(
367
379
  `- DELETE ${notesRoute}/{noteId} to delete a note.`,
368
380
  `- GET ${scheduledTasksRoute} to inspect the scheduled tasks already tracked for this objective.`,
369
381
  `- POST ${scheduledTasksRoute} with {"name","prompt","cadence","agentId"} to create a scheduled task for this objective.`,
370
- `- GET ${linearIssuesRoute} to inspect Linear tickets carrying the objective label "${objective.key}".`,
371
- `- POST ${linearIssuesRoute} with {"title","description","teamId","assigneeId","cycleId","stateId","priority"} to create a Linear ticket labeled "${objective.key}".`,
372
382
  `- GET ${validateRoute} to validate the objective file on disk. Returns {"valid":true} or {"valid":false,"errors":[...]}.`,
383
+ "Tracker integrations:",
384
+ `- Available tracker types: ${
385
+ trackerInfo.available.length > 0
386
+ ? trackerInfo.available.map((t) => `${t.displayName} (${t.type})`).join(", ")
387
+ : "none registered"
388
+ }.`,
389
+ `- Configured for this project: ${
390
+ trackerInfo.configured.length > 0
391
+ ? trackerInfo.configured
392
+ .map((t) => {
393
+ const isDefault = trackerInfo.defaultType === t.type;
394
+ return `${t.displayName} (${t.type})${isDefault ? " — default" : ""}`;
395
+ })
396
+ .join(", ")
397
+ : "none — ask the user to connect a tracker in project settings if they want ticket workflows"
398
+ }.`,
399
+ trackerInfo.defaultType
400
+ ? `- Default tracker for ticket workflows: ${trackerInfo.defaultType}. Prefer this tracker when creating or referencing tickets for this objective unless the user asks otherwise.`
401
+ : trackerInfo.configured.length > 1
402
+ ? "- No default tracker has been selected; ask the user which connected tracker to use before creating tickets."
403
+ : "",
404
+ ...trackerInfo.configured.map(
405
+ (t) =>
406
+ `- GET ${trackersBase}/${t.type}/items?projectId=${projectId} to list ${t.displayName} items. Filter client-side by label "${objective.key}" to find tickets tied to this objective.`
407
+ ),
373
408
  ]
374
409
  .filter(Boolean)
375
410
  .join("\n\n");
@@ -385,7 +420,7 @@ interface ObjectiveChatSession {
385
420
 
386
421
  type ObjectiveChatView = "list" | "detail";
387
422
 
388
- type ObjectiveDetailTab = "activity" | "notes" | "linear" | "scheduled-tasks";
423
+ type ObjectiveDetailTab = "activity" | "notes" | "tasks" | "scheduled-tasks";
389
424
 
390
425
  function summarizeSessionTitle(content: string): string {
391
426
  const normalized = content.replace(/\s+/g, " ").trim();
@@ -478,6 +513,9 @@ function ObjectiveChatPanel({
478
513
  projectSlug,
479
514
  objective,
480
515
  teamName,
516
+ trackerConnections,
517
+ availableTrackers,
518
+ defaultTrackerType,
481
519
  onThreadLinked,
482
520
  onObjectiveUpdated,
483
521
  }: {
@@ -485,6 +523,9 @@ function ObjectiveChatPanel({
485
523
  projectSlug: string;
486
524
  objective: ProjectObjective;
487
525
  teamName: string | null;
526
+ trackerConnections: TrackerConnectionInfo[];
527
+ availableTrackers: { type: string; displayName: string }[];
528
+ defaultTrackerType: string | null;
488
529
  onThreadLinked: (threadId: string) => Promise<void>;
489
530
  onObjectiveUpdated: () => Promise<void>;
490
531
  }) {
@@ -959,7 +1000,11 @@ function ObjectiveChatPanel({
959
1000
  const projectParticipantIds = participants.map((participant) => participant.id);
960
1001
  const rootMessageId = isDetailView ? selectedSessionId : null;
961
1002
  const combinedPrefix = [
962
- buildObjectiveChatPrefix(objective, teamName, projectId, projectSlug, appOrigin),
1003
+ buildObjectiveChatPrefix(objective, teamName, projectId, projectSlug, appOrigin, {
1004
+ available: availableTrackers,
1005
+ configured: trackerConnections.filter((c) => c.connected).map((c) => ({ type: c.type, displayName: c.displayName })),
1006
+ defaultType: defaultTrackerType,
1007
+ }),
963
1008
  promptPrefix,
964
1009
  ]
965
1010
  .filter(Boolean)
@@ -1639,8 +1684,10 @@ export function ProjectObjectiveDetail({
1639
1684
  const [savingNoteIds, setSavingNoteIds] = useState<Set<string>>(new Set());
1640
1685
  const [isCreatingNote, setIsCreatingNote] = useState(false);
1641
1686
  const [noteSearch, setNoteSearch] = useState("");
1642
- const [linearIssues, setLinearIssues] = useState<ObjectiveLinearIssueSummary[]>([]);
1643
- const [linearConnected, setLinearConnected] = useState(true);
1687
+ const [trackerItems, setTrackerItems] = useState<ObjectiveTrackerItemSummary[]>([]);
1688
+ const [trackerConnections, setTrackerConnections] = useState<TrackerConnectionInfo[]>([]);
1689
+ const [availableTrackers, setAvailableTrackers] = useState<{ type: string; displayName: string }[]>([]);
1690
+ const [defaultTrackerType, setDefaultTrackerType] = useState<string | null>(null);
1644
1691
  const [workingOnObjective, setWorkingOnObjective] = useState(false);
1645
1692
 
1646
1693
  const { jobs: scheduledJobs } = usePromptJobs(project?.id ?? null, {
@@ -1956,46 +2003,119 @@ export function ProjectObjectiveDetail({
1956
2003
  }, [objective, project?.id, refetchProject]);
1957
2004
 
1958
2005
  useEffect(() => {
1959
- if (!project?.id || !objective?.id) {
1960
- setLinearIssues([]);
1961
- setLinearConnected(true);
2006
+ if (!project?.id || !objective?.id || !objective?.key) {
2007
+ setTrackerItems([]);
2008
+ setTrackerConnections([]);
2009
+ setAvailableTrackers([]);
1962
2010
  return;
1963
2011
  }
1964
2012
 
1965
2013
  const projectId = project.id;
1966
- const currentObjectiveId = objective.id;
2014
+ const objectiveKey = objective.key;
1967
2015
  let cancelled = false;
1968
2016
 
1969
- async function loadObjectiveResources() {
2017
+ async function loadTrackerResources() {
1970
2018
  try {
1971
- const linearResponse = await fetch(
1972
- `/api/projects/${projectId}/objectives/${currentObjectiveId}/linear-issues`
2019
+ const connRes = await fetch(
2020
+ `/api/trackers/connections?projectId=${encodeURIComponent(projectId)}`
1973
2021
  );
1974
- const linearPayload =
1975
- linearResponse.status === 401
1976
- ? { connected: false, issues: [] as ObjectiveLinearIssueSummary[] }
1977
- : linearResponse.ok
1978
- ? ((await linearResponse.json()) as {
1979
- connected?: boolean;
1980
- issues?: ObjectiveLinearIssueSummary[];
1981
- })
1982
- : { connected: true, issues: [] as ObjectiveLinearIssueSummary[] };
1983
-
2022
+ if (!connRes.ok) {
2023
+ if (!cancelled) {
2024
+ setTrackerItems([]);
2025
+ setTrackerConnections([]);
2026
+ setAvailableTrackers([]);
2027
+ }
2028
+ return;
2029
+ }
2030
+ const connPayload = (await connRes.json()) as {
2031
+ connections?: { type: string; connected?: boolean }[];
2032
+ available?: { type: string; displayName: string }[];
2033
+ defaultTracker?: string | null;
2034
+ };
1984
2035
  if (cancelled) return;
1985
2036
 
1986
- setLinearIssues(
1987
- Array.isArray(linearPayload.issues) ? linearPayload.issues : []
2037
+ const available = Array.isArray(connPayload.available) ? connPayload.available : [];
2038
+ const connections: TrackerConnectionInfo[] = (connPayload.connections ?? []).map((c) => ({
2039
+ type: c.type,
2040
+ displayName: available.find((a) => a.type === c.type)?.displayName ?? c.type,
2041
+ connected: c.connected !== false,
2042
+ }));
2043
+ const nextDefault =
2044
+ typeof connPayload.defaultTracker === "string" && connPayload.defaultTracker
2045
+ ? connPayload.defaultTracker
2046
+ : null;
2047
+
2048
+ setAvailableTrackers(available);
2049
+ setTrackerConnections(connections);
2050
+ setDefaultTrackerType(nextDefault);
2051
+
2052
+ const connectedOnly = connections.filter((c) => c.connected);
2053
+ const activeConns =
2054
+ nextDefault && connectedOnly.some((c) => c.type === nextDefault)
2055
+ ? connectedOnly.filter((c) => c.type === nextDefault)
2056
+ : connectedOnly;
2057
+ if (activeConns.length === 0) {
2058
+ setTrackerItems([]);
2059
+ return;
2060
+ }
2061
+
2062
+ const normalizedKey = objectiveKey.trim().toLowerCase();
2063
+ const results = await Promise.all(
2064
+ activeConns.map(async (conn) => {
2065
+ try {
2066
+ const itemsRes = await fetch(
2067
+ `/api/trackers/${encodeURIComponent(conn.type)}/items?projectId=${encodeURIComponent(projectId)}&limit=500`
2068
+ );
2069
+ if (!itemsRes.ok) return [];
2070
+ const payload = (await itemsRes.json()) as {
2071
+ items?: {
2072
+ id: string;
2073
+ trackerType?: string;
2074
+ identifier: string;
2075
+ title: string;
2076
+ url?: string | null;
2077
+ status: string;
2078
+ assignee?: { name?: string } | null;
2079
+ updatedAt: string;
2080
+ labels?: string[];
2081
+ }[];
2082
+ };
2083
+ const items = Array.isArray(payload.items) ? payload.items : [];
2084
+ return items
2085
+ .filter((item) =>
2086
+ (item.labels ?? []).some(
2087
+ (label) => label.trim().toLowerCase() === normalizedKey
2088
+ )
2089
+ )
2090
+ .map<ObjectiveTrackerItemSummary>((item) => ({
2091
+ id: item.id,
2092
+ trackerType: item.trackerType ?? conn.type,
2093
+ identifier: item.identifier,
2094
+ title: item.title,
2095
+ url: item.url ?? null,
2096
+ status: item.status,
2097
+ assignee: item.assignee?.name ?? null,
2098
+ updatedAt: item.updatedAt,
2099
+ labels: item.labels,
2100
+ }));
2101
+ } catch {
2102
+ return [];
2103
+ }
2104
+ })
1988
2105
  );
1989
- setLinearConnected(linearPayload.connected !== false);
2106
+
2107
+ if (cancelled) return;
2108
+ setTrackerItems(results.flat());
1990
2109
  } catch (error) {
1991
2110
  if (cancelled) return;
1992
- console.warn("Failed to load objective resources", error);
1993
- setLinearIssues([]);
1994
- setLinearConnected(true);
2111
+ console.warn("Failed to load objective tracker resources", error);
2112
+ setTrackerItems([]);
2113
+ setTrackerConnections([]);
2114
+ setAvailableTrackers([]);
1995
2115
  }
1996
2116
  }
1997
2117
 
1998
- void loadObjectiveResources();
2118
+ void loadTrackerResources();
1999
2119
 
2000
2120
  return () => {
2001
2121
  cancelled = true;
@@ -2179,7 +2299,14 @@ export function ProjectObjectiveDetail({
2179
2299
  {([
2180
2300
  { id: "activity" as const, label: "Activity", icon: Clock },
2181
2301
  { id: "notes" as const, label: "Notes", icon: FileText },
2182
- { id: "linear" as const, label: "Tasks", icon: (props: { size?: number; className?: string }) => <TrackerIcon trackerType="linear" className="h-3.5 w-3.5" /> },
2302
+ { id: "tasks" as const, label: "Tasks", icon: (props: { size?: number; className?: string }) => {
2303
+ const connected = trackerConnections.filter((c) => c.connected);
2304
+ const iconType =
2305
+ (defaultTrackerType && connected.some((c) => c.type === defaultTrackerType)
2306
+ ? defaultTrackerType
2307
+ : connected[0]?.type) ?? "tracker";
2308
+ return <TrackerIcon trackerType={iconType} className="h-3.5 w-3.5" />;
2309
+ } },
2183
2310
  { id: "scheduled-tasks" as const, label: "Scheduled Jobs", icon: CalendarClock },
2184
2311
  ]).map((tab) => (
2185
2312
  <button
@@ -2204,9 +2331,9 @@ export function ProjectObjectiveDetail({
2204
2331
  {notes.length}
2205
2332
  </span>
2206
2333
  )}
2207
- {tab.id === "linear" && (() => {
2334
+ {tab.id === "tasks" && (() => {
2208
2335
  const DONE = ["done", "canceled", "cancelled", "completed", "duplicate"];
2209
- const active = linearIssues.filter(
2336
+ const active = trackerItems.filter(
2210
2337
  (i, idx, arr) => arr.findIndex((x) => x.id === i.id) === idx && !DONE.includes(i.status.toLowerCase())
2211
2338
  );
2212
2339
  return active.length > 0 ? (
@@ -2385,9 +2512,9 @@ export function ProjectObjectiveDetail({
2385
2512
  />
2386
2513
  )}
2387
2514
 
2388
- {/* Linear Tickets Tab */}
2389
- {activeTab === "linear" && (() => {
2390
- const dedupedIssues = linearIssues.filter(
2515
+ {/* Tracker Tickets Tab */}
2516
+ {activeTab === "tasks" && (() => {
2517
+ const dedupedIssues = trackerItems.filter(
2391
2518
  (issue, idx, arr) => arr.findIndex((i) => i.id === issue.id) === idx
2392
2519
  );
2393
2520
  const DONE_STATUSES = ["done", "canceled", "cancelled", "completed", "duplicate"];
@@ -2398,6 +2525,16 @@ export function ProjectObjectiveDetail({
2398
2525
  .filter((i) => DONE_STATUSES.includes(i.status.toLowerCase()))
2399
2526
  .slice(-5);
2400
2527
 
2528
+ const connectedTrackers = trackerConnections.filter((c) => c.connected);
2529
+ const activeTrackers =
2530
+ defaultTrackerType && connectedTrackers.some((c) => c.type === defaultTrackerType)
2531
+ ? connectedTrackers.filter((c) => c.type === defaultTrackerType)
2532
+ : connectedTrackers;
2533
+ const connectedLabel =
2534
+ activeTrackers.length === 0
2535
+ ? null
2536
+ : activeTrackers.map((c) => c.displayName).join(", ");
2537
+
2401
2538
  return (
2402
2539
  <section>
2403
2540
  <div className="flex flex-wrap items-start justify-between gap-3 mb-4">
@@ -2405,13 +2542,22 @@ export function ProjectObjectiveDetail({
2405
2542
  Tickets tracked by the objective label{" "}
2406
2543
  <code className="rounded bg-[var(--tone-neutral-bg)] px-1.5 py-0.5 font-mono text-[11px] text-[var(--foreground)]">
2407
2544
  {objective.key}
2408
- </code>.
2545
+ </code>
2546
+ {connectedLabel ? <> across {connectedLabel}.</> : "."}
2409
2547
  </p>
2410
2548
  </div>
2411
- {!linearConnected ? (
2412
- <EmptyState label="Connect Linear to create and track tickets for this objective." />
2549
+ {activeTrackers.length === 0 ? (
2550
+ <EmptyState
2551
+ label={
2552
+ availableTrackers.length === 0
2553
+ ? "No tracker integrations registered."
2554
+ : `Connect a tracker (${availableTrackers
2555
+ .map((t) => t.displayName)
2556
+ .join(", ")}) to create and track tickets for this objective.`
2557
+ }
2558
+ />
2413
2559
  ) : dedupedIssues.length === 0 ? (
2414
- <EmptyState label={`No Linear tickets with label ${objective.key} yet.`} />
2560
+ <EmptyState label={`No tracker tickets with label ${objective.key} yet.`} />
2415
2561
  ) : (
2416
2562
  <>
2417
2563
  {activeIssues.length > 0 && (
@@ -2423,11 +2569,14 @@ export function ProjectObjectiveDetail({
2423
2569
  >
2424
2570
  <div className="flex items-start justify-between gap-3">
2425
2571
  <div className="min-w-0">
2426
- <p className="text-xs font-semibold uppercase tracking-[0.18em] text-[var(--muted-foreground)]">
2427
- {issue.identifier}
2428
- </p>
2572
+ <div className="flex items-center gap-1.5 text-xs font-semibold uppercase tracking-[0.18em] text-[var(--muted-foreground)]">
2573
+ <TrackerIcon trackerType={issue.trackerType} className="h-3 w-3" />
2574
+ <span>{issue.identifier}</span>
2575
+ </div>
2429
2576
  <a
2430
- href={`/projects/${projectSlug}/linear?issue=${issue.id}`}
2577
+ href={issue.url ?? `/projects/${projectSlug}/${issue.trackerType}?issue=${issue.id}`}
2578
+ target={issue.url ? "_blank" : undefined}
2579
+ rel={issue.url ? "noreferrer" : undefined}
2431
2580
  className="mt-1 block text-sm font-medium text-[var(--foreground)] transition-colors hover:text-[var(--primary)]"
2432
2581
  >
2433
2582
  {issue.title}
@@ -2455,9 +2604,12 @@ export function ProjectObjectiveDetail({
2455
2604
  {doneIssues.map((issue) => (
2456
2605
  <a
2457
2606
  key={issue.id}
2458
- href={`/projects/${projectSlug}/linear?issue=${issue.id}`}
2607
+ href={issue.url ?? `/projects/${projectSlug}/${issue.trackerType}?issue=${issue.id}`}
2608
+ target={issue.url ? "_blank" : undefined}
2609
+ rel={issue.url ? "noreferrer" : undefined}
2459
2610
  className="group flex items-center gap-3 rounded-md px-2 py-1.5 text-[var(--muted-foreground)] transition-colors hover:bg-[var(--muted)] hover:text-[var(--foreground)]"
2460
2611
  >
2612
+ <TrackerIcon trackerType={issue.trackerType} className="h-3 w-3 shrink-0" />
2461
2613
  <span className="text-[11px] font-mono shrink-0">{issue.identifier}</span>
2462
2614
  <span className="text-[12px] truncate">{issue.title}</span>
2463
2615
  <span className="ml-auto shrink-0 text-[10px] text-[var(--app-shell-soft-text)] group-hover:text-[var(--muted-foreground)]">{issue.status}</span>
@@ -2518,6 +2670,9 @@ export function ProjectObjectiveDetail({
2518
2670
  projectSlug={projectSlug}
2519
2671
  objective={objective}
2520
2672
  teamName={teamName}
2673
+ trackerConnections={trackerConnections}
2674
+ availableTrackers={availableTrackers}
2675
+ defaultTrackerType={defaultTrackerType}
2521
2676
  onThreadLinked={handleObjectiveThreadLinked}
2522
2677
  onObjectiveUpdated={refetchProject}
2523
2678
  />
@@ -13,10 +13,12 @@ interface TrackerConnectionInfo {
13
13
 
14
14
  interface UseTrackerConnectionsReturn {
15
15
  connections: TrackerConnectionInfo[];
16
+ defaultTracker: string | null;
16
17
  loading: boolean;
17
18
  refresh: () => Promise<void>;
18
19
  addConnection: (type: string, metadata?: Record<string, string>) => Promise<{ ok: boolean; error?: string }>;
19
20
  removeConnection: (type: string) => Promise<{ ok: boolean; error?: string }>;
21
+ setDefaultTracker: (type: string | null) => Promise<{ ok: boolean; error?: string }>;
20
22
  }
21
23
 
22
24
  /**
@@ -26,11 +28,13 @@ interface UseTrackerConnectionsReturn {
26
28
  */
27
29
  export function useTrackerConnections(projectId: string | null): UseTrackerConnectionsReturn {
28
30
  const [connections, setConnections] = useState<TrackerConnectionInfo[]>([]);
31
+ const [defaultTracker, setDefaultTrackerState] = useState<string | null>(null);
29
32
  const [loading, setLoading] = useState(true);
30
33
 
31
34
  const refresh = useCallback(async () => {
32
35
  if (!projectId) {
33
36
  setConnections([]);
37
+ setDefaultTrackerState(null);
34
38
  setLoading(false);
35
39
  return;
36
40
  }
@@ -38,8 +42,10 @@ export function useTrackerConnections(projectId: string | null): UseTrackerConne
38
42
  const res = await fetch(`/api/trackers/connections?projectId=${encodeURIComponent(projectId)}`);
39
43
  const data = await res.json();
40
44
  setConnections(Array.isArray(data.connections) ? data.connections : []);
45
+ setDefaultTrackerState(typeof data.defaultTracker === "string" ? data.defaultTracker : null);
41
46
  } catch {
42
47
  setConnections([]);
48
+ setDefaultTrackerState(null);
43
49
  } finally {
44
50
  setLoading(false);
45
51
  }
@@ -95,5 +101,27 @@ export function useTrackerConnections(projectId: string | null): UseTrackerConne
95
101
  [projectId, refresh]
96
102
  );
97
103
 
98
- return { connections, loading, refresh, addConnection, removeConnection };
104
+ const setDefaultTracker = useCallback(
105
+ async (type: string | null) => {
106
+ if (!projectId) return { ok: false, error: "Missing projectId" };
107
+ try {
108
+ const res = await fetch("/api/trackers/connections", {
109
+ method: "PATCH",
110
+ headers: { "Content-Type": "application/json" },
111
+ body: JSON.stringify({ projectId, defaultTracker: type }),
112
+ });
113
+ if (!res.ok) {
114
+ const data = await res.json().catch(() => ({}));
115
+ return { ok: false, error: data.error || "Failed to set default tracker" };
116
+ }
117
+ await refresh();
118
+ return { ok: true };
119
+ } catch {
120
+ return { ok: false, error: "Failed to set default tracker" };
121
+ }
122
+ },
123
+ [projectId, refresh]
124
+ );
125
+
126
+ return { connections, defaultTracker, loading, refresh, addConnection, removeConnection, setDefaultTracker };
99
127
  }
@@ -97,12 +97,18 @@ function getDb(): DatabaseSync {
97
97
  PRIMARY KEY (thread_id, id)
98
98
  )
99
99
  `);
100
- // Migrate: rename linear_runs → tracker_runs if old table exists
100
+ // Migrate: rename linear_runs → tracker_runs if old table exists and new one doesn't
101
101
  const hasOldTable = (db.prepare(
102
102
  "SELECT name FROM sqlite_master WHERE type='table' AND name='linear_runs'"
103
103
  ).get() as { name: string } | undefined);
104
- if (hasOldTable) {
104
+ const hasNewTable = (db.prepare(
105
+ "SELECT name FROM sqlite_master WHERE type='table' AND name='tracker_runs'"
106
+ ).get() as { name: string } | undefined);
107
+ if (hasOldTable && !hasNewTable) {
105
108
  db.exec("ALTER TABLE linear_runs RENAME TO tracker_runs");
109
+ } else if (hasOldTable && hasNewTable) {
110
+ // Both exist (likely a partial prior migration) — drop the stale old table
111
+ db.exec("DROP TABLE linear_runs");
106
112
  }
107
113
  db.exec(`
108
114
  CREATE TABLE IF NOT EXISTS tracker_runs (