santree 0.2.11 → 0.2.13

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.
@@ -14,7 +14,8 @@ import { run, spawnAsync } from "../lib/exec.js";
14
14
  import { resolveAgentBinary } from "../lib/ai.js";
15
15
  import { extractTicketId } from "../lib/git.js";
16
16
  import { getPRTemplate } from "../lib/github.js";
17
- import { renderPrompt, renderDiff } from "../lib/prompts.js";
17
+ import { renderPrompt, renderDiff, renderTicket } from "../lib/prompts.js";
18
+ import { getTicketContent } from "../lib/linear.js";
18
19
  import * as os from "os";
19
20
  import { initialState, reducer } from "../lib/dashboard/types.js";
20
21
  import { loadDashboardData, loadReviewsData } from "../lib/dashboard/data.js";
@@ -719,6 +720,15 @@ export default function Dashboard() {
719
720
  }
720
721
  dispatch({ type: "PR_CREATE_PHASE", phase: "filling" });
721
722
  const ticketId = extractTicketId(s.prCreateBranch) ?? "";
723
+ const mainRepoRoot = findMainRepoRoot();
724
+ // Fetch ticket content (downloads images for Linear tickets)
725
+ let ticketContent;
726
+ if (ticketId && mainRepoRoot) {
727
+ const ticket = await getTicketContent(ticketId, mainRepoRoot);
728
+ if (ticket) {
729
+ ticketContent = renderTicket(ticket);
730
+ }
731
+ }
722
732
  const commitLog = run(`git log ${base}..HEAD --format="- %s"`, { cwd }) || null;
723
733
  const diffStat = run(`git diff ${base}..HEAD --stat`, { cwd }) || null;
724
734
  const diff = run(`git diff ${base}..HEAD`, { cwd, maxBuffer: 10 * 1024 * 1024 }) || null;
@@ -732,10 +742,11 @@ export default function Dashboard() {
732
742
  pr_template: prTemplate,
733
743
  diff_content: diffContent,
734
744
  ticket_id: ticketId,
745
+ ticket_content: ticketContent,
735
746
  branch_name: s.prCreateBranch,
736
747
  });
737
748
  // Pass prompt via stdin instead of temp file
738
- const agentResult = await spawnAsync(bin, ["-p", "--output-format", "text"], {
749
+ const agentResult = await spawnAsync(bin, ["-p", "--output-format", "text", "--allowedTools", "Read"], {
739
750
  stdin: prompt,
740
751
  });
741
752
  const body = agentResult.output.trim();
@@ -133,6 +133,18 @@ export default function LinearAuth({ options }) {
133
133
  setStatus("done");
134
134
  return;
135
135
  }
136
+ // Token expired and refresh failed — re-authenticate
137
+ setStatus("authenticating");
138
+ const result = await startOAuthFlow();
139
+ if (!result) {
140
+ setError("Authentication failed or timed out. Please try again.");
141
+ setStatus("error");
142
+ return;
143
+ }
144
+ setRepoLinearOrg(repoRoot, result.orgSlug);
145
+ setMessage(`Re-authenticated as ${result.orgName} (${result.orgSlug})`);
146
+ setStatus("done");
147
+ return;
136
148
  }
137
149
  // Check for existing authenticated orgs
138
150
  const store = readAuthStore();
@@ -10,8 +10,9 @@ import { writeFileSync } from "fs";
10
10
  import { tmpdir } from "os";
11
11
  import { findMainRepoRoot, findRepoRoot, getCurrentBranch, getBaseBranch, hasUncommittedChanges, getCommitsAhead, remoteBranchExists, getUnpushedCommits, extractTicketId, isInWorktree, getFirstCommitMessage, getCommitLog, getDiffStat, getDiffContent, } from "../../lib/git.js";
12
12
  import { ghCliAvailable, getPRInfoAsync, pushBranch, createPR, getPRTemplate, } from "../../lib/github.js";
13
- import { renderPrompt, renderDiff } from "../../lib/prompts.js";
13
+ import { renderPrompt, renderDiff, renderTicket } from "../../lib/prompts.js";
14
14
  import { runAgent } from "../../lib/ai.js";
15
+ import { getTicketContent } from "../../lib/linear.js";
15
16
  const execAsync = promisify(exec);
16
17
  export const description = "Create a GitHub pull request";
17
18
  export const options = z.object({
@@ -45,7 +46,7 @@ export default function PR({ options }) {
45
46
  setPendingCreate(false);
46
47
  openPR();
47
48
  }, [pendingCreate]);
48
- function openPR() {
49
+ async function openPR() {
49
50
  if (!branch || !baseBranch)
50
51
  return;
51
52
  const title = getFirstCommitMessage(baseBranch) ?? branch;
@@ -61,6 +62,15 @@ export default function PR({ options }) {
61
62
  return;
62
63
  }
63
64
  const ticketId = extractTicketId(branch);
65
+ const mainRepoRoot = findMainRepoRoot();
66
+ // Fetch ticket content (downloads images for Linear tickets)
67
+ let ticketContent;
68
+ if (ticketId && mainRepoRoot) {
69
+ const ticket = await getTicketContent(ticketId, mainRepoRoot);
70
+ if (ticket) {
71
+ ticketContent = renderTicket(ticket);
72
+ }
73
+ }
64
74
  const diffContent = renderDiff({
65
75
  base_branch: baseBranch,
66
76
  commit_log: getCommitLog(baseBranch),
@@ -71,9 +81,10 @@ export default function PR({ options }) {
71
81
  pr_template: prTemplate,
72
82
  diff_content: diffContent,
73
83
  ticket_id: ticketId ?? "",
84
+ ticket_content: ticketContent,
74
85
  branch_name: branch,
75
86
  });
76
- const result = runAgent(prompt);
87
+ const result = runAgent(prompt, { allowedTools: ["Read"] });
77
88
  if (!result.success) {
78
89
  setStatus("error");
79
90
  setMessage("Failed to generate PR body with Claude");
package/dist/lib/ai.d.ts CHANGED
@@ -60,7 +60,9 @@ export interface RunAgentResult {
60
60
  * Passes prompt directly or via temp file if too large for OS arg limit.
61
61
  * Throws if claude CLI is not found.
62
62
  */
63
- export declare function runAgent(prompt: string): RunAgentResult;
63
+ export declare function runAgent(prompt: string, opts?: {
64
+ allowedTools?: string[];
65
+ }): RunAgentResult;
64
66
  /**
65
67
  * Cleanup images downloaded for a ticket.
66
68
  */
package/dist/lib/ai.js CHANGED
@@ -165,13 +165,14 @@ export function launchAgent(prompt, opts) {
165
165
  * Passes prompt directly or via temp file if too large for OS arg limit.
166
166
  * Throws if claude CLI is not found.
167
167
  */
168
- export function runAgent(prompt) {
168
+ export function runAgent(prompt, opts) {
169
169
  const bin = resolveAgentBinary();
170
170
  if (!bin) {
171
171
  throw new Error("Claude CLI not found. Install: npm install -g @anthropic-ai/claude-code");
172
172
  }
173
173
  const skipPerms = process.env.SANTREE_SKIP_PERMISSIONS ? ["--dangerously-skip-permissions"] : [];
174
- const result = spawnSync(bin, [...skipPerms, "-p", "--output-format", "text", "--", promptArg(prompt)], {
174
+ const toolArgs = opts?.allowedTools?.length ? ["--allowedTools", ...opts.allowedTools] : [];
175
+ const result = spawnSync(bin, [...skipPerms, ...toolArgs, "-p", "--output-format", "text", "--", promptArg(prompt)], {
175
176
  encoding: "utf-8",
176
177
  maxBuffer: 10 * 1024 * 1024,
177
178
  });
@@ -8,7 +8,7 @@ export async function loadDashboardData(repoRoot) {
8
8
  Promise.resolve(listWorktrees()),
9
9
  ]);
10
10
  if (!issues)
11
- throw new Error("Failed to fetch Linear issues. Check authentication.");
11
+ throw new Error("Failed to authenticate with Linear. Run: santree linear auth");
12
12
  // Build worktree map: ticketId -> worktree info
13
13
  const wtMap = new Map();
14
14
  for (const wt of worktrees) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "santree",
3
- "version": "0.2.11",
3
+ "version": "0.2.13",
4
4
  "description": "Git worktree manager",
5
5
  "license": "MIT",
6
6
  "author": "Santiago Toscanini",
@@ -19,6 +19,6 @@ Branch: {{ branch_name }}
19
19
  - Fill the template sections using the commits and diff above
20
20
  - Be concise and factual
21
21
  - For any screenshot sections, leave a comment: `<!-- Screenshot: describe what to show -->`
22
- - If you lack information to fill a section, fill it with what you have from the diff and commits. If you truly have nothing relevant, leave the section empty. Never write excuses like "I don't have permissions" or "Unable to access" — that is not valid template content.
22
+ - If you lack information to fill a section, fill it with what you have from the diff and commits. If you truly have nothing relevant, leave the section empty.
23
23
  - Do NOT wrap the output in a code block
24
24
  - Output ONLY the filled template