santree 0.7.0 → 0.7.3

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.
@@ -1,6 +1,6 @@
1
1
  import { readLinearAuthStore } from "../auth-store.js";
2
2
  import { getRepoLinearOrg, getValidTokens, removeRepoLinearOrg, revokeTokens } from "./auth.js";
3
- import { fetchAssignedIssues, fetchIssue } from "./api.js";
3
+ import { fetchAssignedIssues, fetchIssue, fetchTriageSchedules } from "./api.js";
4
4
  import { cleanupLinearImages, rewriteLinearImages } from "./images.js";
5
5
  export { getRepoLinearOrg, setRepoLinearOrg, removeRepoLinearOrg, getValidTokens, revokeTokens, startOAuthFlow, } from "./auth.js";
6
6
  async function getAuthStatus(repoRoot) {
@@ -59,6 +59,20 @@ async function listAssigned(repoRoot) {
59
59
  }
60
60
  return { ok: true, value: issues };
61
61
  }
62
+ async function getTriageSchedules(repoRoot) {
63
+ const orgSlug = getRepoLinearOrg(repoRoot);
64
+ if (!orgSlug)
65
+ return [];
66
+ const tokens = await getValidTokens(orgSlug);
67
+ if (!tokens)
68
+ return [];
69
+ try {
70
+ return await fetchTriageSchedules(tokens.access_token);
71
+ }
72
+ catch {
73
+ return [];
74
+ }
75
+ }
62
76
  async function getIssue(identifier, repoRoot) {
63
77
  const orgSlug = getRepoLinearOrg(repoRoot);
64
78
  if (!orgSlug) {
@@ -91,10 +105,12 @@ export const linearTracker = {
91
105
  kind: "linear",
92
106
  displayName: "Linear",
93
107
  issueNoun: "ticket",
108
+ supportsTriage: true,
94
109
  getAuthStatus,
95
110
  signOut,
96
111
  extractIdFromBranch,
97
112
  cleanupCache: cleanupLinearImages,
98
113
  listAssigned,
99
114
  getIssue,
115
+ getTriageSchedules,
100
116
  };
@@ -9,6 +9,18 @@ export interface State {
9
9
  name: string;
10
10
  type: string;
11
11
  }
12
+ /** Readiness of an issue given its blocking dependencies:
13
+ * "ready" — no unresolved blockers (or none at all)
14
+ * "blocked" — at least one blocker isn't done yet
15
+ * "unknown" — the tracker doesn't expose dependency data */
16
+ export type Readiness = "ready" | "blocked" | "unknown";
17
+ export declare function issueReadiness(blockedBy: IssueRef[] | undefined): Readiness;
18
+ /** A lightweight reference to a related issue, with whether it's resolved
19
+ * (state.type is completed/canceled). Used for dependency (blocks) relations. */
20
+ export interface IssueRef {
21
+ identifier: string;
22
+ done: boolean;
23
+ }
12
24
  export interface AssignedIssue {
13
25
  identifier: string;
14
26
  title: string;
@@ -20,10 +32,50 @@ export interface AssignedIssue {
20
32
  labels: string[];
21
33
  projectId: string | null;
22
34
  projectName: string | null;
35
+ /** Issues that block this one ("blocked by"). An issue is ready to start when
36
+ * every blocker is `done`. `undefined` when the tracker doesn't expose
37
+ * dependency relations (only Linear does); `[]` means no blockers. */
38
+ blockedBy?: IssueRef[];
39
+ /** Issues this one blocks (downstream dependents). */
40
+ blocking?: IssueRef[];
41
+ /** When the issue's triage SLA breaches, as an ISO timestamp, or null when
42
+ * no SLA applies. Only trackers with a native SLA concept populate it
43
+ * (Linear today); others leave it undefined. Surfaced on the Triage tab as a
44
+ * colored, urgency-coded countdown badge. */
45
+ slaBreachesAt?: string | null;
46
+ /** When the issue is snoozed until, as an ISO timestamp, or null when not
47
+ * snoozed. A snooze in the future means the issue is parked — surfaced on the
48
+ * Triage tab as a greyed, sunk-to-the-bottom row so active work stands out.
49
+ * Linear-only today. */
50
+ snoozedUntilAt?: string | null;
23
51
  }
24
52
  export interface Issue extends AssignedIssue {
25
53
  comments: Comment[];
26
54
  }
55
+ /** One slot in a triage on-call rotation. */
56
+ export interface TriageShift {
57
+ startsAt: string;
58
+ endsAt: string;
59
+ /** Resolved display name (falls back to email, then "Unknown"). */
60
+ name: string;
61
+ /** True when this shift covers the current moment. */
62
+ isCurrent: boolean;
63
+ /** True when this shift belongs to the authenticated viewer. */
64
+ isMe: boolean;
65
+ }
66
+ /** A team's triage responsibility rotation (Linear "Triage responsibility"
67
+ * backed by a time schedule). */
68
+ export interface TriageSchedule {
69
+ teamKey: string;
70
+ teamName: string;
71
+ scheduleName: string;
72
+ /** Display name of whoever is on call right now, if known. */
73
+ currentName: string | null;
74
+ /** Whether the viewer is the one currently on call. */
75
+ currentIsMe: boolean;
76
+ /** Chronological list of shifts. */
77
+ shifts: TriageShift[];
78
+ }
27
79
  export interface AuthStatus {
28
80
  authenticated: boolean;
29
81
  accountLabel?: string;
@@ -65,6 +117,17 @@ export interface IssueTracker {
65
117
  cleanupCache(identifier: string): void;
66
118
  listAssigned(repoRoot: string): Promise<IssueTrackerResult<AssignedIssue[]>>;
67
119
  getIssue(identifier: string, repoRoot: string): Promise<IssueTrackerResult<Issue>>;
120
+ /** When true, this tracker has a native triage concept (incoming issues in
121
+ * a `state.type === "triage"` inbox). The dashboard surfaces a dedicated
122
+ * Triage tab only when the active tracker sets this — feature detection,
123
+ * never a `kind === "linear"` string check, per the
124
+ * no-tracker-conditionals-outside-the-factory policy. Linear sets it;
125
+ * GitHub/Local leave it undefined. */
126
+ readonly supportsTriage?: boolean;
127
+ /** Triage on-call rotations for the viewer's teams. Optional — implemented
128
+ * only by trackers with a triage scheduling concept (Linear). Returns an
129
+ * empty array on failure or when no schedules exist; never throws. */
130
+ getTriageSchedules?(repoRoot: string): Promise<TriageSchedule[]>;
68
131
  /** When true, the tracker implements createIssue/updateIssue/deleteIssue.
69
132
  * Read-only trackers (Linear, GitHub) leave this undefined; UI surfaces
70
133
  * gate every mutation path on `tracker.canMutate === true` (feature
@@ -1 +1,5 @@
1
- export {};
1
+ export function issueReadiness(blockedBy) {
2
+ if (blockedBy === undefined)
3
+ return "unknown";
4
+ return blockedBy.some((b) => !b.done) ? "blocked" : "ready";
5
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "santree",
3
- "version": "0.7.0",
3
+ "version": "0.7.3",
4
4
  "description": "Git worktree manager",
5
5
  "license": "MIT",
6
6
  "author": "Santiago Toscanini",
@@ -0,0 +1,10 @@
1
+ You are helping triage an incoming issue. Below is the full issue with all of its comments. Answer the question that follows concisely and directly, grounded in what the issue and its discussion actually say.
2
+
3
+ When the question is about whether or how the issue can be fixed, you may inspect the codebase read-only (Read/Grep/Glob) to ground your answer in the real code — name the relevant files. Do not make any changes, and do not ask the user to run commands.
4
+
5
+ Keep the answer short and skimmable: a few sentences or a short bullet list. If the issue lacks the information needed to answer, say what's missing.
6
+
7
+ {{ ticket_content }}
8
+
9
+ ## Question
10
+ {{ user_question }}