santree 0.7.4 → 0.7.5
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.
- package/dist/commands/dashboard.js +25 -22
- package/dist/commands/doctor.js +12 -85
- package/dist/commands/setup.d.ts +11 -0
- package/dist/commands/setup.js +194 -0
- package/dist/commands/worktree/create.js +11 -10
- package/dist/commands/worktree/switch.js +3 -11
- package/dist/lib/cd-hint.d.ts +18 -0
- package/dist/lib/cd-hint.js +27 -0
- package/dist/lib/claude-config.d.ts +12 -0
- package/dist/lib/claude-config.js +86 -0
- package/dist/lib/dashboard/DetailPanel.d.ts +4 -1
- package/dist/lib/dashboard/DetailPanel.js +11 -1
- package/dist/lib/dashboard/types.d.ts +6 -0
- package/dist/lib/dashboard/types.js +37 -2
- package/dist/lib/setup/apply.d.ts +16 -0
- package/dist/lib/setup/apply.js +35 -0
- package/dist/lib/setup/gitignore.d.ts +17 -0
- package/dist/lib/setup/gitignore.js +57 -0
- package/dist/lib/setup/shell-config.d.ts +38 -0
- package/dist/lib/setup/shell-config.js +131 -0
- package/dist/lib/setup/steps.d.ts +35 -0
- package/dist/lib/setup/steps.js +328 -0
- package/dist/lib/setup/tools.d.ts +23 -0
- package/dist/lib/setup/tools.js +47 -0
- package/dist/lib/squirrel-loader.d.ts +3 -1
- package/dist/lib/squirrel-loader.js +2 -2
- package/package.json +1 -2
- package/dist/commands/helpers/shell-init.d.ts +0 -11
- package/dist/commands/helpers/shell-init.js +0 -122
- package/shell/init.bash.njk +0 -129
- package/shell/init.zsh.njk +0 -204
|
@@ -10,6 +10,7 @@ const require = createRequire(import.meta.url);
|
|
|
10
10
|
const { version } = require("../../package.json");
|
|
11
11
|
import { findMainRepoRoot, createWorktree, getDefaultBranch, getBaseBranch, hasInitScript, getInitScriptPath, removeWorktree, getDiffTool, getWorktreeStatus, stageFile, unstageFile, stageAll, unstageAll, discardFile, } from "../lib/git.js";
|
|
12
12
|
import { run, spawnAsync } from "../lib/exec.js";
|
|
13
|
+
import { printCdHint } from "../lib/cd-hint.js";
|
|
13
14
|
import { resolveAgentBinary, resolveClaudeBinary, fillCommitMessage, askTicketQuestion, } from "../lib/ai.js";
|
|
14
15
|
import { readTriageInvestigateConfig, isTriageInvestigateConfigured, buildInvestigatePrompt, buildInvestigateCommand, } from "../lib/triage-config.js";
|
|
15
16
|
import { getInstalledClaudeVersion } from "../lib/version.js";
|
|
@@ -1049,7 +1050,9 @@ export default function Dashboard() {
|
|
|
1049
1050
|
? `cd ${shellEscape(sessionCwd)} && ${bin} --resume ${sessionId}`
|
|
1050
1051
|
: null;
|
|
1051
1052
|
const contextArg = contextFile ? ` --context-file "${contextFile}"` : "";
|
|
1052
|
-
const workCmd = mode === "plan"
|
|
1053
|
+
const workCmd = mode === "plan"
|
|
1054
|
+
? `santree worktree work --plan${contextArg}`
|
|
1055
|
+
: `santree worktree work${contextArg}`;
|
|
1053
1056
|
const cmd = resumeCmd ?? workCmd;
|
|
1054
1057
|
const mux = getMultiplexer();
|
|
1055
1058
|
const selected = await mux.selectWindow(windowName);
|
|
@@ -1091,7 +1094,7 @@ export default function Dashboard() {
|
|
|
1091
1094
|
});
|
|
1092
1095
|
}
|
|
1093
1096
|
}
|
|
1094
|
-
// Delayed refresh to pick up session ID created by `
|
|
1097
|
+
// Delayed refresh to pick up session ID created by `santree worktree work`
|
|
1095
1098
|
setTimeout(() => refresh(), 3000);
|
|
1096
1099
|
}, [refresh]);
|
|
1097
1100
|
const launchAfterCreation = useCallback(async (mode, worktreePath, ticketId, contextFile) => {
|
|
@@ -1100,8 +1103,8 @@ export default function Dashboard() {
|
|
|
1100
1103
|
const windowName = ticketId;
|
|
1101
1104
|
const contextArg = contextFile ? ` --context-file "${contextFile}"` : "";
|
|
1102
1105
|
const workCmd = mode === "plan"
|
|
1103
|
-
? `
|
|
1104
|
-
: `
|
|
1106
|
+
? `santree worktree work --plan${contextArg}`
|
|
1107
|
+
: `santree worktree work${contextArg}`;
|
|
1105
1108
|
const created = await mux.createWindow({
|
|
1106
1109
|
name: windowName,
|
|
1107
1110
|
cwd: worktreePath,
|
|
@@ -1123,10 +1126,10 @@ export default function Dashboard() {
|
|
|
1123
1126
|
}
|
|
1124
1127
|
else {
|
|
1125
1128
|
leaveAltScreen();
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1129
|
+
printCdHint({
|
|
1130
|
+
path: worktreePath,
|
|
1131
|
+
work: { mode, contextFile: contextFile || undefined },
|
|
1132
|
+
});
|
|
1130
1133
|
exit();
|
|
1131
1134
|
}
|
|
1132
1135
|
}, [exit, refresh]);
|
|
@@ -1277,10 +1280,10 @@ export default function Dashboard() {
|
|
|
1277
1280
|
}
|
|
1278
1281
|
else {
|
|
1279
1282
|
leaveAltScreen();
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1283
|
+
printCdHint({
|
|
1284
|
+
path: di.worktree.path,
|
|
1285
|
+
work: { mode, contextFile: contextFile || undefined },
|
|
1286
|
+
});
|
|
1284
1287
|
exit();
|
|
1285
1288
|
}
|
|
1286
1289
|
}
|
|
@@ -2572,7 +2575,7 @@ export default function Dashboard() {
|
|
|
2572
2575
|
const created = await mux.createWindow({
|
|
2573
2576
|
name: windowName,
|
|
2574
2577
|
cwd,
|
|
2575
|
-
command: "
|
|
2578
|
+
command: "santree pr review",
|
|
2576
2579
|
});
|
|
2577
2580
|
dispatch({
|
|
2578
2581
|
type: "SET_ACTION_MESSAGE",
|
|
@@ -2584,7 +2587,7 @@ export default function Dashboard() {
|
|
|
2584
2587
|
}
|
|
2585
2588
|
else {
|
|
2586
2589
|
leaveAltScreen();
|
|
2587
|
-
|
|
2590
|
+
printCdHint({ path: ri.worktree.path });
|
|
2588
2591
|
exit();
|
|
2589
2592
|
}
|
|
2590
2593
|
return;
|
|
@@ -2849,7 +2852,7 @@ export default function Dashboard() {
|
|
|
2849
2852
|
const selected = await mux.selectWindow(windowName);
|
|
2850
2853
|
if (selected.ok)
|
|
2851
2854
|
return;
|
|
2852
|
-
const cmd = resumeCmd ?? "
|
|
2855
|
+
const cmd = resumeCmd ?? "santree worktree work";
|
|
2853
2856
|
const created = await mux.createWindow({
|
|
2854
2857
|
name: windowName,
|
|
2855
2858
|
cwd: worktreePath,
|
|
@@ -2865,7 +2868,7 @@ export default function Dashboard() {
|
|
|
2865
2868
|
}
|
|
2866
2869
|
else {
|
|
2867
2870
|
leaveAltScreen();
|
|
2868
|
-
|
|
2871
|
+
printCdHint({ path: di.worktree.path });
|
|
2869
2872
|
exit();
|
|
2870
2873
|
}
|
|
2871
2874
|
return;
|
|
@@ -2924,7 +2927,7 @@ export default function Dashboard() {
|
|
|
2924
2927
|
const created = await mux.createWindow({
|
|
2925
2928
|
name: windowName,
|
|
2926
2929
|
cwd,
|
|
2927
|
-
command: "
|
|
2930
|
+
command: "santree pr review",
|
|
2928
2931
|
});
|
|
2929
2932
|
dispatch({
|
|
2930
2933
|
type: "SET_ACTION_MESSAGE",
|
|
@@ -2936,7 +2939,7 @@ export default function Dashboard() {
|
|
|
2936
2939
|
}
|
|
2937
2940
|
else {
|
|
2938
2941
|
leaveAltScreen();
|
|
2939
|
-
|
|
2942
|
+
printCdHint({ path: di.worktree.path });
|
|
2940
2943
|
exit();
|
|
2941
2944
|
}
|
|
2942
2945
|
return;
|
|
@@ -2993,7 +2996,7 @@ export default function Dashboard() {
|
|
|
2993
2996
|
const created = await mux.createWindow({
|
|
2994
2997
|
name: windowName,
|
|
2995
2998
|
cwd,
|
|
2996
|
-
command: "
|
|
2999
|
+
command: "santree pr fix",
|
|
2997
3000
|
});
|
|
2998
3001
|
dispatch({
|
|
2999
3002
|
type: "SET_ACTION_MESSAGE",
|
|
@@ -3005,7 +3008,7 @@ export default function Dashboard() {
|
|
|
3005
3008
|
}
|
|
3006
3009
|
else {
|
|
3007
3010
|
leaveAltScreen();
|
|
3008
|
-
|
|
3011
|
+
printCdHint({ path: di.worktree.path });
|
|
3009
3012
|
exit();
|
|
3010
3013
|
}
|
|
3011
3014
|
return;
|
|
@@ -3052,7 +3055,7 @@ export default function Dashboard() {
|
|
|
3052
3055
|
});
|
|
3053
3056
|
// ── Render ─────────────────────────────────────────────────────────
|
|
3054
3057
|
if (state.loading) {
|
|
3055
|
-
return (_jsx(Box, { width: columns, height: rows, flexDirection: "column", children: _jsx(Box, { justifyContent: "center", alignItems: "center", flexGrow: 1, children: _jsx(SquirrelLoader, { text: "Loading dashboard..." }) }) }));
|
|
3058
|
+
return (_jsx(Box, { width: columns, height: rows, flexDirection: "column", children: _jsx(Box, { justifyContent: "center", alignItems: "center", flexGrow: 1, children: _jsx(SquirrelLoader, { text: "Loading dashboard...", version: version }) }) }));
|
|
3056
3059
|
}
|
|
3057
3060
|
if (state.error) {
|
|
3058
3061
|
return (_jsx(Box, { width: columns, height: rows, flexDirection: "column", children: _jsxs(Box, { justifyContent: "center", alignItems: "center", flexGrow: 1, flexDirection: "column", children: [_jsxs(Text, { color: "red", bold: true, children: ["Error: ", state.error] }), _jsx(Text, { dimColor: true, children: "Press R to retry or q to quit" })] }) }));
|
|
@@ -3152,7 +3155,7 @@ export default function Dashboard() {
|
|
|
3152
3155
|
? state.treeDetailScrollOffset
|
|
3153
3156
|
: state.activeTab === "triage"
|
|
3154
3157
|
? state.triageDetailScrollOffset
|
|
3155
|
-
: state.detailScrollOffset, height: contentHeight, width: rightWidth, creatingForTicket: state.creatingForTicket, creationLogs: state.creationLogs, deleteStatus: selectedDeleteStatus, triage: state.activeTab === "triage", comments: triageComments, onCall: onCall })) })] })), _jsx(Box, { children: state.overlay === "diff" ? (_jsx(Box, { width: innerWidth, paddingX: 1, children: _jsx(CommandBar, { showWorkspace: hasWorkspaceFile, mode: "diff" }) })) : state.overlay === "triage-schedule" ? (_jsx(Box, { width: innerWidth, paddingX: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: "cyan", bold: true, children: "j/k" }), _jsx(Text, { dimColor: true, children: " scroll" }), _jsx(Text, { dimColor: true, children: " · " }), _jsx(Text, { color: "cyan", bold: true, children: "q" }), _jsx(Text, { dimColor: true, children: " close" })] }) })) : (_jsxs(_Fragment, { children: [_jsx(Box, { width: leftWidth + separatorWidth, paddingX: 1, children: _jsx(CommandBar, { showWorkspace: hasWorkspaceFile, mode: "default" }) }), _jsx(Box, { width: rightWidth, children: _jsx(ActionRow, { activeTab: state.activeTab, selectedIssue: selectedIssue, selectedReview: selectedReview, overlay: state.overlay, trackerName: activeTracker.displayName, canMutate: activeTracker.canMutate === true, triageInvestigateConfigured: triageInvestigateConfigured }) })] })) })] })] }));
|
|
3158
|
+
: state.detailScrollOffset, height: contentHeight, width: rightWidth, creatingForTicket: state.creatingForTicket, creationLogs: state.creationLogs, deleteStatus: selectedDeleteStatus, prSyncing: !!selectedIssue && !!state.pendingPrs[selectedIssue.issue.identifier], triage: state.activeTab === "triage", comments: triageComments, onCall: onCall })) })] })), _jsx(Box, { children: state.overlay === "diff" ? (_jsx(Box, { width: innerWidth, paddingX: 1, children: _jsx(CommandBar, { showWorkspace: hasWorkspaceFile, mode: "diff" }) })) : state.overlay === "triage-schedule" ? (_jsx(Box, { width: innerWidth, paddingX: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: "cyan", bold: true, children: "j/k" }), _jsx(Text, { dimColor: true, children: " scroll" }), _jsx(Text, { dimColor: true, children: " · " }), _jsx(Text, { color: "cyan", bold: true, children: "q" }), _jsx(Text, { dimColor: true, children: " close" })] }) })) : (_jsxs(_Fragment, { children: [_jsx(Box, { width: leftWidth + separatorWidth, paddingX: 1, children: _jsx(CommandBar, { showWorkspace: hasWorkspaceFile, mode: "default" }) }), _jsx(Box, { width: rightWidth, children: _jsx(ActionRow, { activeTab: state.activeTab, selectedIssue: selectedIssue, selectedReview: selectedReview, overlay: state.overlay, trackerName: activeTracker.displayName, canMutate: activeTracker.canMutate === true, triageInvestigateConfigured: triageInvestigateConfigured }) })] })) })] })] }));
|
|
3156
3159
|
}
|
|
3157
3160
|
/**
|
|
3158
3161
|
* Renders the per-issue action key hints (Resume / Editor / View diff / …)
|
package/dist/commands/doctor.js
CHANGED
|
@@ -13,6 +13,7 @@ import { findMainRepoRoot, getSantreeDir, getInitScriptPath } from "../lib/git.j
|
|
|
13
13
|
import { getIssueTracker } from "../lib/trackers/index.js";
|
|
14
14
|
import { getMultiplexer } from "../lib/multiplexer/index.js";
|
|
15
15
|
import { resolveClaudeBinary } from "../lib/ai.js";
|
|
16
|
+
import { isStatuslineConfigured, getStatuslineCommand, isRemoteControlEnabled, missingSessionSignalHooks, } from "../lib/claude-config.js";
|
|
16
17
|
import { CURRENT_VERSION, CLAUDE_CODE_PACKAGE, SANTREE_PACKAGE, getLatestVersionFor, isUpdateAvailable, detectPackageManager, getInstallCommandFor, } from "../lib/version.js";
|
|
17
18
|
const execAsync = promisify(exec);
|
|
18
19
|
export const description = "Check system requirements and integrations";
|
|
@@ -214,16 +215,6 @@ async function checkTrackerAuth() {
|
|
|
214
215
|
hint: status.hint,
|
|
215
216
|
};
|
|
216
217
|
}
|
|
217
|
-
/**
|
|
218
|
-
* Checks if the shell integration is set up by looking for the
|
|
219
|
-
* SANTREE_SHELL_INTEGRATION environment variable exported by the shell scripts.
|
|
220
|
-
*/
|
|
221
|
-
function checkShellIntegration() {
|
|
222
|
-
const shell = process.env.SHELL || "";
|
|
223
|
-
const shellName = shell.includes("zsh") ? "zsh" : shell.includes("bash") ? "bash" : null;
|
|
224
|
-
const configured = process.env.SANTREE_SHELL_INTEGRATION === "1";
|
|
225
|
-
return { configured, shell: shellName };
|
|
226
|
-
}
|
|
227
218
|
/**
|
|
228
219
|
* Checks if Claude Code Remote Control is enabled for all sessions.
|
|
229
220
|
* Remote Control lets you continue local sessions from any device.
|
|
@@ -233,23 +224,12 @@ function checkShellIntegration() {
|
|
|
233
224
|
* See: https://code.claude.com/docs/en/settings#settings-files
|
|
234
225
|
*/
|
|
235
226
|
function checkRemoteControl() {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
try {
|
|
239
|
-
if (fs.existsSync(configPath)) {
|
|
240
|
-
const content = fs.readFileSync(configPath, "utf-8");
|
|
241
|
-
const config = JSON.parse(content);
|
|
242
|
-
if (config.remoteControlAtStartup === true) {
|
|
243
|
-
return { enabled: true };
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
catch {
|
|
248
|
-
// JSON parse error or file read error
|
|
227
|
+
if (isRemoteControlEnabled()) {
|
|
228
|
+
return { enabled: true };
|
|
249
229
|
}
|
|
250
230
|
return {
|
|
251
231
|
enabled: false,
|
|
252
|
-
hint: 'Run /config in Claude Code
|
|
232
|
+
hint: 'Run `santree setup` (or /config in Claude Code → "Enable Remote Control for all sessions")',
|
|
253
233
|
};
|
|
254
234
|
}
|
|
255
235
|
/**
|
|
@@ -257,30 +237,12 @@ function checkRemoteControl() {
|
|
|
257
237
|
* If ~/.claude/settings.json has statusLine pointing to santree
|
|
258
238
|
*/
|
|
259
239
|
async function checkStatusline() {
|
|
260
|
-
const
|
|
261
|
-
const
|
|
262
|
-
let claudeSettingsConfigured = false;
|
|
263
|
-
let currentCommand;
|
|
264
|
-
try {
|
|
265
|
-
if (fs.existsSync(claudeSettingsPath)) {
|
|
266
|
-
const content = fs.readFileSync(claudeSettingsPath, "utf-8");
|
|
267
|
-
const settings = JSON.parse(content);
|
|
268
|
-
if (settings.statusLine?.command) {
|
|
269
|
-
currentCommand = String(settings.statusLine.command);
|
|
270
|
-
// Check if it points to santree statusline
|
|
271
|
-
claudeSettingsConfigured =
|
|
272
|
-
currentCommand.includes("santree statusline") ||
|
|
273
|
-
currentCommand.includes("santree helpers statusline");
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
catch {
|
|
278
|
-
// JSON parse error or file read error
|
|
279
|
-
}
|
|
240
|
+
const claudeSettingsConfigured = isStatuslineConfigured();
|
|
241
|
+
const currentCommand = getStatuslineCommand();
|
|
280
242
|
let hint;
|
|
281
243
|
if (!claudeSettingsConfigured) {
|
|
282
244
|
hint =
|
|
283
|
-
'
|
|
245
|
+
'Run `santree setup`, or add to ~/.claude/settings.json: "statusLine": { "type": "command", "command": "santree helpers statusline" }';
|
|
284
246
|
}
|
|
285
247
|
return {
|
|
286
248
|
claudeSettingsConfigured,
|
|
@@ -294,44 +256,14 @@ async function checkStatusline() {
|
|
|
294
256
|
* that run "santree helpers session-signal".
|
|
295
257
|
*/
|
|
296
258
|
function checkSessionSignalHooks() {
|
|
297
|
-
const
|
|
298
|
-
const claudeSettingsPath = path.join(home, ".claude", "settings.json");
|
|
299
|
-
const requiredEvents = ["Notification", "Stop", "UserPromptSubmit", "SessionEnd"];
|
|
300
|
-
const missingHooks = [];
|
|
301
|
-
try {
|
|
302
|
-
if (fs.existsSync(claudeSettingsPath)) {
|
|
303
|
-
const content = fs.readFileSync(claudeSettingsPath, "utf-8");
|
|
304
|
-
const settings = JSON.parse(content);
|
|
305
|
-
const hooks = settings.hooks || {};
|
|
306
|
-
for (const event of requiredEvents) {
|
|
307
|
-
const eventHooks = hooks[event];
|
|
308
|
-
if (!Array.isArray(eventHooks)) {
|
|
309
|
-
missingHooks.push(event);
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
// Check if any hook entry has a nested hook command containing session-signal
|
|
313
|
-
const found = eventHooks.some((entry) => {
|
|
314
|
-
const innerHooks = entry.hooks || [];
|
|
315
|
-
return innerHooks.some((h) => typeof h.command === "string" && h.command.includes("session-signal"));
|
|
316
|
-
});
|
|
317
|
-
if (!found)
|
|
318
|
-
missingHooks.push(event);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
else {
|
|
322
|
-
missingHooks.push(...requiredEvents);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
catch {
|
|
326
|
-
missingHooks.push(...requiredEvents);
|
|
327
|
-
}
|
|
259
|
+
const missingHooks = missingSessionSignalHooks();
|
|
328
260
|
if (missingHooks.length === 0) {
|
|
329
261
|
return { configured: true, missingHooks: [] };
|
|
330
262
|
}
|
|
331
263
|
return {
|
|
332
264
|
configured: false,
|
|
333
265
|
missingHooks,
|
|
334
|
-
hint: `Missing: ${missingHooks.join(", ")}. Run: santree helpers session-signal install`,
|
|
266
|
+
hint: `Missing: ${missingHooks.join(", ")}. Run: santree setup (or santree helpers session-signal install)`,
|
|
335
267
|
};
|
|
336
268
|
}
|
|
337
269
|
/**
|
|
@@ -470,9 +402,6 @@ function TrackerRow({ tracker }) {
|
|
|
470
402
|
const isOk = tracker.authenticated && !tracker.hint;
|
|
471
403
|
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsx(StatusIcon, { ok: isOk, required: true }), _jsx(Text, { children: " " }), _jsxs(Text, { bold: true, children: [tracker.displayName, " API"] }), _jsx(Text, { dimColor: true, children: " - Issue tracker integration" })] }), tracker.authenticated ? (_jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [tracker.accountLabel && _jsxs(Text, { dimColor: true, children: ["Account: ", tracker.accountLabel] }), tracker.repoLinked !== undefined && (_jsxs(Text, { dimColor: true, children: ["Repo linked: ", tracker.repoLinked ? "yes" : "no"] })), tracker.hint && _jsxs(Text, { color: "yellow", children: ["\u21B3 ", tracker.hint] })] })) : (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { color: "yellow", children: ["\u21B3 ", tracker.hint] }) }))] }));
|
|
472
404
|
}
|
|
473
|
-
function ShellRow({ configured, shell }) {
|
|
474
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsx(StatusIcon, { ok: configured, required: true }), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Shell Integration" }), _jsx(Text, { dimColor: true, children: " - Enables directory switching" })] }), configured ? (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { dimColor: true, children: ["Shell: ", shell] }) })) : (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { color: "yellow", children: ["\u21B3 Add to .", shell, "rc: eval \"$(santree helpers shell-init ", shell, ")\""] }) }))] }));
|
|
475
|
-
}
|
|
476
405
|
function StatuslineRow({ status }) {
|
|
477
406
|
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsx(StatusIcon, { ok: status.claudeSettingsConfigured, required: false }), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Claude Statusline" }), _jsx(Text, { dimColor: true, children: " - Custom statusline in Claude Code" }), _jsx(Text, { dimColor: true, children: " (optional)" })] }), _jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [status.currentCommand ? (_jsxs(Text, { dimColor: true, children: ["Command: ", status.currentCommand] })) : (_jsx(Text, { dimColor: true, children: "Command: not configured" })), status.hint && _jsxs(Text, { color: "yellow", children: ["\u21B3 ", status.hint] })] })] }));
|
|
478
407
|
}
|
|
@@ -508,7 +437,6 @@ function SantreeSetupRow({ status }) {
|
|
|
508
437
|
export default function Doctor() {
|
|
509
438
|
const [tools, setTools] = useState([]);
|
|
510
439
|
const [tracker, setTracker] = useState(null);
|
|
511
|
-
const [shellStatus, setShellStatus] = useState(null);
|
|
512
440
|
const [remoteControl, setRemoteControl] = useState(null);
|
|
513
441
|
const [statusline, setStatusline] = useState(null);
|
|
514
442
|
const [sessionSignal, setSessionSignal] = useState(null);
|
|
@@ -559,7 +487,7 @@ export default function Doctor() {
|
|
|
559
487
|
}
|
|
560
488
|
}
|
|
561
489
|
}
|
|
562
|
-
// Optional: a syntax-highlighting diff pager — used by `
|
|
490
|
+
// Optional: a syntax-highlighting diff pager — used by `santree worktree diff`
|
|
563
491
|
// and the dashboard `v` overlay when SANTREE_DIFF_TOOL is set. Any
|
|
564
492
|
// diff pager works (delta, diff-so-fancy, …); without one set, the
|
|
565
493
|
// dashboard renders inline with santree's own colorizer and the CLI
|
|
@@ -597,7 +525,6 @@ export default function Doctor() {
|
|
|
597
525
|
const statuslineResult = await checkStatusline();
|
|
598
526
|
setTools(results);
|
|
599
527
|
setTracker(trackerResult);
|
|
600
|
-
setShellStatus(checkShellIntegration());
|
|
601
528
|
setRemoteControl(checkRemoteControl());
|
|
602
529
|
setStatusline(statuslineResult);
|
|
603
530
|
setSessionSignal(checkSessionSignalHooks());
|
|
@@ -613,6 +540,6 @@ export default function Doctor() {
|
|
|
613
540
|
const requiredMissing = tools.filter((t) => t.required && (!t.installed || t.hint));
|
|
614
541
|
const optionalMissing = tools.filter((t) => !t.required && !t.installed);
|
|
615
542
|
const trackerOk = tracker?.authenticated && !tracker?.hint;
|
|
616
|
-
const allRequired = requiredMissing.length === 0 && trackerOk
|
|
617
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Santree Doctor" }), _jsxs(Text, { dimColor: true, children: [" v", version] })] }), _jsx(Box, { marginBottom: 1, flexDirection: "column", children: _jsx(Text, { bold: true, underline: true, children: "CLI Tools" }) }), tools.map((tool) => (_jsx(ToolRow, { tool: tool }, tool.name))), _jsx(Box, { marginBottom: 1, marginTop: 1, flexDirection: "column", children: _jsx(Text, { bold: true, underline: true, children: "Integrations" }) }), tracker && _jsx(TrackerRow, { tracker: tracker }),
|
|
543
|
+
const allRequired = requiredMissing.length === 0 && trackerOk;
|
|
544
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Santree Doctor" }), _jsxs(Text, { dimColor: true, children: [" v", version] })] }), _jsx(Box, { marginBottom: 1, flexDirection: "column", children: _jsx(Text, { bold: true, underline: true, children: "CLI Tools" }) }), tools.map((tool) => (_jsx(ToolRow, { tool: tool }, tool.name))), _jsx(Box, { marginBottom: 1, marginTop: 1, flexDirection: "column", children: _jsx(Text, { bold: true, underline: true, children: "Integrations" }) }), tracker && _jsx(TrackerRow, { tracker: tracker }), santreeSetup && _jsx(SantreeSetupRow, { status: santreeSetup }), _jsx(Box, { marginBottom: 1, marginTop: 1, flexDirection: "column", children: _jsx(Text, { bold: true, underline: true, children: "Claude Code" }) }), remoteControl && _jsx(RemoteControlRow, { status: remoteControl }), statusline && _jsx(StatuslineRow, { status: statusline }), sessionSignal && _jsx(SessionSignalRow, { status: sessionSignal }), englishTutor && _jsx(EnglishTutorRow, { status: englishTutor }), _jsx(Box, { marginTop: 1, borderStyle: "single", borderColor: allRequired ? "green" : "yellow", paddingX: 2, children: allRequired ? (_jsx(Text, { color: "green", children: "All requirements satisfied! Santree is ready to use." })) : (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "yellow", children: [requiredMissing.length + (trackerOk ? 0 : 1), " required item(s) need attention"] }), optionalMissing.length > 0 && (_jsxs(Text, { dimColor: true, children: [optionalMissing.length, " optional item(s) not installed"] }))] })) })] }));
|
|
618
545
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const description = "Guided setup \u2014 configure editor, diff tool, Claude Code & this repo";
|
|
3
|
+
export declare const options: z.ZodObject<{
|
|
4
|
+
dryRun: z.ZodDefault<z.ZodBoolean>;
|
|
5
|
+
yes: z.ZodDefault<z.ZodBoolean>;
|
|
6
|
+
}, z.core.$strip>;
|
|
7
|
+
type Props = {
|
|
8
|
+
options: z.infer<typeof options>;
|
|
9
|
+
};
|
|
10
|
+
export default function Setup({ options: opts }: Props): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { Box, Text, useInput } from "ink";
|
|
4
|
+
import Spinner from "ink-spinner";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { buildContext, buildSteps, } from "../lib/setup/steps.js";
|
|
7
|
+
export const description = "Guided setup — configure editor, diff tool, Claude Code & this repo";
|
|
8
|
+
export const options = z.object({
|
|
9
|
+
dryRun: z.boolean().default(false).describe("Preview what would change without writing anything"),
|
|
10
|
+
yes: z.boolean().default(false).describe("Apply all recommended steps non-interactively"),
|
|
11
|
+
});
|
|
12
|
+
export default function Setup({ options: opts }) {
|
|
13
|
+
const dryRun = opts.dryRun;
|
|
14
|
+
const [phase, setPhase] = useState("detecting");
|
|
15
|
+
const [steps, setSteps] = useState([]);
|
|
16
|
+
const [ctx, setCtx] = useState(null);
|
|
17
|
+
// select phase
|
|
18
|
+
const [cursor, setCursor] = useState(0);
|
|
19
|
+
const [selected, setSelected] = useState(new Set());
|
|
20
|
+
// configure phase
|
|
21
|
+
const [configQueue, setConfigQueue] = useState([]);
|
|
22
|
+
const [configPos, setConfigPos] = useState(0);
|
|
23
|
+
const [optionCursor, setOptionCursor] = useState(0);
|
|
24
|
+
const [choices, setChoices] = useState({});
|
|
25
|
+
// applying phase
|
|
26
|
+
const [applyIdx, setApplyIdx] = useState(0);
|
|
27
|
+
const [applyOrder, setApplyOrder] = useState([]);
|
|
28
|
+
const [applied, setApplied] = useState([]);
|
|
29
|
+
const [running, setRunning] = useState(null);
|
|
30
|
+
// ── Detect ────────────────────────────────────────────────────────────────
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
(async () => {
|
|
33
|
+
await new Promise((r) => setTimeout(r, 80)); // let the spinner paint first
|
|
34
|
+
const c = buildContext(dryRun);
|
|
35
|
+
const all = buildSteps(c).filter((s) => s.detect === "actionable");
|
|
36
|
+
setCtx(c);
|
|
37
|
+
setSteps(all);
|
|
38
|
+
if (all.length === 0) {
|
|
39
|
+
setPhase("done");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const rec = new Set();
|
|
43
|
+
all.forEach((s, i) => {
|
|
44
|
+
if (s.recommended)
|
|
45
|
+
rec.add(i);
|
|
46
|
+
});
|
|
47
|
+
setSelected(rec);
|
|
48
|
+
if (opts.yes) {
|
|
49
|
+
beginApply(all, rec, {});
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
setPhase("select");
|
|
53
|
+
}
|
|
54
|
+
})();
|
|
55
|
+
}, []);
|
|
56
|
+
function beginApply(allSteps, sel, picked) {
|
|
57
|
+
const order = [...sel].sort((a, b) => a - b);
|
|
58
|
+
// For steps with options that weren't interactively chosen, default to the
|
|
59
|
+
// first option (used by --yes and single-option steps).
|
|
60
|
+
const merged = { ...picked };
|
|
61
|
+
for (const i of order) {
|
|
62
|
+
const s = allSteps[i];
|
|
63
|
+
if (s.options && s.options.length > 0 && merged[s.id] === undefined) {
|
|
64
|
+
merged[s.id] = s.options[0].value;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
setChoices(merged);
|
|
68
|
+
setApplyOrder(order);
|
|
69
|
+
setApplyIdx(0);
|
|
70
|
+
setApplied([]);
|
|
71
|
+
setPhase("applying");
|
|
72
|
+
}
|
|
73
|
+
// ── Select phase input ──────────────────────────────────────────────────────
|
|
74
|
+
useInput((input, key) => {
|
|
75
|
+
if (key.escape)
|
|
76
|
+
process.exit(0);
|
|
77
|
+
else if (key.upArrow)
|
|
78
|
+
setCursor((c) => Math.max(0, c - 1));
|
|
79
|
+
else if (key.downArrow)
|
|
80
|
+
setCursor((c) => Math.min(steps.length - 1, c + 1));
|
|
81
|
+
else if (input === " ") {
|
|
82
|
+
setSelected((s) => {
|
|
83
|
+
const next = new Set(s);
|
|
84
|
+
if (next.has(cursor))
|
|
85
|
+
next.delete(cursor);
|
|
86
|
+
else
|
|
87
|
+
next.add(cursor);
|
|
88
|
+
return next;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
else if (key.return) {
|
|
92
|
+
// Steps needing an interactive choice (>1 option).
|
|
93
|
+
const queue = [...selected]
|
|
94
|
+
.sort((a, b) => a - b)
|
|
95
|
+
.filter((i) => (steps[i].options?.length ?? 0) > 1);
|
|
96
|
+
if (queue.length > 0) {
|
|
97
|
+
setConfigQueue(queue);
|
|
98
|
+
setConfigPos(0);
|
|
99
|
+
setOptionCursor(0);
|
|
100
|
+
setPhase("configure");
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
beginApply(steps, selected, {});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}, { isActive: phase === "select" });
|
|
107
|
+
// ── Configure phase input ───────────────────────────────────────────────────
|
|
108
|
+
useInput((_input, key) => {
|
|
109
|
+
if (key.escape) {
|
|
110
|
+
setPhase("select");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const stepIdx = configQueue[configPos];
|
|
114
|
+
const step = steps[stepIdx];
|
|
115
|
+
const optCount = step.options.length;
|
|
116
|
+
if (key.upArrow)
|
|
117
|
+
setOptionCursor((c) => Math.max(0, c - 1));
|
|
118
|
+
else if (key.downArrow)
|
|
119
|
+
setOptionCursor((c) => Math.min(optCount - 1, c + 1));
|
|
120
|
+
else if (key.return) {
|
|
121
|
+
const value = step.options[optionCursor].value;
|
|
122
|
+
const nextChoices = { ...choices, [step.id]: value };
|
|
123
|
+
setChoices(nextChoices);
|
|
124
|
+
if (configPos + 1 < configQueue.length) {
|
|
125
|
+
setConfigPos((p) => p + 1);
|
|
126
|
+
setOptionCursor(0);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
beginApply(steps, selected, nextChoices);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}, { isActive: phase === "configure" });
|
|
133
|
+
// ── Applying phase ──────────────────────────────────────────────────────────
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
if (phase !== "applying")
|
|
136
|
+
return;
|
|
137
|
+
if (applyIdx >= applyOrder.length) {
|
|
138
|
+
setRunning(null);
|
|
139
|
+
setPhase("done");
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const step = steps[applyOrder[applyIdx]];
|
|
143
|
+
setRunning(step.title);
|
|
144
|
+
let cancelled = false;
|
|
145
|
+
(async () => {
|
|
146
|
+
await new Promise((r) => setTimeout(r, 60)); // paint "running" frame
|
|
147
|
+
let result;
|
|
148
|
+
try {
|
|
149
|
+
result = await step.apply(choices[step.id]);
|
|
150
|
+
}
|
|
151
|
+
catch (e) {
|
|
152
|
+
result = { ok: false, message: e instanceof Error ? e.message : String(e) };
|
|
153
|
+
}
|
|
154
|
+
if (cancelled)
|
|
155
|
+
return;
|
|
156
|
+
setApplied((prev) => [...prev, { step, result }]);
|
|
157
|
+
setApplyIdx((i) => i + 1);
|
|
158
|
+
})();
|
|
159
|
+
return () => {
|
|
160
|
+
cancelled = true;
|
|
161
|
+
};
|
|
162
|
+
}, [phase, applyIdx]);
|
|
163
|
+
// ── Exit ────────────────────────────────────────────────────────────────────
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
if (phase === "done") {
|
|
166
|
+
const t = setTimeout(() => process.exit(0), 120);
|
|
167
|
+
return () => clearTimeout(t);
|
|
168
|
+
}
|
|
169
|
+
}, [phase]);
|
|
170
|
+
// ── Render ──────────────────────────────────────────────────────────────────
|
|
171
|
+
const title = (_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "santree setup" }), dryRun && _jsx(Text, { color: "yellow", children: " (dry run \u2014 nothing will be written)" })] }));
|
|
172
|
+
if (phase === "detecting") {
|
|
173
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [title, _jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { children: " Detecting your setup\u2026" })] })] }));
|
|
174
|
+
}
|
|
175
|
+
if (phase === "done" && steps.length === 0) {
|
|
176
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [title, _jsx(Text, { color: "green", children: "\u2713 Everything's already configured \u2014 you're all set." }), !ctx?.repoRoot && (_jsx(Text, { dimColor: true, children: "Tip: run this again inside a git repo to configure repo-specific bits." }))] }));
|
|
177
|
+
}
|
|
178
|
+
if (phase === "select") {
|
|
179
|
+
let lastScope = null;
|
|
180
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [title, _jsx(Text, { children: "What should I set up? (Space toggles, Enter applies)" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: steps.map((s, i) => {
|
|
181
|
+
const head = s.scope !== lastScope;
|
|
182
|
+
lastScope = s.scope;
|
|
183
|
+
const box = selected.has(i) ? "[x]" : "[ ]";
|
|
184
|
+
const isCur = i === cursor;
|
|
185
|
+
return (_jsxs(Box, { flexDirection: "column", children: [head && _jsx(Text, { dimColor: true, children: s.scope === "global" ? "Global" : "This repo" }), _jsxs(Text, { children: [_jsxs(Text, { color: isCur ? "cyan" : undefined, bold: isCur, children: [isCur ? "> " : " ", box] }), " ", s.title, s.kind === "spawn" && _jsx(Text, { color: "yellow", children: " \u2937 runs a command" })] }), isCur && _jsx(Text, { dimColor: true, children: " " + s.detail })] }, s.id));
|
|
186
|
+
}) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "\u2191/\u2193 move \u00B7 Space toggle \u00B7 Enter apply \u00B7 Esc cancel" }) })] }));
|
|
187
|
+
}
|
|
188
|
+
if (phase === "configure") {
|
|
189
|
+
const step = steps[configQueue[configPos]];
|
|
190
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [title, _jsx(Text, { bold: true, children: step.title }), _jsx(Text, { children: step.optionPrompt || "Pick one:" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: step.options.map((o, i) => (_jsxs(Text, { children: [_jsx(Text, { color: i === optionCursor ? "cyan" : undefined, bold: i === optionCursor, children: i === optionCursor ? "> " : " " }), o.label] }, o.value))) }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["\u2191/\u2193 select \u00B7 Enter confirm", configQueue.length > 1 ? ` (${configPos + 1}/${configQueue.length})` : ""] }) })] }));
|
|
191
|
+
}
|
|
192
|
+
// applying / done
|
|
193
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [title, applied.map(({ step, result }, i) => (_jsxs(Text, { color: result.ok ? "green" : "red", children: [result.ok ? "✓" : "✗", " ", step.title, " ", _jsxs(Text, { dimColor: true, children: ["\u2014 ", result.message] })] }, i))), phase === "applying" && running && (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: _jsx(Spinner, { type: "dots" }) }), _jsxs(Text, { children: [" ", running, "\u2026"] })] })), phase === "done" && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "green", bold: true, children: "Done. Run `santree doctor` to verify." }), !dryRun && ctx?.shell && (_jsxs(Text, { dimColor: true, children: ["Restart your shell (or `source ", ctx.shell.rcPath, "`) to load the changes."] }))] }))] }));
|
|
194
|
+
}
|
|
@@ -6,6 +6,7 @@ import { z } from "zod";
|
|
|
6
6
|
import * as fs from "fs";
|
|
7
7
|
import { createWorktree, findMainRepoRoot, getDefaultBranch, pullLatest, hasInitScript, getInitScriptPath, extractTicketId, } from "../../lib/git.js";
|
|
8
8
|
import { spawnAsync } from "../../lib/exec.js";
|
|
9
|
+
import { formatCdCommand } from "../../lib/cd-hint.js";
|
|
9
10
|
import { getMultiplexer } from "../../lib/multiplexer/index.js";
|
|
10
11
|
export const description = "Create a new worktree from a branch";
|
|
11
12
|
export const options = z.object({
|
|
@@ -36,6 +37,7 @@ export default function Create({ options, args }) {
|
|
|
36
37
|
const [worktreePath, setWorktreePath] = useState("");
|
|
37
38
|
const [baseBranch, setBaseBranch] = useState(null);
|
|
38
39
|
const [muxWindowName, setMuxWindowName] = useState(null);
|
|
40
|
+
const [cdHint, setCdHint] = useState(null);
|
|
39
41
|
async function finalize(path, branch) {
|
|
40
42
|
const wantsWindow = options.window || options.tmux;
|
|
41
43
|
if (wantsWindow) {
|
|
@@ -43,7 +45,7 @@ export default function Create({ options, args }) {
|
|
|
43
45
|
if (!mux.isActive()) {
|
|
44
46
|
setMessage("Worktree created, but no active multiplexer");
|
|
45
47
|
setStatus("done");
|
|
46
|
-
|
|
48
|
+
setCdHint(formatCdCommand({ path }));
|
|
47
49
|
return;
|
|
48
50
|
}
|
|
49
51
|
setStatus("spawning-window");
|
|
@@ -52,28 +54,27 @@ export default function Create({ options, args }) {
|
|
|
52
54
|
setMuxWindowName(windowName);
|
|
53
55
|
let runCommand;
|
|
54
56
|
if (options.work) {
|
|
55
|
-
runCommand = options.plan ? "
|
|
57
|
+
runCommand = options.plan ? "santree worktree work --plan" : "santree worktree work";
|
|
56
58
|
}
|
|
57
59
|
const result = await mux.createWindow({ name: windowName, cwd: path, command: runCommand });
|
|
58
60
|
if (!result.ok) {
|
|
59
61
|
setMessage(`Worktree created, but failed to create window${result.message ? `: ${result.message}` : ""}`);
|
|
60
62
|
setStatus("done");
|
|
61
|
-
|
|
63
|
+
setCdHint(formatCdCommand({ path }));
|
|
62
64
|
return;
|
|
63
65
|
}
|
|
64
66
|
setStatus("done");
|
|
65
67
|
const workInfo = options.work ? (options.plan ? " + Claude (plan)" : " + Claude") : "";
|
|
66
68
|
setMessage(`Worktree and window created!${workInfo}`);
|
|
67
|
-
//
|
|
69
|
+
// No cd hint when a window is created — the user is already in the new window
|
|
68
70
|
return;
|
|
69
71
|
}
|
|
70
72
|
setStatus("done");
|
|
71
73
|
setMessage("Worktree created successfully!");
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
74
|
+
setCdHint(formatCdCommand({
|
|
75
|
+
path,
|
|
76
|
+
work: options.work ? { mode: options.plan ? "plan" : "implement" } : undefined,
|
|
77
|
+
}));
|
|
77
78
|
}
|
|
78
79
|
useEffect(() => {
|
|
79
80
|
async function run() {
|
|
@@ -159,5 +160,5 @@ export default function Create({ options, args }) {
|
|
|
159
160
|
status === "creating" ||
|
|
160
161
|
status === "init-script" ||
|
|
161
162
|
status === "spawning-window";
|
|
162
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 1, width: "100%", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "\uD83C\uDF31 Create Worktree" }) }), branchName && (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: status === "error" ? "red" : status === "done" ? "green" : "blue", paddingX: 1, width: "100%", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "branch:" }), _jsx(Text, { color: "cyan", bold: true, children: branchName })] }), baseBranch && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "base:" }), _jsx(Text, { color: "blue", children: baseBranch })] })), options["no-pull"] && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "skip pull:" }), _jsx(Text, { color: "yellow", children: "yes" })] })), options.work && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "after:" }), _jsx(Text, { backgroundColor: "magenta", color: "white", children: options.plan ? " plan " : " work " })] })), (options.window || options.tmux) && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "window:" }), _jsx(Text, { backgroundColor: "green", color: "white", children: ` ${options.name || "auto"} ` })] }))] })), _jsxs(Box, { marginTop: 1, children: [isLoading && (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: _jsx(Spinner, { type: "dots" }) }), _jsxs(Text, { children: [" ", message] })] })), status === "done" && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "green", bold: true, children: ["\u2713 ", message] }), _jsxs(Text, { dimColor: true, children: [" ", worktreePath] }), muxWindowName && _jsxs(Text, { dimColor: true, children: [" window: ", muxWindowName] })] })), status === "error" && (_jsxs(Text, { color: "red", bold: true, children: ["\u2717 ", message] }))] })] }));
|
|
163
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, width: "100%", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "\uD83C\uDF31 Create Worktree" }) }), branchName && (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: status === "error" ? "red" : status === "done" ? "green" : "blue", paddingX: 1, width: "100%", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "branch:" }), _jsx(Text, { color: "cyan", bold: true, children: branchName })] }), baseBranch && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "base:" }), _jsx(Text, { color: "blue", children: baseBranch })] })), options["no-pull"] && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "skip pull:" }), _jsx(Text, { color: "yellow", children: "yes" })] })), options.work && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "after:" }), _jsx(Text, { backgroundColor: "magenta", color: "white", children: options.plan ? " plan " : " work " })] })), (options.window || options.tmux) && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "window:" }), _jsx(Text, { backgroundColor: "green", color: "white", children: ` ${options.name || "auto"} ` })] }))] })), _jsxs(Box, { marginTop: 1, children: [isLoading && (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: _jsx(Spinner, { type: "dots" }) }), _jsxs(Text, { children: [" ", message] })] })), status === "done" && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "green", bold: true, children: ["\u2713 ", message] }), _jsxs(Text, { dimColor: true, children: [" ", worktreePath] }), muxWindowName && _jsxs(Text, { dimColor: true, children: [" window: ", muxWindowName] }), cdHint && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "\u2192 Run this to enter the worktree:" }), _jsxs(Text, { color: "cyan", children: [" ", cdHint] })] }))] })), status === "error" && (_jsxs(Text, { color: "red", bold: true, children: ["\u2717 ", message] }))] })] }));
|
|
163
164
|
}
|