opencode-sesh 0.1.6 → 0.2.0
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/AGENTS.md +69 -16
- package/CHANGELOG.md +62 -1
- package/README.md +45 -22
- package/bin/sesh-delete.sh +38 -4
- package/bin/sesh-list.sh +57 -9
- package/bin/sesh-shortcuts.sh +3 -2
- package/bin/sesh.sh +59 -16
- package/install.sh +109 -76
- package/package.json +7 -4
- package/tui/sesh-panel.tsx +414 -70
package/bin/sesh.sh
CHANGED
|
@@ -38,6 +38,18 @@ fi
|
|
|
38
38
|
if [ "${1:-}" = --check ]; then
|
|
39
39
|
"$SESH_JQ" -en '"2026-01-01T00:00:00Z" | fromdateiso8601 | type == "number"' >/dev/null || { echo 'jq capability check failed' >&2; exit 1; }
|
|
40
40
|
command -v sqlite3 >/dev/null 2>&1 || command -v "$OPENCODE_BIN" >/dev/null 2>&1 || { echo 'neither sqlite3 nor opencode is available' >&2; exit 1; }
|
|
41
|
+
# The TUI panel is a copied file that opencode imports once at startup. Report
|
|
42
|
+
# when the installed copy has fallen behind this package so upgrades are not
|
|
43
|
+
# silently stale.
|
|
44
|
+
panel_bundled="$SCRIPT_DIR/../tui/sesh-panel.tsx"
|
|
45
|
+
panel_installed="${XDG_CONFIG_HOME:-$HOME/.config}/opencode/plugins/sesh-panel.tsx"
|
|
46
|
+
if [ -f "$panel_installed" ] && [ -f "$panel_bundled" ]; then
|
|
47
|
+
if cmp -s "$panel_bundled" "$panel_installed"; then
|
|
48
|
+
echo 'sesh: sidebar panel is current'
|
|
49
|
+
else
|
|
50
|
+
echo 'sesh: sidebar panel is out of date; run `sesh install`, then fully quit and reopen opencode' >&2
|
|
51
|
+
fi
|
|
52
|
+
fi
|
|
41
53
|
echo 'sesh: dependencies ready'
|
|
42
54
|
exit 0
|
|
43
55
|
fi
|
|
@@ -47,6 +59,7 @@ picker_limit=0
|
|
|
47
59
|
picker_archived=0
|
|
48
60
|
picker_print=0
|
|
49
61
|
picker_fork=0
|
|
62
|
+
picker_json=0
|
|
50
63
|
picker_query=''
|
|
51
64
|
while [ "$#" -gt 0 ]; do
|
|
52
65
|
case "$1" in
|
|
@@ -55,12 +68,14 @@ while [ "$#" -gt 0 ]; do
|
|
|
55
68
|
--archived) picker_archived=1 ;;
|
|
56
69
|
--print) picker_print=1 ;;
|
|
57
70
|
--fork) picker_fork=1 ;;
|
|
71
|
+
--json) picker_json=1 ;;
|
|
58
72
|
--query) picker_query=${2:-}; shift ;;
|
|
59
73
|
*) echo "unknown option: $1" >&2; exit 2 ;;
|
|
60
74
|
esac
|
|
61
75
|
shift
|
|
62
76
|
done
|
|
63
77
|
case "$picker_limit" in ''|*[!0-9]*) echo "--limit must be a non-negative integer" >&2; exit 2;; esac
|
|
78
|
+
[ "$picker_json" = 0 ] || [ "$picker_print" = 1 ] || { echo '--json requires --print' >&2; exit 2; }
|
|
64
79
|
|
|
65
80
|
shell_quote() {
|
|
66
81
|
# Quotes fixed command arguments embedded in fzf's shell action strings.
|
|
@@ -94,6 +109,12 @@ trap cleanup EXIT
|
|
|
94
109
|
trap 'exit 130' HUP INT TERM
|
|
95
110
|
|
|
96
111
|
cache_cmd="$q_worker render --state-dir $q_state --query {q} --selected-id {6}"
|
|
112
|
+
# fzf's --header is the only place a persistent scope/status line can live
|
|
113
|
+
# without becoming a selectable row, so it is refreshed by transform-header on
|
|
114
|
+
# start, on the 3s poll and after either action that can change it. Order
|
|
115
|
+
# matters: a transform-* action placed AFTER reload/reload-sync in the chain
|
|
116
|
+
# leaves the list empty, so the header is always transformed first.
|
|
117
|
+
header_cmd="$q_list --header --state-dir $q_state"
|
|
97
118
|
preview_cmd="if [ -f $q_state/help ]; then $q_shortcuts; else $q_preview $q_state {2}; fi"
|
|
98
119
|
help_bind="?:transform:[ -z {q} ] && ( [ -f $q_state/help ] && rm -f $q_state/help && echo hide-preview || ( : > $q_state/help && echo show-preview+refresh-preview ) ) || echo 'put(?)'"
|
|
99
120
|
toggle_cmd="$q_list --toggle-scope --state-dir $q_state >/dev/null"
|
|
@@ -101,16 +122,18 @@ delete_cmd="$q_delete $q_state {2}"
|
|
|
101
122
|
|
|
102
123
|
query="$picker_query"
|
|
103
124
|
while :; do
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
--
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
125
|
+
header_line=$("$SCRIPT_DIR/sesh-list.sh" --header --state-dir "$state_dir" 2>/dev/null || true)
|
|
126
|
+
selection=''
|
|
127
|
+
set +e
|
|
128
|
+
selection=$(FZF_DEFAULT_COMMAND='printf ""' FZF_DEFAULT_OPTS='' FZF_DEFAULT_OPTS_FILE='' "$SESH_FZF" --print-query --expect=ctrl-f --query="$query" --ansi --delimiter=$'\t' --with-nth=4 --track --id-nth=6 --disabled --layout=reverse --info=hidden --prompt='❯ ' \
|
|
129
|
+
--preview="$preview_cmd" --preview-window='right,50%,wrap,hidden' \
|
|
130
|
+
--header-first --header="$header_line" \
|
|
131
|
+
--bind='space:transform:[ -z {q} ] && echo toggle-preview || echo "put( )"' \
|
|
132
|
+
--bind="$help_bind" \
|
|
133
|
+
--bind="ctrl-x:execute($delete_cmd)+transform-header($header_cmd)+reload($cache_cmd)" \
|
|
134
|
+
--bind="ctrl-g:execute($toggle_cmd)+transform-header($header_cmd)+reload($cache_cmd)" \
|
|
135
|
+
--bind="change:reload:$cache_cmd" \
|
|
136
|
+
--bind="start,every(3):transform-header($header_cmd)+reload-sync:$cache_cmd")
|
|
114
137
|
status=$?
|
|
115
138
|
set -e
|
|
116
139
|
case "$status" in 0) ;; 1|130) exit 0;; *) echo "sesh: picker failed ($status)" >&2; exit "$status";; esac
|
|
@@ -120,13 +143,30 @@ selection=${selection#*$'\n'}
|
|
|
120
143
|
key=${selection%%$'\n'*}
|
|
121
144
|
selection=${selection#*$'\n'}
|
|
122
145
|
session_id=$(printf '%s\n' "$selection" | cut -f2)
|
|
123
|
-
|
|
146
|
+
tracking_id=$(printf '%s\n' "$selection" | cut -f6)
|
|
147
|
+
if [ -z "$session_id" ]; then
|
|
148
|
+
# Enter/Ctrl-F on a directory header or a notice row. Leave a one-shot
|
|
149
|
+
# message for the reopened picker so the keypress visibly did something,
|
|
150
|
+
# and keep the query so the user has not lost their place.
|
|
151
|
+
case "$tracking_id" in
|
|
152
|
+
hdr:*) printf '%s' 'That row is a directory header, not a session.' > "$state_dir/action-notice" ;;
|
|
153
|
+
notice:*) printf '%s' 'That row is a notice, not a session; pick a session row.' > "$state_dir/action-notice" ;;
|
|
154
|
+
esac
|
|
155
|
+
continue
|
|
156
|
+
fi
|
|
124
157
|
# Dispatch queues a new worker scan and waits for that exact serialized poll.
|
|
125
158
|
# It cannot accept a previously fresh status while the worker owns its lock.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
159
|
+
# A --print lookup reads the published snapshot, so it skips the wait entirely
|
|
160
|
+
# and stays usable from scripts without paying a rescan.
|
|
161
|
+
if [ "$picker_print" = 0 ]; then
|
|
162
|
+
printf 'sesh: refreshing session state…' >&2
|
|
163
|
+
poll_target=$("$SCRIPT_DIR/sesh-refresh-worker.sh" request --state-dir "$state_dir")
|
|
164
|
+
if ! "$SCRIPT_DIR/sesh-refresh-worker.sh" wait --state-dir "$state_dir" --target "$poll_target" --timeout 12; then
|
|
165
|
+
printf '\r\033[K' >&2
|
|
166
|
+
echo 'sesh: session state unavailable; selection cancelled, retry after refresh' >&2
|
|
167
|
+
continue
|
|
168
|
+
fi
|
|
169
|
+
printf '\r\033[K' >&2
|
|
130
170
|
fi
|
|
131
171
|
record=$("$SESH_JQ" -c --arg sid "$session_id" 'select(.sessionId==$sid)' "$state_dir/snapshot.jsonl")
|
|
132
172
|
[ -n "$record" ] || continue
|
|
@@ -134,7 +174,10 @@ cwd=$(printf '%s' "$record" | "$SESH_JQ" -r '.cwd')
|
|
|
134
174
|
fork=0
|
|
135
175
|
if [ "$picker_fork" = 1 ] || [ "$key" = ctrl-f ]; then fork=1; fi
|
|
136
176
|
if [ "$picker_print" = 1 ]; then
|
|
137
|
-
if [ "$
|
|
177
|
+
if [ "$picker_json" = 1 ]; then
|
|
178
|
+
"$SESH_JQ" -cn --arg sessionId "$session_id" --arg cwd "$cwd" --argjson fork "$fork" \
|
|
179
|
+
'{sessionId: $sessionId, cwd: $cwd, fork: ($fork == 1)}'
|
|
180
|
+
elif [ "$fork" = 1 ]; then printf '%s\t%s\tfork\n' "$session_id" "$cwd"; else printf '%s\t%s\n' "$session_id" "$cwd"; fi
|
|
138
181
|
exit 0
|
|
139
182
|
fi
|
|
140
183
|
if [ ! -d "$cwd" ]; then
|
package/install.sh
CHANGED
|
@@ -19,6 +19,7 @@ BIN_DIR="${XDG_BIN_HOME:-$HOME/.local/bin}"
|
|
|
19
19
|
INSTALL_TOOL=1
|
|
20
20
|
INSTALL_TUI=1
|
|
21
21
|
UNINSTALL=0
|
|
22
|
+
SYNC_PANEL=0
|
|
22
23
|
|
|
23
24
|
# Fallback plugin version for when the opencode CLI is unavailable; the
|
|
24
25
|
# installer prefers the local opencode version so the panel matches its SDK.
|
|
@@ -43,6 +44,7 @@ Usage: bash install.sh [--bin-dir DIR] [--no-tool] [--no-tui] [--uninstall]
|
|
|
43
44
|
--no-tool Skip installing the sesh-list custom tool.
|
|
44
45
|
--no-tui Skip installing the in-TUI sessions panel plugin.
|
|
45
46
|
--uninstall Remove everything this script installed.
|
|
47
|
+
--sync-panel Refresh an already-installed TUI panel only (used by postinstall).
|
|
46
48
|
--help Show this help.
|
|
47
49
|
USAGE
|
|
48
50
|
}
|
|
@@ -53,6 +55,7 @@ while [ "$#" -gt 0 ]; do
|
|
|
53
55
|
--no-tool) INSTALL_TOOL=0; shift ;;
|
|
54
56
|
--no-tui) INSTALL_TUI=0; shift ;;
|
|
55
57
|
--uninstall) UNINSTALL=1; shift ;;
|
|
58
|
+
--sync-panel) SYNC_PANEL=1; shift ;;
|
|
56
59
|
--help) usage; exit 0 ;;
|
|
57
60
|
*) fail "unknown option: $1" ;;
|
|
58
61
|
esac
|
|
@@ -110,62 +113,6 @@ launcher_is_ours() {
|
|
|
110
113
|
[ -f "$target" ] && grep -qF 'Pick an opencode session' "$target" 2>/dev/null
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
if [ "$UNINSTALL" = 1 ]; then
|
|
114
|
-
if [ -L "$LAUNCHER" ]; then
|
|
115
|
-
if launcher_is_ours "$LAUNCHER"; then
|
|
116
|
-
rm -f "$LAUNCHER" && note "removed $LAUNCHER"
|
|
117
|
-
else
|
|
118
|
-
note "left $LAUNCHER untouched (not a sesh launcher: $(readlink "$LAUNCHER" 2>/dev/null))"
|
|
119
|
-
fi
|
|
120
|
-
fi
|
|
121
|
-
remove_if_marker "$COMMAND_DST" "rich interactive picker" && note "removed $COMMAND_DST"
|
|
122
|
-
remove_if_marker "$TOOL_DST" "list opencode sessions for the agent" && note "removed $TOOL_DST"
|
|
123
|
-
remove_if_marker "$PANEL_DST" "@jsxImportSource @opentui/solid" && note "removed $PANEL_DST"
|
|
124
|
-
remove_tui_entry && note "unregistered the panel from $TUI_JSON"
|
|
125
|
-
note "uninstalled (extraction cache and plugin dependencies left intact)"
|
|
126
|
-
exit 0
|
|
127
|
-
fi
|
|
128
|
-
|
|
129
|
-
for path in \
|
|
130
|
-
bin/sesh \
|
|
131
|
-
bin/sesh.sh \
|
|
132
|
-
bin/sesh-list.sh \
|
|
133
|
-
bin/sesh-preview.sh \
|
|
134
|
-
bin/sesh-delete.sh \
|
|
135
|
-
bin/sesh-refresh-worker.sh \
|
|
136
|
-
bin/sesh-shortcuts.sh \
|
|
137
|
-
opencode/tools/sesh-list.ts \
|
|
138
|
-
tui/sesh-panel.tsx \
|
|
139
|
-
themes/glow-dark-clean.json; do
|
|
140
|
-
[ -f "$SOURCE_DIR/$path" ] || fail "installer bundle is incomplete: missing $path."
|
|
141
|
-
done
|
|
142
|
-
|
|
143
|
-
for tool in bash jq fzf; do
|
|
144
|
-
command -v "$tool" >/dev/null 2>&1 || fail "required tool '$tool' is unavailable."
|
|
145
|
-
done
|
|
146
|
-
fzf_version=$(fzf --version 2>/dev/null || true)
|
|
147
|
-
if [[ "$fzf_version" =~ ^([0-9]+)\.([0-9]+)(\.([0-9]+))? ]]; then
|
|
148
|
-
if [ "$((10#${BASH_REMATCH[1]}))" -eq 0 ] && [ "$((10#${BASH_REMATCH[2]}))" -lt 73 ]; then
|
|
149
|
-
fail "fzf >= 0.73.0 is required (found $fzf_version)."
|
|
150
|
-
fi
|
|
151
|
-
else
|
|
152
|
-
fail "cannot parse fzf version from: $fzf_version."
|
|
153
|
-
fi
|
|
154
|
-
command -v sqlite3 >/dev/null 2>&1 || command -v opencode >/dev/null 2>&1 \
|
|
155
|
-
|| fail "either sqlite3 or the opencode CLI is required."
|
|
156
|
-
command -v opencode >/dev/null 2>&1 || note "opencode CLI not found: listing and previews work via sqlite3, but resume and delete need opencode."
|
|
157
|
-
|
|
158
|
-
umask 077
|
|
159
|
-
mkdir -p "$BIN_DIR"
|
|
160
|
-
if [ -e "$LAUNCHER" ] && [ ! -L "$LAUNCHER" ]; then
|
|
161
|
-
fail "$LAUNCHER already exists and is not a symlink. Move it aside, then retry."
|
|
162
|
-
fi
|
|
163
|
-
if [ -L "$LAUNCHER" ] && ! launcher_is_ours "$LAUNCHER"; then
|
|
164
|
-
fail "$LAUNCHER is a symlink to $(readlink "$LAUNCHER" 2>/dev/null), which is not a sesh install. Move it aside, then retry."
|
|
165
|
-
fi
|
|
166
|
-
ln -sfn "$SOURCE_DIR/bin/sesh" "$LAUNCHER"
|
|
167
|
-
note "linked $LAUNCHER -> $SOURCE_DIR/bin/sesh"
|
|
168
|
-
|
|
169
116
|
install_file() {
|
|
170
117
|
local src=$1 dst=$2
|
|
171
118
|
mkdir -p "$(dirname "$dst")"
|
|
@@ -181,26 +128,20 @@ install_file() {
|
|
|
181
128
|
note "installed $dst"
|
|
182
129
|
}
|
|
183
130
|
|
|
184
|
-
|
|
185
|
-
# agent; the TUI plugin now registers the slash itself. Drop the old file so the
|
|
186
|
-
# two do not collide.
|
|
187
|
-
remove_if_marker "$COMMAND_DST" "rich interactive picker" && note "removed obsolete markdown command $COMMAND_DST"
|
|
188
|
-
[ "$INSTALL_TOOL" = 1 ] && install_file "$SOURCE_DIR/opencode/tools/sesh-list.ts" "$TOOL_DST"
|
|
189
|
-
|
|
190
|
-
opencode_version=''
|
|
191
|
-
if command -v opencode >/dev/null 2>&1; then
|
|
192
|
-
raw_version=$(opencode --version 2>/dev/null || true)
|
|
193
|
-
if [[ "$raw_version" =~ ([0-9]+\.[0-9]+\.[0-9]+) ]]; then opencode_version="${BASH_REMATCH[1]}"; fi
|
|
194
|
-
fi
|
|
195
|
-
plugin_version="${opencode_version:-$PLUGIN_VERSION_FALLBACK}"
|
|
196
|
-
|
|
197
|
-
if [ "$INSTALL_TUI" = 1 ]; then
|
|
131
|
+
install_tui_panel() {
|
|
198
132
|
install_file "$SOURCE_DIR/tui/sesh-panel.tsx" "$PANEL_DST"
|
|
199
133
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
134
|
+
local opencode_version='' raw_version='' plugin_version=''
|
|
135
|
+
if command -v opencode >/dev/null 2>&1; then
|
|
136
|
+
raw_version=$(opencode --version 2>/dev/null || true)
|
|
137
|
+
if [[ "$raw_version" =~ ([0-9]+\.[0-9]+\.[0-9]+) ]]; then opencode_version="${BASH_REMATCH[1]}"; fi
|
|
138
|
+
fi
|
|
139
|
+
plugin_version="${opencode_version:-$PLUGIN_VERSION_FALLBACK}"
|
|
140
|
+
|
|
141
|
+
# opencode has no persistent-sidebar API: the panel is an xlarge modal dialog
|
|
142
|
+
# (see tui/sesh-panel.tsx). Register it idempotently and drop the previous
|
|
143
|
+
# filename if an older install left it behind.
|
|
144
|
+
local tui_created=0 tui_tmp=''
|
|
204
145
|
if [ ! -f "$TUI_JSON" ]; then
|
|
205
146
|
printf '{\n "$schema": "https://opencode.ai/tui.json",\n "plugin": []\n}\n' > "$TUI_JSON"
|
|
206
147
|
tui_created=1
|
|
@@ -228,7 +169,7 @@ if [ "$INSTALL_TUI" = 1 ]; then
|
|
|
228
169
|
# Local plugins resolve their imports from the config directory, and opencode
|
|
229
170
|
# runs `bun install` there at startup. Keep the required packages declared
|
|
230
171
|
# without clobbering versions the user already pinned.
|
|
231
|
-
pkg_created=0
|
|
172
|
+
local pkg_created=0 pkg_tmp=''
|
|
232
173
|
if [ ! -f "$PACKAGE_JSON" ]; then
|
|
233
174
|
printf '{\n "dependencies": {}\n}\n' > "$PACKAGE_JSON"
|
|
234
175
|
pkg_created=1
|
|
@@ -257,12 +198,104 @@ if [ "$INSTALL_TUI" = 1 ]; then
|
|
|
257
198
|
rm -f "$pkg_tmp"
|
|
258
199
|
note "warning: could not update $PACKAGE_JSON; the TUI panel may fail to load"
|
|
259
200
|
fi
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
# opencode imports plugins once at startup and has no hot reload, so a running
|
|
204
|
+
# instance keeps the old panel until it is fully quit (reloading a window reuses
|
|
205
|
+
# the same process). Say so explicitly and name the processes still holding it.
|
|
206
|
+
note_restart() {
|
|
207
|
+
note "restart opencode to load the updated panel: quit it completely"
|
|
208
|
+
note " (all windows and background processes), then reopen."
|
|
209
|
+
if command -v pgrep >/dev/null 2>&1; then
|
|
210
|
+
local pids
|
|
211
|
+
pids=$(pgrep -x opencode 2>/dev/null | tr '\n' ' ' | sed 's/ $//' || true)
|
|
212
|
+
[ -n "$pids" ] && note " still running: opencode PID(s) $pids"
|
|
213
|
+
fi
|
|
214
|
+
return 0
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if [ "$UNINSTALL" = 1 ]; then
|
|
218
|
+
if [ -L "$LAUNCHER" ]; then
|
|
219
|
+
if launcher_is_ours "$LAUNCHER"; then
|
|
220
|
+
rm -f "$LAUNCHER" && note "removed $LAUNCHER"
|
|
221
|
+
else
|
|
222
|
+
note "left $LAUNCHER untouched (not a sesh launcher: $(readlink "$LAUNCHER" 2>/dev/null))"
|
|
223
|
+
fi
|
|
224
|
+
fi
|
|
225
|
+
remove_if_marker "$COMMAND_DST" "rich interactive picker" && note "removed $COMMAND_DST"
|
|
226
|
+
remove_if_marker "$TOOL_DST" "list opencode sessions for the agent" && note "removed $TOOL_DST"
|
|
227
|
+
remove_if_marker "$PANEL_DST" "@jsxImportSource @opentui/solid" && note "removed $PANEL_DST"
|
|
228
|
+
remove_tui_entry && note "unregistered the panel from $TUI_JSON"
|
|
229
|
+
note "uninstalled (extraction cache and plugin dependencies left intact)"
|
|
230
|
+
exit 0
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
for path in \
|
|
234
|
+
bin/sesh \
|
|
235
|
+
bin/sesh.sh \
|
|
236
|
+
bin/sesh-list.sh \
|
|
237
|
+
bin/sesh-preview.sh \
|
|
238
|
+
bin/sesh-delete.sh \
|
|
239
|
+
bin/sesh-refresh-worker.sh \
|
|
240
|
+
bin/sesh-shortcuts.sh \
|
|
241
|
+
opencode/tools/sesh-list.ts \
|
|
242
|
+
tui/sesh-panel.tsx \
|
|
243
|
+
themes/glow-dark-clean.json; do
|
|
244
|
+
[ -f "$SOURCE_DIR/$path" ] || fail "installer bundle is incomplete: missing $path."
|
|
245
|
+
done
|
|
246
|
+
|
|
247
|
+
if [ "$SYNC_PANEL" = 1 ]; then
|
|
248
|
+
# Postinstall path: refresh a panel the user already opted into, without
|
|
249
|
+
# touching the launcher or requiring fzf/sqlite. No-op when the panel is
|
|
250
|
+
# absent, so `npm install` never adds a TUI panel on its own.
|
|
251
|
+
[ -f "$PANEL_DST" ] || { note "sesh sidebar panel is not installed; nothing to sync."; exit 0; }
|
|
252
|
+
umask 077
|
|
253
|
+
install_tui_panel
|
|
254
|
+
note_restart
|
|
255
|
+
exit 0
|
|
256
|
+
fi
|
|
257
|
+
|
|
258
|
+
for tool in bash jq fzf; do
|
|
259
|
+
command -v "$tool" >/dev/null 2>&1 || fail "required tool '$tool' is unavailable."
|
|
260
|
+
done
|
|
261
|
+
fzf_version=$(fzf --version 2>/dev/null || true)
|
|
262
|
+
if [[ "$fzf_version" =~ ^([0-9]+)\.([0-9]+)(\.([0-9]+))? ]]; then
|
|
263
|
+
if [ "$((10#${BASH_REMATCH[1]}))" -eq 0 ] && [ "$((10#${BASH_REMATCH[2]}))" -lt 73 ]; then
|
|
264
|
+
fail "fzf >= 0.73.0 is required (found $fzf_version)."
|
|
265
|
+
fi
|
|
266
|
+
else
|
|
267
|
+
fail "cannot parse fzf version from: $fzf_version."
|
|
268
|
+
fi
|
|
269
|
+
command -v sqlite3 >/dev/null 2>&1 || command -v opencode >/dev/null 2>&1 \
|
|
270
|
+
|| fail "either sqlite3 or the opencode CLI is required."
|
|
271
|
+
command -v opencode >/dev/null 2>&1 || note "opencode CLI not found: listing and previews work via sqlite3, but resume and delete need opencode."
|
|
272
|
+
|
|
273
|
+
umask 077
|
|
274
|
+
mkdir -p "$BIN_DIR"
|
|
275
|
+
if [ -e "$LAUNCHER" ] && [ ! -L "$LAUNCHER" ]; then
|
|
276
|
+
fail "$LAUNCHER already exists and is not a symlink. Move it aside, then retry."
|
|
277
|
+
fi
|
|
278
|
+
if [ -L "$LAUNCHER" ] && ! launcher_is_ours "$LAUNCHER"; then
|
|
279
|
+
fail "$LAUNCHER is a symlink to $(readlink "$LAUNCHER" 2>/dev/null), which is not a sesh install. Move it aside, then retry."
|
|
280
|
+
fi
|
|
281
|
+
ln -sfn "$SOURCE_DIR/bin/sesh" "$LAUNCHER"
|
|
282
|
+
note "linked $LAUNCHER -> $SOURCE_DIR/bin/sesh"
|
|
283
|
+
|
|
284
|
+
# Earlier versions shipped a markdown `/sesh` command that could only prompt the
|
|
285
|
+
# agent; the TUI plugin now registers the slash itself. Drop the old file so the
|
|
286
|
+
# two do not collide.
|
|
287
|
+
remove_if_marker "$COMMAND_DST" "rich interactive picker" && note "removed obsolete markdown command $COMMAND_DST"
|
|
288
|
+
[ "$INSTALL_TOOL" = 1 ] && install_file "$SOURCE_DIR/opencode/tools/sesh-list.ts" "$TOOL_DST"
|
|
289
|
+
|
|
290
|
+
if [ "$INSTALL_TUI" = 1 ]; then
|
|
291
|
+
install_tui_panel
|
|
260
292
|
fi
|
|
261
293
|
|
|
262
294
|
"$LAUNCHER" --check || fail "post-install check failed."
|
|
263
295
|
note "done"
|
|
264
296
|
note " picker: run 'sesh' in any terminal, or '/sesh' inside opencode"
|
|
265
|
-
note " sidebar:
|
|
297
|
+
note " sidebar: ctrl+o opens the full picker"
|
|
298
|
+
note_restart
|
|
266
299
|
case ":$PATH:" in
|
|
267
300
|
*":$BIN_DIR:"*) ;;
|
|
268
301
|
*) note " note: $BIN_DIR is not on PATH; add it to use the launcher anywhere." ;;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-sesh",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Browse, search, preview and resume opencode sessions from the terminal and the TUI.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,16 +36,19 @@
|
|
|
36
36
|
"publishConfig": {
|
|
37
37
|
"access": "public"
|
|
38
38
|
},
|
|
39
|
+
"packageManager": "bun@1.4.2",
|
|
39
40
|
"engines": {
|
|
40
41
|
"node": ">=20"
|
|
41
42
|
},
|
|
42
43
|
"scripts": {
|
|
43
44
|
"prepare": "husky",
|
|
44
|
-
"
|
|
45
|
+
"postinstall": "bash install.sh --sync-panel || true",
|
|
46
|
+
"test": "bash tests/sesh.sh && bash tests/changelog.sh",
|
|
47
|
+
"test:changelog": "bash tests/changelog.sh",
|
|
45
48
|
"test:picker": "python3 tests/picker.py",
|
|
46
49
|
"typecheck": "tsc -p tsconfig.json",
|
|
47
|
-
"lint:sh": "for script in bin/*.sh bin/sesh install.sh tests/*.sh; do bash -n \"$script\" || exit; done",
|
|
48
|
-
"prepublishOnly": "
|
|
50
|
+
"lint:sh": "for script in bin/*.sh bin/sesh install.sh tests/*.sh scripts/*.sh; do bash -n \"$script\" || exit; done",
|
|
51
|
+
"prepublishOnly": "bun run test && bun run typecheck"
|
|
49
52
|
},
|
|
50
53
|
"devDependencies": {
|
|
51
54
|
"@commitlint/cli": "^21.2.2",
|