# PocketShell's main branch is an unreleased rewrite that does not open an SSH connection

> A voice-first Android SSH client where the default branch has been replaced by a Vue 3, TypeScript and Capacitor application that is explicitly untagged and not yet connected to any host, while the released Kotlin app sits on a separate branch. The build is careful about provenance: one pinned submodule, a lockfile, and an APK manifest that records the core commit and a hash of the bundled web assets.

**alexeygrigorev/pocketshell** — Voice-first, tmux-native, agent-aware Android SSH client

- Repository: https://github.com/alexeygrigorev/pocketshell
- Stars: 39 · Forks: 6
- Language: Kotlin
- License: NOASSERTION
- Published: 2026-08-26 · Updated: 2026-08-26 · Language: en
- Canonical page: https://hysenlabs.com/projects/alexeygrigorev-pocketshell

## The default branch builds a shell that has never connected to a host

The most important sentence on the page is about what the app does not yet do. The shell currently displays offline host and workspace states and a sample terminal, and it does not yet connect to SSH hosts or implement the feature set of the previous app. Everything else on the page, from the build instructions to the CI gate description, describes a real and working pipeline around a product that is not finished. So the correct reading of this repository is that the plumbing is done and the function is not: you can build an APK, install it, and look at a terminal emulator rendering a sample session, but you cannot point it at a server. The description calls the project voice-first, tmux-native and agent-aware, and none of those three qualities are demonstrated by what is currently on the default branch.

```
git clone --recurse-submodules https://github.com/PocketShell-io/pocketshell.git
cd pocketshell
pnpm install --frozen-lockfile
scripts/run-js-unit-gate.sh
scripts/assemble-debug.sh
```

## The working application lives on a different branch from the main one

Two applications live in one repository and you have to know which one you are reading. The default branch carries the 0.6.0 development line, which is the new web-stack rewrite. The released 0.5.x Kotlin application lives on a branch named for its release series. That means the branch you land on by default is not the one you want if your goal is a usable SSH client, which is an unusual and easy-to-miss arrangement. The documentation is at least explicit about it, and it adds the condition under which the development line becomes usable: it is untagged and unreleased until its release gates pass. The release history bears that out in an odd way, since alongside ordinary version tags the repository carries a tag with a validation-flavoured name dated the same day as one of the 0.5.x tags, which suggests the rewrite work is being gated through its own tag stream.

## One submodule, pinned, imported as source rather than pulled from a registry

The dependency story has one unusual decision and it is worth understanding before you build. There is exactly one submodule in the repository, a core package pinned by the superproject. The Android side does not install it from a registry and does not consume it as a published package. Instead it imports two things directly from the submodule source: the core TypeScript entry point under one alias, and the browser-safe user interface source directory under another alias that desktop and web builds also use. That means the interface is literally the same files as the desktop project's interface, not a copy of it, which is what keeps the visual identity consistent between the two. It also means the clone must be recursive. A plain clone produces a repository with an empty directory where the interface should be, and the failure shows up during the build rather than during the clone.

## The APK manifest records which core commit went in and what the web assets hashed to

Given that the interface comes from a submodule rather than a registry, provenance is the interesting part, and the documentation is unusually specific about it. The build manifest for the Android package records the commit of the core submodule that was used, and records the SHA-256 of the web assets that were bundled into the APK. That pairing answers the two questions you would otherwise have after the fact: which version of the shared interface is in this build, and whether the web bundle in the installed package is byte for byte the one that was built. The documentation also retains exported database schemas for a future installed-data reader, filed away in a migration directory rather than deleted, which is the other half of good provenance on an application that has to read its own previous version's local database after an upgrade.

```
scripts/test-agents-fixture-aplexer.sh --docker
```

## Every JavaScript dependency is pinned to an exact version

Look at the dependency list in the manifest and there is not a single version range in it. Every framework, plugin and tool is written as an exact version, no operator prefix anywhere. That is unusual for a web application and it tells you what the team prioritised: reproducible builds over easy upgrades. The runtime side is small and tells you what the rewrite actually is. A Vue version, a state store, the Capacitor core and its two plugins at matching versions, a variable font package, and the terminal emulator with its fit addon. That terminal dependency is the clearest signal about intent, since an SSH client that renders a remote session inside a native view needs a terminal emulator rather than a text area. The build tooling mirrors the same discipline with pinned compiler, bundler, test runner and type checker versions.

## The web build is blocked behind a source verification step

The build script for the web application does not simply run the bundler. It runs a verification script first, then a type check, then the bundler, and it stops on the first failure. The verification step is a separate Node script, and the fact that it is a distinct gate rather than a lint rule suggests it is checking something structural about the source tree rather than style. That ordering matters for a repository whose interface lives in a submodule: it is a place to catch an import that resolves outside the intended boundary, or a stale copy of a file that should have come from the pinned core. The Android debug build then chains three steps in order, building the web bundle, synchronising it into the native project, and invoking the platform build tool against the Android directory. There is also a variant that builds under a suffixed package name and installs it, which is how you keep a personal build from colliding with the real one.

```
pnpm build:web && pnpm cap:sync && ./android/gradlew -p android assembleDebug
```

```
scripts/assemble-debug.sh --suffix local --install
```

## The Node floor in the manifest and the floor in the docs disagree

A small inconsistency is worth flagging because it affects whether your build works on the first try. The requirements line on the page asks for Node.js 22, a specific package manager version, JDK 21, and a particular Android SDK platform. The manifest's engine field asks for Node 20 or newer. So the documented requirement is stricter than the declared one by two major versions. For most contributors this is harmless, since a machine on Node 22 satisfies both. It matters in two situations: on a machine pinned to Node 20, where the manifest will happily let a build start that the documentation says is unsupported, and in continuous integration, where an image built to satisfy the engine field alone could pass the manifest check and fail later on something the stricter floor was protecting against. Pinning your toolchain to what the page says is the safe move.

## A debug keystore and a cgroups config sit at the repository root

Two files at the top level tell you about how this project is developed rather than what it does. A debug keystore file is committed at the root, and the documentation states plainly that the application keeps its existing debug key, which tells you the rewrite branch has not replaced the signing setup even as it has replaced essentially everything else. Alongside it sits a cgroups configuration file, which on Android governs how much CPU and memory a process group gets and is a plausible companion to the agent and container fixture work the repository preserves. There are also two directories with dotted names holding agent instructions and an internal document directory prefixed to keep it out of the build, plus a process document at the root. None of this is documented in prose, which means the repository's conventions are discoverable only by opening it.

## Conclusion

Use the release branch if you want a working Android SSH client today, and treat the default branch as a development line you read rather than one you install. Two things to check before you build either. Which branch you actually want, because they are different applications with different toolchains, one Kotlin and one a web build wrapped by Capacitor. And that you clone recursively, because the shared interface package is a submodule rather than a registry dependency, and a shallow or plain clone fails later at build time rather than at clone time. The rewrite is tracked to a named umbrella issue and a second issue for its gate replacements, so its state is documented rather than guessed at.

## FAQ

### What is the point of a shell jacket?

This repository is about software, not clothing. PocketShell is a voice-first Android SSH client. Its released 0.5.x Kotlin application lives on a separate release branch, and the default branch carries an untagged 0.6.0 rewrite built with Vue 3, TypeScript and Capacitor.

### What are jackets with inside pockets called?

Nothing here concerns garments. What the repository documents is a build pipeline: a recursive clone, a frozen lockfile install, a JavaScript unit gate script and a debug assembly script, requiring Node.js, a specific package manager, JDK 21 and a particular Android SDK platform.

### What does "waterproof shell" mean in PocketShell?

Here shell means a terminal shell, and the app renders one. It depends on a terminal emulator with a fit addon inside the Capacitor web view, and the default branch currently shows a sample terminal with offline host and workspace states rather than a live connection to any SSH host.

### What's the difference between a rain jacket and a rain shell?

The repository addresses no clothing question. What it does document is the state of its rewrite: the development branch builds the web app and a debug package, runs unit tests and checks a Docker fixture, while emulator parity journeys, scheduled verdicts and release packaging remain owned by the existing Android gates until a tracked issue provides validated replacements.

## Sources

- [Official README](https://github.com/alexeygrigorev/pocketshell#readme)
- [Project repository](https://github.com/alexeygrigorev/pocketshell)
- [Release notes](https://github.com/alexeygrigorev/pocketshell/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/alexeygrigorev-pocketshell
