vibe-pomo 0.1.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.
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Per-session Timer TUI.
4
+ * Launched in a new terminal window for each `pomodoro start`.
5
+ *
6
+ * Usage: tsx src/tui/timer/index.tsx <sessionId>
7
+ */
8
+ import React from 'react'
9
+ import { render } from 'ink'
10
+ import { TimerApp } from './TimerApp.js'
11
+ import { sendAndReceive } from '../../shared/ipcClient.mjs'
12
+ import { MSG } from '../../shared/protocol.mjs'
13
+
14
+ const sessionId = process.argv[2]
15
+ if (!sessionId) {
16
+ console.error('Usage: pomodoro-timer <sessionId>')
17
+ process.exit(1)
18
+ }
19
+
20
+ async function main() {
21
+ // Fetch initial session state
22
+ let session: any = {}
23
+ try {
24
+ session = await sendAndReceive({ type: MSG.SESSION_QUERY, sessionId })
25
+ } catch {
26
+ console.error('Could not connect to Pomodoro daemon.')
27
+ process.exit(1)
28
+ }
29
+
30
+ if (session.error) {
31
+ console.error('Session not found:', sessionId)
32
+ process.exit(1)
33
+ }
34
+
35
+ render(
36
+ <TimerApp
37
+ sessionId={sessionId}
38
+ task={session.task ?? ''}
39
+ plannedMs={session.plannedMs ?? 25 * 60 * 1000}
40
+ initialState={session.state}
41
+ initialRemaining={session.remaining}
42
+ initialOvertime={session.overtime}
43
+ />,
44
+ { exitOnCtrlC: false }
45
+ )
46
+ }
47
+
48
+ main().catch((err) => {
49
+ console.error('Timer error:', err)
50
+ process.exit(1)
51
+ })
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Session lifecycle watcher.
3
+ *
4
+ * Monitors a target PID (the Claude Code process). When that process exits,
5
+ * automatically breaks the associated Pomodoro session.
6
+ *
7
+ * Spawned detached by `pomodoro start` when called from inside Claude Code.
8
+ *
9
+ * Usage: node src/watcher.mjs <watchPid> <sessionId>
10
+ */
11
+ import { sendAndReceive } from './shared/ipcClient.mjs'
12
+ import { readLock } from './shared/lockfile.mjs'
13
+ import { MSG } from './shared/protocol.mjs'
14
+
15
+ const [,, watchPidStr, sessionId] = process.argv
16
+ const watchPid = parseInt(watchPidStr, 10)
17
+
18
+ if (!watchPid || !sessionId) {
19
+ process.exit(1)
20
+ }
21
+
22
+ function isAlive(pid) {
23
+ try { process.kill(pid, 0); return true }
24
+ catch { return false }
25
+ }
26
+
27
+ async function breakSession() {
28
+ const lock = readLock()
29
+ if (!lock) return
30
+ try {
31
+ await sendAndReceive({ type: MSG.SESSION_BREAK, sessionId })
32
+ } catch {}
33
+ }
34
+
35
+ // Poll every 3 seconds
36
+ const interval = setInterval(async () => {
37
+ if (!isAlive(watchPid)) {
38
+ clearInterval(interval)
39
+ await breakSession()
40
+ process.exit(0)
41
+ }
42
+ }, 3000)
43
+
44
+ // Don't keep event loop alive for other reasons
45
+ interval.unref()
46
+
47
+ // Also exit cleanly if daemon disappears
48
+ setTimeout(function check() {
49
+ if (!readLock()) { clearInterval(interval); process.exit(0) }
50
+ setTimeout(check, 10000).unref()
51
+ }, 10000).unref()