Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ Pass structured data on every log call where useful for filtering, searching, or
| `tmux_pane` | `str` | pane identifier |
| `tmux_config_path` | `str` | workspace config file path |
| `tmux_layout` | `str` | window layout string |
| `tmux_hook_cmd` | `str` | lifecycle hook shell command line |

**Heavy/optional keys** (DEBUG only, potentially large):

Expand Down
2 changes: 2 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ def socket_name(request: pytest.FixtureRequest) -> str:

# Modules that actually need tmux fixtures in their doctests
DOCTEST_NEEDS_TMUX = {
"tmuxp.cli.stop",
"tmuxp.util",
"tmuxp.workspace.builder",
}

Expand Down
7 changes: 7 additions & 0 deletions docs/cli/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ Interactive Python shell with tmux context.
Export running sessions to config files.
:::

:::{grid-item-card} tmuxp stop
:link: stop
:link-type: doc
Stop running sessions with cleanup hooks.
:::

:::{grid-item-card} tmuxp convert
:link: convert
:link-type: doc
Expand Down Expand Up @@ -53,6 +59,7 @@ load
shell
ls
search
stop
```

```{toctree}
Expand Down
38 changes: 38 additions & 0 deletions docs/cli/stop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
(cli-stop)=

(cli-stop-reference)=

# tmuxp stop

Stop (kill) a running tmux session. If the session was created from a workspace
with `on_project_stop`, that hook runs before the session is killed.

## Command

```{eval-rst}
.. argparse::
:module: tmuxp.cli
:func: create_parser
:prog: tmuxp
:path: stop
```

## Basic Usage

Stop a session by name:

```console
$ tmuxp stop mysession
```

Stop the currently attached session:

```console
$ tmuxp stop
```

Use a custom socket:

```console
$ tmuxp stop -L mysocket mysession
```
13 changes: 13 additions & 0 deletions docs/configuration/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,19 @@ The `synchronize` window key provides a shorthand for enabling
```
````

## Lifecycle Hooks

Run shell commands at different stages of the session lifecycle:

````{tab} YAML
```{literalinclude} ../../examples/lifecycle-hooks.yaml
:language: yaml

```
````

See {ref}`top-level` for full hook documentation.

## Pane Titles

Pane title keys turn on tmux pane border titles and label individual panes:
Expand Down
36 changes: 36 additions & 0 deletions docs/configuration/top-level.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,42 @@ Notes:

Above: Use `tmux` directly to attach _banana_.

## Lifecycle Hooks

Workspace configs support four lifecycle hooks:

```yaml
session_name: myproject
on_project_start: notify-send "Starting myproject"
on_project_restart: notify-send "Reattaching to myproject"
on_project_exit: notify-send "Detached from myproject"
on_project_stop: notify-send "Stopping myproject"
windows:
- window_name: main
panes:
-
```

| Hook | When it runs |
|------|-------------|
| `on_project_start` | Before a new session is built. |
| `on_project_restart` | Before reattaching to an existing session. |
| `on_project_exit` | When the last client detaches. |
| `on_project_stop` | Before `tmuxp stop` kills the session. |

Each hook accepts a string command or a list of command strings:

```yaml
on_project_start:
- notify-send "Starting"
- ./setup.sh
```

`on_project_start`, `on_project_restart`, and `on_project_stop` run through the
shell and block tmuxp until they finish. `on_project_exit` is different: it runs
via tmux's `client-detached` hook after tmuxp has already returned, so it never
blocks the command. Hook failures are logged and do not stop the tmuxp command.

## Pane Titles

Enable pane border titles to display labels on each pane:
Expand Down
1 change: 1 addition & 0 deletions docs/internals/api/cli/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ ls
progress
search
shell
stop
utils
```

Expand Down
8 changes: 8 additions & 0 deletions docs/internals/api/cli/stop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# tmuxp stop - `tmuxp.cli.stop`

```{eval-rst}
.. automodule:: tmuxp.cli.stop
:members:
:show-inheritance:
:undoc-members:
```
9 changes: 9 additions & 0 deletions examples/lifecycle-hooks.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
session_name: lifecycle hooks
on_project_start: echo "project starting"
on_project_restart: echo "project restarting"
on_project_exit: echo "project exiting"
on_project_stop: echo "project stopping"
windows:
- window_name: main
panes:
-
27 changes: 27 additions & 0 deletions src/tmuxp/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@
command_shell,
create_shell_subparser,
)
from .stop import (
STOP_DESCRIPTION,
CLIStopNamespace,
command_stop,
create_stop_subparser,
)
from .utils import tmuxp_echo

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -130,6 +136,13 @@
"tmuxp edit myproject",
],
),
(
"stop",
[
"tmuxp stop mysession",
"tmuxp stop -L mysocket mysession",
],
),
(
"debug-info",
[
Expand All @@ -155,6 +168,7 @@
"import",
"search",
"shell",
"stop",
"debug-info",
]
CLIImportSubparserName: TypeAlias = t.Literal["teamocil", "tmuxinator"]
Expand Down Expand Up @@ -262,6 +276,14 @@ def create_parser() -> argparse.ArgumentParser:
)
create_freeze_subparser(freeze_parser)

stop_parser = subparsers.add_parser(
"stop",
help="stop (kill) a tmux session",
description=STOP_DESCRIPTION,
formatter_class=formatter_class,
)
create_stop_subparser(stop_parser)

return parser


Expand Down Expand Up @@ -353,6 +375,11 @@ def cli(_args: list[str] | None = None) -> None:
args=CLIFreezeNamespace(**vars(args)),
parser=parser,
)
elif args.subparser_name == "stop":
command_stop(
args=CLIStopNamespace(**vars(args)),
parser=parser,
)
elif args.subparser_name == "ls":
command_ls(
args=CLILsNamespace(**vars(args)),
Expand Down
Loading
Loading