From 34c0095f2f64965eef3334970199e4cfd4936973 Mon Sep 17 00:00:00 2001 From: Ry J Date: Tue, 21 Jul 2026 09:03:54 +1000 Subject: [PATCH 1/4] docs: add contribution guide, issue/PR templates Co-Authored-By: Claude Fable 5 --- .github/ISSUE_TEMPLATE/bug_report.md | 27 ++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 19 +++++++ .github/PULL_REQUEST_TEMPLATE.md | 13 +++++ CONTRIBUTING.md | 63 +++++++++++++++++++++++ README.md | 6 ++- 5 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 CONTRIBUTING.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..4c33324 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,27 @@ +--- +name: Bug report +about: Something isn't working +title: '' +labels: bug +assignees: '' +--- + +**Describe the bug** +A clear description of what went wrong. + +**To reproduce** +Steps to reproduce the behavior: +1. ... +2. ... + +**Expected behavior** +What you expected to happen instead. + +**Environment** +- Session Manager version: (Menu → About, or `brew info`) +- Install method: Homebrew / release DMG / built from source +- macOS version: +- Claude Code version: (`claude --version`) + +**Screenshots / logs** +If applicable, add screenshots or relevant log output. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..8dcdd8c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,19 @@ +--- +name: Feature request +about: Suggest an idea or improvement +title: '' +labels: enhancement +assignees: '' +--- + +**What problem does this solve?** +Describe the problem or friction you're hitting. + +**Proposed solution** +What you'd like to happen. Mockups or examples welcome. + +**Alternatives considered** +Any workarounds you're using today, or other approaches that could work. + +**Willing to implement it?** +Let us know if you'd like to submit a PR for this yourself — see [CONTRIBUTING.md](../../CONTRIBUTING.md). diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..4c94d68 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,13 @@ +## What does this PR do? + + + +## How was it tested? + + + +## Checklist + +- [ ] `npm test` passes +- [ ] `npm run build` passes +- [ ] Updated `docs/wiki/` if this changes how a feature behaves diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..13c1430 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,63 @@ +# Contributing to Session Manager + +Thanks for your interest in contributing! Session Manager is a small project, so the process is deliberately lightweight. + +## How contributions work + +- **Found a bug?** [Open a bug report](https://github.com/ryj-dev/session-manager/issues/new?template=bug_report.md). Include your macOS version, app version, and steps to reproduce. +- **Want a feature?** [Open a feature request](https://github.com/ryj-dev/session-manager/issues/new?template=feature_request.md) first, before writing code. This avoids you spending time on something that might not fit the project's direction. +- **Have a fix or feature ready?** Fork the repo, create a branch, and open a pull request against `main`. + +For anything beyond a small fix (typos, obvious one-liners), please open an issue first so we can discuss the approach. + +## Development setup + +Requirements: + +- macOS (the app currently targets macOS; arm64 is the primary platform) +- Node.js 20+ +- [Claude Code](https://claude.com/claude-code) installed, since the app manages Claude Code sessions + +```sh +git clone https://github.com//session-manager.git +cd session-manager +npm install +npm run dev # launches the app with hot reload via electron-vite +``` + +Other useful commands: + +```sh +npm test # run unit tests (node --test over src/main/*.test.ts) +npm run build # type-check and build all processes +npm run dist:mac # produce a distributable .dmg in release/ +``` + +## Project layout + +``` +src/main/ Electron main process — PTY/session management, hook server, + MCP server, pipeline orchestration, persistence +src/preload/ Context-bridge API between main and renderer +src/renderer/ React UI — graph view, terminals, canvas, settings +docs/wiki/ Bundled feature wiki (shipped read-only in the app) +resources/ Static assets bundled into the app +scripts/ Build and release helpers +``` + +## Pull request guidelines + +- **Keep PRs focused.** One fix or feature per PR is much easier to review than a grab-bag. +- **Match the existing style.** The codebase is TypeScript throughout; follow the conventions of the file you're editing rather than introducing new patterns. +- **Add or update tests** when you change logic in `src/main/` that has existing test coverage (`*.test.ts` files). UI changes don't currently have automated tests — describe how you verified them manually in the PR. +- **Run before pushing:** `npm test` and `npm run build` should both pass. +- **Describe the "why".** In the PR description, explain what problem this solves and how you tested it. Screenshots or short recordings are very welcome for UI changes. +- **Update the wiki if behavior changes.** If your change alters how a feature works, update the matching article in `docs/wiki/` — it ships inside the app as the authoritative feature docs. + +## Reviews and releases + +PRs are reviewed as time permits — this is a spare-time project, so please be patient. Merged changes ship in the next tagged release (versioned `v0.x.y`, distributed via GitHub Releases and Homebrew). + +## License + +By contributing, you agree that your contributions will be licensed under the [MIT License](LICENSE) that covers the project. diff --git a/README.md b/README.md index d35222a..cf26fd6 100644 --- a/README.md +++ b/README.md @@ -90,9 +90,11 @@ All keyboard shortcuts are configurable. Open **Settings → Keyboard shortcuts* Other settings worth knowing about live in the same panel: base projects directory, terminal pairing mode (off / split / overlay shell alongside Claude), auto-mode defaults for spawned sessions, message popup behavior, and per-integration disable toggles. -## Reporting issues +## Reporting issues & contributing -Open an issue at . +Open an issue at — there are templates for bug reports and feature requests. + +Want to contribute a fix or feature? See [CONTRIBUTING.md](CONTRIBUTING.md) for the workflow (fork → branch → PR) and development setup. ## Credits From 81b35a7edb744be0b91ba6e31d27cefd399fdbdb Mon Sep 17 00:00:00 2001 From: Ry J Date: Tue, 21 Jul 2026 09:25:52 +1000 Subject: [PATCH 2/4] fix: Cmd+Q performs a real quit instead of leaving the app lingering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The renderer's capture-phase Cmd+Q handler ran on macOS too (despite its comment), where preventDefault suppressed the menu's quit accelerator and window.close() merely closed the window — macOS keeps apps alive on window-all-closed, so the app lingered in the dock with the hook server, MCP registration, and scheduler still active. Route the hotkey through a new app:quit IPC that calls app.quit(), so the full before-quit saveAndCleanup (session save, PTY teardown, skill-command cleanup, scheduler/hook/watcher/embed shutdown, MCP unregister, plugin uninstall) runs on every platform. Co-Authored-By: Claude Fable 5 --- src/main/ipc.ts | 6 ++++++ src/preload/index.ts | 4 ++++ src/renderer/App.tsx | 10 +++++++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 763c12a..6be27b2 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -86,6 +86,12 @@ function attachSessionListeners( } export function registerIpcHandlers(opts: { reinstallMcp: () => void }): void { + // Real quit (renderer Cmd+Q fallback). app.quit() fires before-quit, which + // runs the full saveAndCleanup teardown — never bypass it with window.close(). + ipcMain.on('app:quit', () => { + app.quit() + }) + // Register the attach-listeners callback for hook-server spawned sessions setAttachListeners((id, session) => attachSessionListeners(id, session)) diff --git a/src/preload/index.ts b/src/preload/index.ts index 5f36cf8..4ba36bf 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -355,6 +355,10 @@ const api = { cleanupRemoveNotes: (): Promise => ipcRenderer.invoke('cleanup:removeNotes'), cleanupRemoveSessions: (): Promise => ipcRenderer.invoke('cleanup:removeSessions'), cleanupResetAppSettings: (): Promise => ipcRenderer.invoke('cleanup:resetAppSettings'), + + // App lifecycle — real quit via app.quit() so the full before-quit cleanup + // (session save, PTY teardown, MCP unregister, plugin uninstall) always runs + quitApp: (): void => ipcRenderer.send('app:quit'), } export type CleanupResult = { ok: boolean; error?: string } diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 2122a00..5e59707 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -970,11 +970,15 @@ export function App(): JSX.Element { return } - // Quit app — on Mac this is handled natively by Cmd+Q via the menu; - // on Windows/Linux there's no menu so we handle it here + // Quit app. This capture-phase handler fires before the macOS menu's + // quit accelerator, so it must perform a REAL quit: window.close() here + // would only close the window and leave the app lingering in the dock + // (macOS keeps apps alive on window-all-closed). app:quit → app.quit() + // runs the full before-quit cleanup (session save, PTY teardown, + // MCP unregister, plugin uninstall) on every platform. if (key === 'q') { e.preventDefault() - window.close() + window.api.quitApp() return } } From b5863fb2670a80152fc6eba7223f021475b41abe Mon Sep 17 00:00:00 2001 From: Ry J Date: Tue, 21 Jul 2026 11:00:19 +1000 Subject: [PATCH 3/4] fix: titlebar drag regions and file explorer navigation - Carve out overlay controls (New schedule, drawer close buttons, project picker) with titlebar-no-drag so the window-level drag strip beneath the view-mode titlebars no longer swallows their clicks - Offset the file explorer header past the macOS traffic lights - Stop file explorer up-navigation from stranding the user above an allowed root by checking the parent is an accessible directory first Co-Authored-By: Claude Opus 4.8 (1M context) --- src/renderer/components/FileExplorer.tsx | 16 ++++++++++++---- .../components/pipeline/PipelineView.tsx | 9 ++++++--- .../components/scheduled/ScheduledTasksView.tsx | 10 +++++++--- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/renderer/components/FileExplorer.tsx b/src/renderer/components/FileExplorer.tsx index 48e2bbc..59ceee1 100644 --- a/src/renderer/components/FileExplorer.tsx +++ b/src/renderer/components/FileExplorer.tsx @@ -129,10 +129,14 @@ export function FileExplorer({ case 'ArrowLeft': case 'Backspace': e.preventDefault() - // Go up one directory + // Go up one directory, but only if the parent is actually accessible — + // navigating above an allowed root (e.g. /Users) returns an empty + // listing with no entries to move forward into, stranding the user. const parent = currentPath.split('/').slice(0, -1).join('/') if (parent) { - setCurrentPath(parent) + window.api.isDirectory(parent).then((ok) => { + if (ok) setCurrentPath(parent) + }) } break case 'Escape': @@ -156,8 +160,12 @@ export function FileExplorer({ transition={{ duration: 0.2 }} className="absolute inset-y-0 left-0 w-80 z-30 bg-zinc-900/95 backdrop-blur-xl border-r border-zinc-800 flex flex-col" > - {/* Header */} -
+ {/* Header — offset past the macOS traffic lights so the path isn't hidden behind them */} +
{currentPath} diff --git a/src/renderer/components/pipeline/PipelineView.tsx b/src/renderer/components/pipeline/PipelineView.tsx index da7e555..df25952 100644 --- a/src/renderer/components/pipeline/PipelineView.tsx +++ b/src/renderer/components/pipeline/PipelineView.tsx @@ -748,7 +748,7 @@ function SessionDrawer({ {stageMeta.label}

{card.title}

- +
@@ -907,7 +907,7 @@ function BacklogDetailDrawer({ Backlog

{todo.title}

- + {/* Meta: status + tags */} @@ -1299,7 +1299,10 @@ function ProjectPicker({ const select = (name: string | null): void => { onChange(name); setOpen(false) } return ( -
+ // titlebar-no-drag: the view-mode titlebars underneath keep their + // app-region: drag strip active through this overlay (drag regions are + // window-level and ignore z-order), and this trigger sits inside it. +
@@ -430,7 +434,7 @@ function ScheduleFormDrawer({ >

{task ? 'Edit schedule' : 'New schedule'}

- +
@@ -619,7 +623,7 @@ function RunTerminalDrawer({ task, run, onClose }: { task: ScheduledTask; run: S Run · {fmtTime(run.startedAt)}

{task.name}

- +
From 0fb0ecf0d9b90be527ab5e6b57b7a821372c5747 Mon Sep 17 00:00:00 2001 From: Ry J Date: Tue, 21 Jul 2026 11:00:19 +1000 Subject: [PATCH 4/4] =?UTF-8?q?release:=20v0.2.2=20=E2=80=94=20titlebar=20?= =?UTF-8?q?drag=20&=20file=20explorer=20nav=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 590920f..567850e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "session-manager", - "version": "0.1.2", + "version": "0.2.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "session-manager", - "version": "0.1.2", + "version": "0.2.2", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 2f83789..672b5b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "session-manager", - "version": "0.2.1", + "version": "0.2.2", "description": "Desktop app for managing multiple Claude Code sessions", "author": "Ry J", "license": "MIT",