thinkpool-pair 0.4.0 → 0.5.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.
Files changed (2) hide show
  1. package/bridge.mjs +50 -4
  2. package/package.json +1 -1
package/bridge.mjs CHANGED
@@ -7,6 +7,14 @@
7
7
  npx thinkpool-pair <ROOM> -- <cmd…> # share a specific CLI (skip the picker)
8
8
  npx thinkpool-pair <ROOM> --headless # pure relay — terminals open from the web
9
9
 
10
+ v0.5 — continue, don't restart: when the picked agent supports
11
+ resuming (Claude Code: `--continue` = most recent conversation in
12
+ THIS directory) and a prior session exists here, the bridge offers to
13
+ continue it instead of starting fresh — quit your running agent, run
14
+ the bridge in the same directory, and you're back in the same
15
+ conversation, now shared. `--continue` / `--fresh` skip the question.
16
+ (True attach to a live process was ruled out: reptyr-style TTY
17
+ hijacking is Linux-only and SIP-blocked on macOS.)
10
18
  v0.4 — room file drops: files dropped/pasted in the web room download
11
19
  to a temp dir here (file-put → file-done) so the agent can read them.
12
20
  v0.3 — multi-terminal. The bridge is your machine's presence in the
@@ -49,8 +57,12 @@ catch {
49
57
  // Order = display order; Claude Code stays first (the original default). The
50
58
  // picker only offers the ones actually installed (PATH probe below). Power
51
59
  // users can bypass it entirely with `-- <any command>`.
60
+ // `resume` marks agents where continuing the latest session is a SAFE,
61
+ // verified flag (wrong flags kill the spawn → bridge exits — only add
62
+ // agents after checking the real CLI). probe() must be cheap + read-only;
63
+ // a miss just means "continue" isn't offered.
52
64
  const KNOWN_AGENTS = [
53
- { label: 'Claude Code', cmd: 'claude' },
65
+ { label: 'Claude Code', cmd: 'claude', resume: { args: ['--continue'], probe: claudeHasSession } },
54
66
  { label: 'Codex CLI', cmd: 'codex' },
55
67
  { label: 'Gemini CLI', cmd: 'gemini' },
56
68
  { label: 'Aider', cmd: 'aider' },
@@ -62,6 +74,16 @@ const KNOWN_AGENTS = [
62
74
  { label: 'Qwen Code', cmd: 'qwen' },
63
75
  ]
64
76
 
77
+ // Does Claude Code have a resumable conversation for THIS directory?
78
+ // Sessions live under ~/.claude/projects/<cwd with [/\.:] → '-'> (one
79
+ // subdir per session). Read-only probe; any miss = no "continue" offer.
80
+ function claudeHasSession() {
81
+ try {
82
+ const slug = path.resolve(process.cwd()).replace(/[/\\.:]/g, '-')
83
+ return fs.readdirSync(path.join(os.homedir(), '.claude', 'projects', slug)).length > 0
84
+ } catch { return false }
85
+ }
86
+
65
87
  // Is `c` on PATH? Pure-Node scan (no `which` subprocess), cross-platform.
66
88
  const onPath = (c) => {
67
89
  const exts = process.platform === 'win32'
@@ -94,11 +116,23 @@ const pickAgent = (installed) => new Promise((resolve) => {
94
116
 
95
117
  const argv = process.argv.slice(2)
96
118
  const room = (argv[0] || '').toUpperCase().trim()
97
- if (!room || room.startsWith('-')) { console.error('usage: npx thinkpool-pair <ROOM> [--headless] [-- <command…>]'); process.exit(1) }
119
+ if (!room || room.startsWith('-')) { console.error('usage: npx thinkpool-pair <ROOM> [--headless] [--continue|--fresh] [-- <command…>]'); process.exit(1) }
98
120
  const headless = argv.includes('--headless')
99
121
  const installedAgents = KNOWN_AGENTS.filter(a => onPath(a.cmd))
100
122
  const dashIdx = argv.indexOf('--')
101
- let attachedCmd = null, attachedArgs = []
123
+ // Own flags are only read from BEFORE `--`; after it, every token belongs
124
+ // to the shared command (`-- claude --continue` must not trip these).
125
+ const ownArgs = dashIdx >= 0 ? argv.slice(0, dashIdx) : argv
126
+ const forceContinue = ownArgs.includes('--continue')
127
+ const forceFresh = ownArgs.includes('--fresh')
128
+
129
+ // One yes/no on the bridge's stderr (same channel as the agent picker).
130
+ const askYesNo = (q) => new Promise((resolve) => {
131
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr })
132
+ rl.question(q, (ans) => { rl.close(); resolve(!/^n/i.test(String(ans).trim())) })
133
+ })
134
+
135
+ let attachedCmd = null, attachedArgs = [], continuing = false
102
136
  if (dashIdx >= 0) {
103
137
  ;[attachedCmd, ...attachedArgs] = argv.slice(dashIdx + 1)
104
138
  } else if (!headless) {
@@ -107,6 +141,18 @@ if (dashIdx >= 0) {
107
141
  process.exit(1)
108
142
  }
109
143
  attachedCmd = await pickAgent(installedAgents)
144
+ // Continue the latest session instead of starting fresh — offered only
145
+ // when the agent supports it AND a prior session exists in this cwd.
146
+ // --continue forces it (your call even if the probe misses); --fresh
147
+ // skips the question; non-TTY stays fresh (no surprise in scripts).
148
+ const agent = KNOWN_AGENTS.find(a => a.cmd === attachedCmd)
149
+ if (agent?.resume && !forceFresh) {
150
+ if (forceContinue) continuing = true
151
+ else if (process.stdin.isTTY && agent.resume.probe()) {
152
+ continuing = await askYesNo(`\n Continue your latest ${agent.label} session in this directory? [Y/n] `)
153
+ }
154
+ if (continuing) attachedArgs = [...agent.resume.args]
155
+ }
110
156
  }
111
157
  const name = process.env.TP_NAME || os.userInfo().username || 'host'
112
158
 
@@ -326,7 +372,7 @@ channel
326
372
  announce()
327
373
  process.stderr.write(headless
328
374
  ? `\n ◆ thinkpool — relaying room ${room} (headless). Open terminals from the web UI.\n\n`
329
- : `\n ◆ thinkpool — sharing "${attachedCmd}" into room ${room}. Open the web UI and you're both in.\n\n`)
375
+ : `\n ◆ thinkpool — sharing "${attachedCmd}"${continuing ? ' (continuing your latest session)' : ''} into room ${room}. Open the web UI and you're both in.\n\n`)
330
376
  }
331
377
  })
332
378
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Share a local coding-agent CLI (Claude Code, Codex, Gemini, Aider, …) into a ThinkPool Code room, live.",
5
5
  "type": "module",
6
6
  "bin": { "thinkpool-pair": "bridge.mjs" },