4top 0.2.0a1__tar.gz

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 (57) hide show
  1. 4top-0.2.0a1/CHANGELOG.md +165 -0
  2. 4top-0.2.0a1/CONTRIBUTING.md +96 -0
  3. 4top-0.2.0a1/LICENSE +21 -0
  4. 4top-0.2.0a1/MANIFEST.in +5 -0
  5. 4top-0.2.0a1/PKG-INFO +233 -0
  6. 4top-0.2.0a1/README.md +206 -0
  7. 4top-0.2.0a1/README.zh-CN.md +134 -0
  8. 4top-0.2.0a1/SECURITY.md +15 -0
  9. 4top-0.2.0a1/docs/compatibility.md +83 -0
  10. 4top-0.2.0a1/docs/demo/demo.svg +158 -0
  11. 4top-0.2.0a1/docs/design.md +88 -0
  12. 4top-0.2.0a1/docs/privacy.md +46 -0
  13. 4top-0.2.0a1/docs/remote-design.md +126 -0
  14. 4top-0.2.0a1/docs/troubleshooting.md +46 -0
  15. 4top-0.2.0a1/docs/validation/README.md +104 -0
  16. 4top-0.2.0a1/docs/validation/install-summary.json +57 -0
  17. 4top-0.2.0a1/docs/validation/linux-remote-2026-09-26.md +58 -0
  18. 4top-0.2.0a1/docs/validation/macos-0.1.0a2.md +56 -0
  19. 4top-0.2.0a1/docs/validation/macos-fresh-install-0.1.0a2.json +57 -0
  20. 4top-0.2.0a1/docs/validation/macos-suite-dev.json +18 -0
  21. 4top-0.2.0a1/pyproject.toml +55 -0
  22. 4top-0.2.0a1/requirements-dev.lock +22 -0
  23. 4top-0.2.0a1/scripts/acceptance.py +48 -0
  24. 4top-0.2.0a1/scripts/check_docs.py +173 -0
  25. 4top-0.2.0a1/scripts/check_release.py +154 -0
  26. 4top-0.2.0a1/scripts/export_demo.py +30 -0
  27. 4top-0.2.0a1/scripts/remote_update.py +136 -0
  28. 4top-0.2.0a1/scripts/verify_install.py +61 -0
  29. 4top-0.2.0a1/setup.cfg +4 -0
  30. 4top-0.2.0a1/src/4top.egg-info/PKG-INFO +233 -0
  31. 4top-0.2.0a1/src/4top.egg-info/SOURCES.txt +55 -0
  32. 4top-0.2.0a1/src/4top.egg-info/dependency_links.txt +1 -0
  33. 4top-0.2.0a1/src/4top.egg-info/entry_points.txt +2 -0
  34. 4top-0.2.0a1/src/4top.egg-info/requires.txt +9 -0
  35. 4top-0.2.0a1/src/4top.egg-info/top_level.txt +1 -0
  36. 4top-0.2.0a1/src/fourtop/__init__.py +3 -0
  37. 4top-0.2.0a1/src/fourtop/__main__.py +3 -0
  38. 4top-0.2.0a1/src/fourtop/agents.py +150 -0
  39. 4top-0.2.0a1/src/fourtop/app.py +824 -0
  40. 4top-0.2.0a1/src/fourtop/cli.py +251 -0
  41. 4top-0.2.0a1/src/fourtop/config.py +167 -0
  42. 4top-0.2.0a1/src/fourtop/doctor.py +90 -0
  43. 4top-0.2.0a1/src/fourtop/errors.py +27 -0
  44. 4top-0.2.0a1/src/fourtop/hosts.py +167 -0
  45. 4top-0.2.0a1/src/fourtop/models.py +76 -0
  46. 4top-0.2.0a1/src/fourtop/services.py +269 -0
  47. 4top-0.2.0a1/src/fourtop/state.py +53 -0
  48. 4top-0.2.0a1/tests/conftest.py +83 -0
  49. 4top-0.2.0a1/tests/fixtures/fake_agent.py +96 -0
  50. 4top-0.2.0a1/tests/integration/test_launch.py +122 -0
  51. 4top-0.2.0a1/tests/tui/test_app.py +377 -0
  52. 4top-0.2.0a1/tests/unit/test_cli_config.py +242 -0
  53. 4top-0.2.0a1/tests/unit/test_docs.py +73 -0
  54. 4top-0.2.0a1/tests/unit/test_history.py +337 -0
  55. 4top-0.2.0a1/tests/unit/test_hosts.py +299 -0
  56. 4top-0.2.0a1/tests/unit/test_native_roots.py +44 -0
  57. 4top-0.2.0a1/tests/unit/test_release.py +49 -0
@@ -0,0 +1,165 @@
1
+ # Changelog
2
+
3
+ ## 4top 0.2.0a1 — 2026-09-26
4
+
5
+ The first release of the session-first line: 4top tracks transcripts rather than
6
+ processes, and reaches other machines over ssh. The previous line owned a process
7
+ per launch and reported liveness; that layer and its vocabulary are gone.
8
+
9
+ ### Breaking: 4top tracks sessions, not processes
10
+
11
+ A native agent is recoverable from its transcript alone, so the transcript is the
12
+ durable object and the process is the ephemeral one. 4top now keeps only
13
+ transcripts: it does not own, supervise or report on a process, and it drives no
14
+ terminal multiplexer of its own.
15
+
16
+ - Deleted: the process-ownership layer and everything that existed to support it
17
+ — launch records, reservations, locks, the operation log, ownership markers,
18
+ exit-code and zombie evidence, the liveness states, and the commands, options
19
+ and `[runtime]` configuration section that reached into it.
20
+ - `new` runs the agent in the calling terminal: the CLI replaces itself with the
21
+ agent (`execvpe`), and the TUI suspends, waits and returns to the panel. Keeping
22
+ work alive across a disconnect belongs to whatever the user already runs.
23
+ - `list --json` rows are `schema_version` 2 and carry `key`, `agent`, `host`,
24
+ `cwd`, `title`, `started`, `last`, `source`, `status`, `problems` and
25
+ `can_resume`. Consumers of the previous row schema must be updated.
26
+ - Local history is shown by default. The old view-schema `history` flag is
27
+ ignored and the view file is written as schema 3.
28
+ - Nothing needs migrating: state is now a local identity plus the last selection.
29
+ Earlier files under `$XDG_STATE_HOME/4top` are ignored and can be deleted. A new
30
+ local identity changes history keys, so anything that copied a key must copy it
31
+ again.
32
+
33
+ ### Remote hosts over SSH
34
+
35
+ - Add `[hosts.NAME]` with `ssh`, `command`, `refresh_seconds` and
36
+ `timeout_seconds`, plus the global `--host` option. Views are isolated:
37
+ `--host` replaces the local scope instead of merging machines into one table.
38
+ - The remote side is the same CLI, with no daemon and no new port. It runs with
39
+ `BatchMode=yes` (a missing key fails fast), a `ControlMaster` socket inside
40
+ private state, and a hard timeout; every remote argument is quoted for the
41
+ remote shell.
42
+ - A row whose `schema_version` differs is refused instead of partially parsed. A
43
+ remote row is relabelled with the configured host name, and its history key
44
+ stays opaque so it is never recomputed on the wrong machine.
45
+ - `resume` and `new` with `--host` hand the terminal to `ssh -t`, so the process
46
+ is created on the machine that owns the history.
47
+ - `H` switches the running panel between this machine and a configured host, so
48
+ comparing two machines no longer means quitting and relaunching. Rows, selection
49
+ and search state are dropped with the old scope, an in-flight refresh for the old
50
+ scope is discarded, and the saved selection stays local.
51
+
52
+ ### Releases
53
+
54
+ - Publish one project per repository. The first attempt to publish both from this
55
+ workflow failed with `403 Invalid API Token: OIDC scoped token is not valid for
56
+ project '4top'`: one job means one OIDC exchange, and the token is scoped to the
57
+ project its publisher matched. Rather than disambiguate two publishers with two
58
+ environments, this workflow now publishes only `4top` and consumes `session-ls`
59
+ from PyPI, which is the layout the two packages actually have: separate projects,
60
+ separate version lines, separate pipelines.
61
+ - Publish from CI on a `v<version>` tag: the suite runs, `scripts/check_release.py`
62
+ gates the release, both wheels are built and installed into a fresh environment,
63
+ `session-ls` is published before `4top`, and the GitHub release is opened. Uploads
64
+ use PyPI trusted publishing, so no token is stored in the repository, and the
65
+ manual trigger defaults to a dry run.
66
+ - The gate refuses a tag that does not match the version, artifacts that are not the
67
+ versions being released, and a root requirement the library cannot satisfy. That
68
+ last one is real: the root requires `session-ls>=0.2.0` while PyPI only ever held
69
+ 0.1.0, so publishing the root alone would have produced an uninstallable package.
70
+
71
+ ### Interface
72
+
73
+ - The host picker (`H`) switches the panel between this machine and a configured
74
+ host, so comparing two machines no longer means relaunching.
75
+ - The selection line is context and one action: `agent · directory` and what Enter
76
+ does. The history key moved to the details screen, where it is needed, and the
77
+ status line says nothing when there is nothing to report.
78
+ - A recorded directory that no longer exists now names itself, is marked in the
79
+ details screen, and asks for a directory to resume in instead of failing with
80
+ "not accessible".
81
+ - The mouse wheel moves the highlight with the view instead of scrolling the
82
+ viewport away from it.
83
+ - Resume preflights first. `4top check KEY` reports whether a session can resume
84
+ here, and the panel asks before it hands over the terminal, so a host without
85
+ that agent installed is a visible message instead of a failure that flashes past
86
+ under a repainted screen. A hand-over that still fails reports its exit code, and
87
+ a dropped ssh connection says that the session is unchanged in its transcript.
88
+ - The ssh connection keeps a liveness probe and a warm control connection, so an
89
+ unstable link fails instead of hanging.
90
+ - `doctor` reports the `revision` of the code it is running, and `--host NAME doctor`
91
+ reports both, so two machines on different revisions is visible instead of turning
92
+ into a confusing error later.
93
+ - `scripts/remote_update.py NAME` brings a remote's 4top forward. It uses the host's
94
+ own egress first and, only if that fails, lends this machine's proxy through a
95
+ reverse tunnel that lives exactly as long as the update. Both hosts were observed
96
+ with a proxy that answers on one port and fails on another, or works and then stops,
97
+ so the fallback is a real case and it says which one it used.
98
+ - `scripts/check_docs.py` fails when the documentation stops describing the code,
99
+ and runs in a pre-commit hook, the test suite and CI.
100
+
101
+ ### Fixes in this line
102
+
103
+ - A large history no longer costs a full re-render on every refresh. Rows whose
104
+ inputs are unchanged keep their rendered cells and their derived labels, so a
105
+ 2.7k-row store renders in under two milliseconds per tick instead of about 50 ms.
106
+ - Recognize Claude 2.1.x metadata-only session files. Claude prepends records such
107
+ as `last-prompt`, `mode`, `attachment` and `cost-state`, and a session that was
108
+ opened, renamed and quit never writes a user or assistant message; those files
109
+ were reported as `ValueError` instead of being listed. Identity now comes from
110
+ the `sessionId` these records carry, and the native `ai-title`/`agent-name` is
111
+ used as the title only when the session has no user text. `PARSER_VERSION` is 2
112
+ in both parsers, so the first scan after upgrading re-reads every file once.
113
+ - Report why a history file was rejected, not only the exception class, and keep
114
+ OSError text (which contains the private path) out of the message.
115
+ - Print the human table without the terminal UI stack. Display width is computed
116
+ with `unicodedata`, so `4top list` works where only the standard library is
117
+ installed. Found by running the CLI on a host that had neither the UI dependency
118
+ nor a package manager to add it.
119
+ - Stop reporting a non-directory that a history pattern matched as an
120
+ unavailable directory. A real store keeps a marker file inside its project
121
+ directory, so every scan reported an issue and every list exited 6. A configured
122
+ root that is not a directory is still reported once, and a genuinely unreadable
123
+ directory is still reported.
124
+ - Stop reporting a remote login banner as a query issue. Only diagnostics prefixed
125
+ by the remote CLI count, so a healthy host no longer looks broken and no longer
126
+ exits 6. Found by pointing `--host` at a machine whose ssh shell prints a banner.
127
+ - Correct the acceptance record: Pi 0.87.0 is installed on the acceptance Mac and
128
+ its capability probe passes. No authenticated Pi smoke check was run, and the
129
+ previous "not installed" statement was wrong.
130
+
131
+ ## 4top 0.1.0a2 — 2026-09-24
132
+
133
+ - Preserve absent native store environment overrides. In particular, launching
134
+ Claude no longer relocates its default configuration and triggers onboarding
135
+ for an already-authenticated installation. Explicit profiles still take priority.
136
+ - Add 13 native-root regression cases covering all three agent drivers.
137
+ - Explain that resume uses current native configuration, not replayed launch flags.
138
+ - Validate 141 automated tests on macOS 27.0 arm64, plus a separate authenticated
139
+ Codex 0.155.1 exact-resume smoke check.
140
+ - Add 32 regression cases hardening the then-current process-ownership layer
141
+ against exit races, PID reuse and malformed ownership data.
142
+ - Establish the independent `4ier/4top` repository and fresh-wheel installation.
143
+ - Keep Pi, other native versions, physical SSH loss and Linux native validation
144
+ explicitly outside this release's compatibility evidence.
145
+
146
+ See [the Mac acceptance record](docs/validation/macos-0.1.0a2.md).
147
+
148
+ ## 4top 0.1.0a1 / session-ls 0.2.0
149
+
150
+ - Initial keyboard-first TUI and scriptable CLI over a shared service layer.
151
+ - Exact-target process management, bounded previews, safe destructive actions,
152
+ explicit history linking and same-history reservations. That layer is not part
153
+ of the current line; see the unreleased section above.
154
+ - Experimental Claude/Codex/Pi exact resume drivers; read-only Cursor history.
155
+ - Private atomic state, no extra daemon, no account.
156
+ - Literal Unicode full search, cancellation, stable selection, no-color, isolated demo.
157
+ - Real process and terminal tests alongside core and headless UI tests.
158
+ - session-ls keeps its six-field JSON interface and independent stdlib-only package.
159
+ Imports no longer change SIGPIPE; full search now follows documented literal
160
+ semantics instead of accidentally interpreting a grep regular expression.
161
+ - Cached file identity includes parser version and inode metadata; cache writes use
162
+ unique temporary names. Corrupt caches are rebuilt rather than silently trusted.
163
+
164
+ This alpha does not certify authenticated native CLI compatibility, all minimum
165
+ platform versions, cross-machine aggregation, or external-user usability gates.
@@ -0,0 +1,96 @@
1
+ # Contributing to 4top
2
+
3
+ Start with a reproducible bug or a small adapter fixture. The project intentionally
4
+ leaves process ownership, persistence and multiplexing to the user and to the
5
+ original CLI; proposals for agent loops, cloud accounts, semantic state guesses,
6
+ process supervisors or automatic privilege bypasses are outside this release.
7
+
8
+ ## Development
9
+
10
+ ```sh
11
+ python3 -m venv .venv
12
+ .venv/bin/python -m pip install -e ./packages/session-ls -e '.[dev]'
13
+ .venv/bin/python -m pytest
14
+ .venv/bin/python -m ruff check .
15
+ git config core.hooksPath .githooks # once per clone: run the gates before commit
16
+ ```
17
+
18
+ `scripts/check_docs.py` fails when the documentation stops describing the code: an
19
+ undocumented command or key, a documented command that no longer exists, a missing
20
+ configuration option, a broken relative link, or vocabulary from a design that has
21
+ been removed. It runs in the pre-commit hook, in the test suite and in CI, so a
22
+ stale README blocks the change instead of misleading the next reader.
23
+
24
+ To make the `4top` command itself run this checkout, install it as an editable
25
+ tool. Otherwise a previously installed copy keeps running older code, which looks
26
+ like a bug in the change you just made.
27
+
28
+ ```sh
29
+ uv tool install --force --editable . # both packages stay live
30
+ ```
31
+
32
+ No multiplexer is required. `tests/integration` starts real fake agents as real
33
+ processes and asserts their argv, cwd, environment and written session file; the
34
+ remote tests use a fake `ssh` on `PATH` instead of a network. Tests never access the
35
+ developer's native HOME/store or the real state directory. Unexpected external
36
+ pytest plugins can be excluded with
37
+ `PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python -m pytest -p pytest_asyncio.plugin`.
38
+
39
+ Use `requirements-dev.lock` to reproduce the tested development dependency set.
40
+ The root package and session-ls have independent distribution metadata; do not put
41
+ both console scripts into the root wheel or create a circular dependency.
42
+
43
+ ## Required evidence
44
+
45
+ A PR explains the user-visible change, safety boundary, tests, actual environment,
46
+ uncovered cases and rollback. Adapter PRs include synthetic/redacted fixtures and
47
+ an exact native CLI version with capabilities split into history/new/resume.
48
+ No arbitrary plugin code is loaded from history or configuration.
49
+
50
+ Acceptance must distinguish unit tests, headless Textual, real-process launch,
51
+ remote transport and human usability tests. Do not turn a mock result into an authenticated smoke-test
52
+ claim. Performance results name the dataset, machine, versions and measurement.
53
+ No recorded workload needs to contain real customer data.
54
+
55
+ Run and update the [acceptance guide](docs/validation/README.md) before a release.
56
+ Export the synthetic UI with `python scripts/export_demo.py`; the result must say
57
+ DEMO. It is not a real-CLI demonstration or a performance benchmark.
58
+
59
+ ## Release
60
+
61
+ Publishing is a tag; CI does the rest. PyPI versions are immutable, so a gate runs
62
+ before anything is uploaded.
63
+
64
+ 1. Set the version in `pyproject.toml`, and in `packages/session-ls/pyproject.toml`
65
+ when the parser changed, and update the changelog.
66
+ 2. Check locally: `python scripts/check_release.py v<version>`.
67
+ 3. Tag and push: `git tag v<version> && git push origin v<version>`.
68
+ 4. CI runs the suite, gates the release, builds both wheels, installs them into a
69
+ fresh virtual environment, publishes `4top`, and opens the GitHub release.
70
+
71
+ The gate is what stops the release that cannot work: the root wheel requires
72
+ `session-ls>=0.2.0`, PyPI only ever held 0.1.0, so publishing the root alone would
73
+ have produced a package nobody could install. A version already on PyPI is skipped
74
+ rather than treated as an error, so re-running a release is safe;
75
+ `python scripts/check_release.py --decide` says what would be uploaded.
76
+
77
+ Uploads use PyPI trusted publishing, so no token is stored here. It needs one
78
+ publisher on PyPI for the project `4top`, pointing at owner `4ier`, repository
79
+ `4top`, workflow `publish.yml` and environment `pypi`.
80
+
81
+ This repository publishes one project. `session-ls` has its own repository and its
82
+ own pipeline and is consumed here as an ordinary PyPI dependency; publishing two
83
+ projects from one workflow would need two jobs and two distinguishable publishers,
84
+ because one job means one OIDC exchange and the minted token is scoped to the project
85
+ its publisher matched. The
86
+ manual trigger of that workflow defaults to a dry run, which rehearses the whole
87
+ path without uploading.
88
+
89
+ ## Reporting
90
+
91
+ Use the bug template and redact paths, prompts and credentials. For security issues,
92
+ see SECURITY.md. Do not attach native auth files, raw production transcripts, or an
93
+ unreviewed screen recording. No automated issue upload is performed by 4top.
94
+
95
+ Code is MIT-licensed. Preserve attribution to the existing session-ls core. Keep
96
+ English and Chinese README commands and limitations in sync.
4top-0.2.0a1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 4ier
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,5 @@
1
+ include LICENSE README.md README.zh-CN.md CHANGELOG.md CONTRIBUTING.md SECURITY.md requirements-dev.lock
2
+ recursive-include docs *.md *.svg *.json
3
+ recursive-include tests *.py
4
+ recursive-include scripts *.py *.sh
5
+ recursive-exclude * __pycache__ *.pyc
4top-0.2.0a1/PKG-INFO ADDED
@@ -0,0 +1,233 @@
1
+ Metadata-Version: 2.4
2
+ Name: 4top
3
+ Version: 0.2.0a1
4
+ Summary: Your coding agents, one terminal. Find, search and resume native sessions, locally or over SSH.
5
+ Author: 4ier
6
+ License-Expression: MIT
7
+ Project-URL: Repository, https://github.com/4ier/4top
8
+ Project-URL: Issues, https://github.com/4ier/4top/issues
9
+ Keywords: coding-agents,terminal,session-history,claude,codex,pi,ssh
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Console :: Curses
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Classifier: Operating System :: MacOS
14
+ Classifier: Programming Language :: Python :: 3
15
+ Requires-Python: >=3.11
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: session-ls<0.3,>=0.2.0
19
+ Requires-Dist: textual<9,>=8.2.8
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest<10,>=8; extra == "dev"
22
+ Requires-Dist: pytest-asyncio<2,>=0.23; extra == "dev"
23
+ Requires-Dist: pexpect<5,>=4.9; extra == "dev"
24
+ Requires-Dist: ruff<0.17,>=0.16; extra == "dev"
25
+ Requires-Dist: build<2,>=1; extra == "dev"
26
+ Dynamic: license-file
27
+
28
+ # 4top
29
+
30
+ **Your coding agents, one terminal.**
31
+
32
+ Find your work. Resume an exact native session. Reach your other machines over SSH.
33
+ A keyboard-first terminal dashboard built on **session-ls**, with no extra 4top
34
+ daemon, account, model calls, or telemetry.
35
+
36
+ [中文](README.zh-CN.md) · [Compatibility](docs/compatibility.md) · [Remote hosts](docs/remote-design.md) · [Acceptance](docs/validation/README.md)
37
+
38
+ ![4top synthetic demo — no real user history](docs/demo/demo.svg)
39
+
40
+ > **0.2.0a1 — alpha.** The current development line owns no process and drives no
41
+ > multiplexer of its own. Codex **0.155.1** passed an authenticated exact-resume
42
+ > smoke check against an earlier build;
43
+ > Claude Code, Pi and other native versions are not certified.
44
+ > [Evidence](docs/validation/macos-0.1.0a2.md). This release is not on PyPI.
45
+
46
+ ## Install from this checkout
47
+
48
+ macOS or Linux; **Python 3.11+**. No multiplexer is required. Current validation
49
+ records, rather than this minimum target, determine which versions were tested.
50
+ Windows users need a Linux environment such as WSL; native Windows is unsupported.
51
+
52
+ ```sh
53
+ git clone https://github.com/4ier/4top.git
54
+ cd 4top
55
+ python3 -m venv .venv
56
+ .venv/bin/python -m pip install ./packages/session-ls .
57
+ .venv/bin/4top --demo
58
+ ```
59
+
60
+ Both local packages are intentionally supplied to pip: `session-ls 0.2.0` is not
61
+ assumed to exist on a package registry. The root wheel contains only `fourtop`;
62
+ the independent `session-ls` package retains its small, stdlib-only CLI.
63
+
64
+ ```sh
65
+ . .venv/bin/activate
66
+ 4top # browse every session on this machine
67
+ 4top new codex # start an agent here, in this terminal
68
+ 4top --host build-box # view another machine over ssh
69
+ ```
70
+
71
+ 4top never installs or authenticates agents for you. Install the original Claude
72
+ Code, Codex, or Pi CLI separately and keep its existing authentication flow.
73
+ The panel also reads Cursor transcripts, but does not launch or resume Cursor.
74
+
75
+ ## The daily loop
76
+
77
+ Open `4top` and you get every session on the machine, most recent first. Select a
78
+ row and press **Enter**: 4top asks for confirmation and then runs the native CLI
79
+ in this terminal to resume that exact session. Leave the agent and you are back in
80
+ the panel. **`q` closes only the panel.**
81
+
82
+ A plain terminal or any multiplexer you already run is equally fine: 4top starts
83
+ the agent in the terminal it was launched from and never allocates a terminal of
84
+ its own. If you want a session to survive closing your laptop, run 4top inside the
85
+ multiplexer you already use.
86
+
87
+ | Key | Action |
88
+ | --- | --- |
89
+ | `↑` / `↓`, `Enter` | Select and resume |
90
+ | `H` | Switch the panel between this machine and a configured host |
91
+ | `/`, `Enter`, `Esc` | Search metadata, return to table, clear/cancel |
92
+ | `Ctrl-F` | Explicit literal full-content search; `Esc` cancels |
93
+ | `Space`, `i` | Read-only preview, details |
94
+ | `n`, `r`, `?` | New agent, refresh, help |
95
+ | `q`, `Ctrl-C` | Close only the panel |
96
+
97
+ Search supports case-insensitive words and quoted phrases; all terms must match.
98
+ Full search decodes JSON text, including Chinese escaped as `\u....`. It reads
99
+ only configured sources, reports partial scans, and never executes transcript
100
+ content. The UI renders titles and previews as plain, sanitized text.
101
+
102
+ ## Sessions, not processes
103
+
104
+ A native agent is a file. `pi --session <path>`, `claude --resume <id>` and
105
+ `codex resume <id>` all work from the transcript alone, so the transcript is the
106
+ thing 4top tracks, and the process is the ephemeral part.
107
+
108
+ **Resume** starts a new native process from an exact ID or source path. It cannot
109
+ restore lost memory, network connections, shell children, or a destroyed machine.
110
+ Resume uses the CLI's **current native configuration**; 4top does not replay the
111
+ original launch flags. Review native permissions before sending another task.
112
+
113
+ Because 4top owns no process it claims nothing about liveness either: a row is a
114
+ session you can resume, and that is all it says. Failed queries are reported as
115
+ issues and never rendered as an empty machine.
116
+
117
+ Two consequences worth knowing. A resumed agent is a **new** process; two agents in
118
+ one directory still have **no code/worktree isolation**. And `4top new` runs the
119
+ agent in the foreground of the terminal you launched it from: outside a multiplexer
120
+ it ends with that terminal.
121
+
122
+ ## Remote hosts over SSH
123
+
124
+ Point 4top at any machine you can already `ssh` into. There is no daemon to
125
+ install, no port to open, and no credential store: the remote side is the same
126
+ CLI, and the local 4top only runs it.
127
+
128
+ ```toml
129
+ # ~/.config/4top/config.toml
130
+ [hosts.build-box]
131
+ ssh = "me@build-box" # any ssh destination, including a tailnet name
132
+ # command = "/opt/4top/bin/4top" # if a non-login PATH does not include 4top
133
+ # refresh_seconds = 15.0 # slower than local: each tick is a round trip
134
+ # timeout_seconds = 10.0
135
+ ```
136
+
137
+ ```sh
138
+ 4top --host build-box # the whole panel, scoped to that host
139
+ 4top --host build-box list --json
140
+ 4top --host me@10.0.0.4 doctor # an unconfigured target works too
141
+ ```
142
+
143
+ Views stay isolated: the default scope is this machine, and a host replaces it
144
+ rather than merging machines into one table. `H` switches the panel between them
145
+ without leaving it; `--host NAME` starts the panel already scoped. Anything that starts a process runs
146
+ **on that host** through `ssh -t`, so the resumed agent lives where its history
147
+ lives; the remote CLI does the work and the local side only hands over the
148
+ terminal. Connection reuse (`ControlMaster`) keeps refreshes cheap, `BatchMode`
149
+ means a missing key fails fast instead of prompting, and a remote that speaks a
150
+ different row schema is refused instead of partially parsed.
151
+
152
+ Before it hands over the terminal, the panel asks the host that owns the session
153
+ whether the resume can work there (`4top check`). A host without that agent
154
+ installed, or a session whose directory is gone, is reported in the panel instead
155
+ of failing during the hand-over, where the message would be painted over. The ssh
156
+ connection also keeps a liveness probe, so a link that dies becomes an error rather
157
+ than a hang, and the session stays in its transcript to be resumed again.
158
+
159
+ ## Command line
160
+
161
+ ```sh
162
+ 4top list --json # one JSON object per row
163
+ 4top list --agent pi --project 4top
164
+ 4top search 'retry "database timeout"' # metadata match
165
+ 4top search '中文' --full # decoded full-content search
166
+ 4top preview h_<key> # one bounded read-only page
167
+ 4top check h_<key> --json # would a resume work here, and why not
168
+ 4top new codex -- --model MODEL # native arguments after --
169
+ 4top resume h_<key> --yes # restore this process as the agent
170
+ 4top doctor --json
171
+ ```
172
+
173
+ `doctor` also reports `revision`, and `4top --host NAME doctor` reports it for both
174
+ sides, so a remote running older code is visible instead of failing later. Bring a
175
+ remote forward with `scripts/remote_update.py NAME`; it uses the host's own egress
176
+ first and falls back to a tunnel from this machine.
177
+
178
+ `--config`, `--host` and `--no-color` work before or after the subcommand.
179
+ `check` exits 0 when the session can resume here and 3 when it cannot. Keys
180
+ may be shortened only when their prefixes are unambiguous (at least four
181
+ characters). Row numbers are never execution targets. `list --json` rows carry
182
+ `schema_version`, `key`, `agent`, `host`, `cwd`, `title`, `started`, `last`,
183
+ `source`, `status` and `can_resume`.
184
+
185
+ ## Configuration and privacy
186
+
187
+ Optional configuration: `$XDG_CONFIG_HOME/4top/config.toml` (default
188
+ `~/.config/4top/config.toml`). No setup file is needed for standard stores.
189
+
190
+ ```toml
191
+ [ui]
192
+ refresh_seconds = 1.0
193
+ history_refresh_seconds = 5.0
194
+ color = "auto" # or "none"; NO_COLOR is also supported
195
+
196
+ [history]
197
+ metadata_max_bytes = 2097152
198
+ metadata_max_lines = 2000
199
+ preview_max_lines = 200
200
+
201
+ [agents.codex]
202
+ # root = "/absolute/path/to/codex-home"
203
+ # executable = "/absolute/path/to/a-real-wrapper"
204
+ ```
205
+
206
+ Agent store roots respect `CODEX_HOME`, `CLAUDE_CONFIG_DIR`, and
207
+ `PI_CODING_AGENT_DIR`; an explicit configured root wins. Selected history and
208
+ launch profile must agree. Local state is private: `$XDG_STATE_HOME/4top` holds a
209
+ local identity and your last selection, and `$XDG_CACHE_HOME/4top` holds
210
+ rebuildable metadata. No environment values, prompt text, or transcripts are
211
+ retained. The only network access is the ssh you configured.
212
+
213
+ [Privacy](docs/privacy.md) · [Troubleshooting](docs/troubleshooting.md) · [Design](docs/design.md)
214
+
215
+ ## Develop and contribute
216
+
217
+ ```sh
218
+ .venv/bin/python -m pip install -e ./packages/session-ls -e '.[dev]'
219
+ .venv/bin/python -m pytest
220
+ .venv/bin/python -m ruff check .
221
+ uv tool install --force --editable . # optional: `4top` runs this checkout
222
+ ```
223
+
224
+ Tests use private temporary HOME/state directories and synthetic agents; the ssh
225
+ tests use a fake `ssh` on `PATH`. No account credentials, network access or model
226
+ calls are required. Record your OS, Python and native CLI versions when reporting
227
+ compatibility. **Never post raw transcripts or tokens.**
228
+
229
+ For fresh installation, CI, reproducible demo export, native smoke testing, and
230
+ release gates, see [CONTRIBUTING](CONTRIBUTING.md) and the [acceptance guide](docs/validation/README.md).
231
+
232
+ 4top builds on 4ier's `session-ls` parsers and uses Textual. It is not affiliated
233
+ with the vendors of the supported coding agents. **MIT licensed.**