Compare commits

...

2 Commits

Author SHA1 Message Date
Bastien Chanot
ea33e231ef docs(memory): add BDR-009 + LRN-008 (dtach menu -> ~/.bashrc, VS Code non-login)
- BDR-009: reverses BDR-007; menu moved to ~/.bashrc because VS Code
  Remote-SSH terminals are non-login; per-tab firing accepted over a
  once-per-connection sentinel.
- LRN-008: VS Code Remote-SSH (Linux) terminals are non-login -> skip
  ~/.profile; hook ~/.bashrc for "run once at session start"; diagnose via
  VSCODE_IPC_HOOK_CLI + no login-bash ancestry + shopt -q login_shell.
- journal: 2026-06-25 session entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CN1KSmsuLG6TxSeN5m8xvM
2026-06-26 00:09:16 +02:00
Bastien Chanot
e37eb77ed5 fix(dtach): wire resume menu into ~/.bashrc for non-login VS Code shells
VS Code Remote-SSH integrated terminals are non-login interactive shells:
they source ~/.bashrc but never ~/.profile, where the resume menu was wired
(login-scope, BDR-007). The auto-check therefore never fired in the user's
actual environment, even after install.sh.

Source dtach-router from bashrc-linux (every interactive shell) instead, and
turn install.sh's wire_dtach_profile() into unwire_dtach_profile(): it now
strips any stale ~/.profile block so a plain SSH login (which reads ~/.bashrc
via ~/.profile) does not prompt twice. README updated to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CN1KSmsuLG6TxSeN5m8xvM
2026-06-26 00:09:16 +02:00
6 changed files with 53 additions and 23 deletions

View File

@ -60,3 +60,16 @@ grant asserted in README per FSF convention, LICENSE holds plain GPLv3 text. Alt
(permissive — allow CLOSED derivatives, weaker open guarantee); Unlicense (public domain, no copyleft).
Repo private (CLAUDE.md Public=no) so license optional, but user wanted one set. Reversible: swap LICENSE +
README line if "full opensource" meant permissive. Status: done in repo (uncommitted).
## BDR-009 — dtach resume menu moved ~/.profile → ~/.bashrc (every interactive shell)
2026-06-25. Reversed BDR-007. Root cause: user works in VS Code Remote-SSH; its Linux integrated terminals are
NON-login interactive shells → read `~/.bashrc`, never `~/.profile` → login-scoped wiring (BDR-007) silently
never fired (LRN-008). Now source dtach-router from `bashrc-linux` via `case $- in *i*) … . dtach-router`;
fires in EVERY interactive shell (covers VS Code, plain SSH via `~/.profile`→`~/.bashrc`, tmux, new tabs).
install.sh `wire_dtach_profile()``unwire_dtach_profile()`: strips any stale `~/.profile` block (marker +
legacy) so plain SSH login (sources `~/.bashrc` via `~/.profile`) doesn't prompt twice. Trade-off ACCEPTED
(user chose "simplest"): menu shows in each new terminal tab when sessions exist, not once-per-connection —
the exact noise BDR-007 avoided, now tolerated for VS Code reliability. Alts rejected: (a) once-per-connection
sentinel keyed to `SSH_CONNECTION`/`VSCODE_IPC_HOOK_CLI` in `$XDG_RUNTIME_DIR` — more code, user declined;
(b) VS Code `terminal.integrated` `args:["-l"]` — not carried by dotfiles, same per-tab firing. Supersedes
BDR-007. Status: done in repo; live needs `./install.sh` re-run.

View File

@ -41,3 +41,12 @@ SOURCE `case $- in *i*) ... . dtach-router` via idempotent wire_dtach_profile()
marker block, re-appends marker block). Added cc (create) / d (re-summon) aliases to bashrc-linux.
shellcheck + bash -n CLEAN; migration simulated on real .profile copy. LRN-006 + BDR-007. README synced.
Not committed; live ~/.profile not yet re-migrated.
## 2026-06-25 — dtach menu: ~/.profile → ~/.bashrc (VS Code non-login fix)
User: dtach resume menu never fires at session start, even post-install. Root cause: user runs VS Code
Remote-SSH → its Linux terminals are NON-login → skip ~/.profile (where BDR-007 wired it). Proven by process
tree (VSCODE_IPC_HOOK_CLI, no sshd/login-bash) + provably-correct ~/.profile wiring + existing session yet zero
menu. Fix: source dtach-router from bashrc-linux (every interactive shell); install.sh wire_dtach_profile() →
unwire_dtach_profile() strips stale ~/.profile block (avoids double-prompt on plain SSH). User chose simplest
(per-tab) over once-per-connection sentinel. shellcheck install.sh CLEAN, bash -n OK, strip proven idempotent
on .profile copy. BDR-009 (supersedes BDR-007) + LRN-008. Live needs ./install.sh re-run.

View File

@ -56,3 +56,14 @@ returned EMPTY while code-server + RDP stayed undocumented. A partial doc commit
hides earlier feature drift. Fix: drift-detect against FEATURE commits (scan `git log` for feat/* touching
source since the doc's last SUBSTANTIVE edit, OR cross-ref each entry-point / install-step in code vs doc
text) — never trust doc timestamp alone. Surfaced by /doc clean this session.
## LRN-008 — VS Code Remote-SSH terminals are non-login → skip ~/.profile
2026-06-25. VS Code Remote-SSH (Linux) integrated terminals = NON-login interactive bash → source `~/.bashrc`,
NEVER `~/.profile`. Any login-scoped startup wiring (`~/.profile`, `~/.bash_profile`) silently never runs there.
Diagnose via process tree: `VSCODE_IPC_HOOK_CLI` env + `.vscode-server/.../remote-cli` in PATH, no `sshd` /
no `bash -l` ancestry (the launching shell reparents to systemd); confirm with `shopt -q login_shell`. For
"run once at session start" that must work under VS Code, hook `~/.bashrc` (universal: sourced by login shells
via `~/.profile` AND directly by non-login interactive shells), NOT `~/.profile`. Extends/corrects LRN-006
(its `~/.profile` fix is valid only for real login shells, not IDE remotes). Deductive tell that pinned it:
wiring proven correct + target resource (session) proven present, yet menu never fires at startup → the startup
file is not being sourced → non-login shell. See BDR-009.

View File

@ -23,7 +23,7 @@ curl -fsSL https://git.bchanot.fr/bchanot/config/raw/branch/master/remote-instal
| `bash/bashrc-linux` | bashrc for desktop Linux (git-aware prompt + command timer). |
| `bash/bashrc-osx` | bashrc for macOS. |
| `bin/dt` | dtach session manager for claude-in-dtach sessions. |
| `bin/dtach-router` | SSH-login dashboard to resume dtach sessions (wired into `~/.profile` by the installer). |
| `bin/dtach-router` | Dashboard to resume dtach sessions, shown at the start of every interactive shell (wired into `~/.bashrc` by the installer). |
| `bin/claude-provider`| Switch Claude Code between Anthropic and OpenRouter. |
| `etc/profile.d/disk-usage-warning.sh` | Login-time warning (bold red) when `/` or `/home` cross 85% usage. Deployed to `/etc/profile.d/` on Linux. |
@ -57,7 +57,7 @@ What it does:
5. Copies the tracked vim files into `~/.vim` and symlinks `~/.vimrc`.
6. Picks the bashrc by OS: macOS → `bashrc-osx` (falls back to `bashrc-linux` if missing), everything else → `bashrc-linux`. Copies it to `~/.bashrc`.
7. Installs Python CLIs via `pipx` (`PyMuPDF` → `pymupdf`, `Markdown``markdown_py`) — skipped if `pipx` is absent.
8. Copies the `bin/` scripts (`dt`, `dtach-router`, `claude-provider`) into `~/.local/bin` and wires the dtach session-resume menu into `~/.profile` (idempotent — sourced only at interactive login, and replaces any prior block).
8. Copies the `bin/` scripts (`dt`, `dtach-router`, `claude-provider`) into `~/.local/bin`. The dtach session-resume menu ships in the deployed `bashrc-linux`, so every interactive shell offers it — including VS Code Remote-SSH terminals, which are non-login and never read `~/.profile`. The installer also strips any older dtach block left in `~/.profile` so a plain SSH login doesn't prompt twice.
9. On Linux, installs `etc/profile.d/disk-usage-warning.sh` to `/etc/profile.d/` (needs `sudo`) so each login warns when `/` or `/home` cross 85% usage.
10. On Linux, installs **code-server** (VS Code in the browser) via its vendor script — skipped if already present — and enables the `code-server@$USER` systemd service.
11. On Linux, sets up **RDP remote login** via `gnome-remote-desktop` (Wayland-native): installs the daemon + `openssl`, generates a self-signed TLS cert once, and prompts interactively for shared "gate" credentials (skipped when no terminal is attached, or already set). Disables `xrdp` if present; opens UFW port `3389` only when UFW is already active.
@ -82,7 +82,7 @@ The script is re-runnable: each run re-backs up to `~/Oldconfig` (overwriting th
Deployed to `~/.local/bin` (the deployed bashrc adds this dir to `PATH`):
- **`dt`** — manage claude-in-dtach sessions (`dt ls|at|kill`). Needs `dtach` + `fzf`.
- **`dtach-router`** — session dashboard on SSH login. The installer wires it into `~/.profile`, where it is **sourced** (not executed) at interactive login and is a silent no-op when no session exists. Create a session with `cc [name]`, re-open the menu anytime with `d` (both aliases from the bashrc). Needs `dt`, `dtach`, `fzf`.
- **`dtach-router`** — session dashboard shown at shell startup. It ships in the deployed bashrc and is **sourced** (not executed) in every interactive shell, so it also fires in VS Code Remote-SSH terminals (non-login shells that skip `~/.profile`). Silent no-op when no session exists. Create a session with `cc [name]`, re-open the menu anytime with `d` (both aliases from the bashrc). Needs `dt`, `dtach`, `fzf`.
- **`claude-provider`** — switch Claude Code between Anthropic and OpenRouter.
OpenRouter mode reads the key from **`$OPENROUTER_API_KEY`** (never hardcoded). Export it from a private, untracked file, e.g. `~/.bashrc.local`:
```sh

View File

@ -117,5 +117,10 @@ PROMPT_COMMAND='set_prompt'
dtach_claude() { dtach -c "$HOME/.dtach/${1:-claude-$(date +%H%M%S)}" -e '^\' claude; }
alias cc='dtach_claude'
# Rappeler a la demande le menu de reprise (sinon il s'affiche seul au login SSH).
# Rappeler a la demande le menu de reprise.
alias d='source ~/.local/bin/dtach-router'
# Au demarrage d'un shell interactif, proposer de reprendre une session dtach existante
# (silencieux si aucune). Place ici plutot que dans ~/.profile car les terminaux VS Code
# Remote-SSH sont non-login et ne lisent pas ~/.profile ; ~/.bashrc, lui, est toujours lu.
case $- in *i*) [ -x "$HOME/.local/bin/dtach-router" ] && . "$HOME/.local/bin/dtach-router" ;; esac

View File

@ -113,16 +113,16 @@ install_disk_warning() {
/etc/profile.d/disk-usage-warning.sh
}
# Wire the dtach session-resume menu into ~/.profile. At interactive login we SOURCE
# dtach-router rather than execute it: the script hands control back with `return` and
# attaches to the host TTY, so running it as a command makes its interactivity guard
# fail and errors on /dev/tty whenever the login shell is non-interactive (bash -lc,
# cron, scp). Sourced, the guard works and it is a silent no-op when no session exists.
# Idempotent: strips any prior block first — the marker-delimited managed one AND the
# legacy execute-based one (DT=$(dt ls) ... fi) from earlier installs. User scope, no sudo.
wire_dtach_profile() {
# The dtach session-resume menu now ships in ~/.bashrc (deployed above): every interactive
# shell sources it, including VS Code Remote-SSH terminals, which are non-login and therefore
# never read ~/.profile. This strips any dtach block a previous install left in ~/.profile —
# the marker-delimited managed one AND the legacy execute-based one (DT=$(dt ls) ... fi) — so
# the menu does not also fire from there (a plain SSH login sources ~/.bashrc via ~/.profile,
# which would otherwise prompt twice). No-op when absent. User scope, no sudo.
unwire_dtach_profile() {
local profile="$HOME/.profile"
touch "$profile"
[ -f "$profile" ] || return 0
grep -qF 'dtach-router' "$profile" || return 0
awk '
$0 == "# >>> claude-dtach >>>" { drop = 1; next }
@ -131,14 +131,6 @@ wire_dtach_profile() {
$0 == "DT=$(dt ls)", $0 == "fi" { next }
{ print }
' "$profile" > "$profile.tmp" && mv "$profile.tmp" "$profile"
cat >> "$profile" << 'EOF'
# >>> claude-dtach >>>
# At interactive login, offer to resume a claude-in-dtach session (no-op if none).
case $- in *i*) [ -x "$HOME/.local/bin/dtach-router" ] && . "$HOME/.local/bin/dtach-router" ;; esac
# <<< claude-dtach <<<
EOF
}
# System packages: Debian/Ubuntu only. Skipped where apt-get is absent (e.g. macOS).
@ -224,8 +216,8 @@ cp "$SCRIPT_DIR"/bin/* "$HOME/.local/bin/"
chmod +x "$HOME"/.local/bin/dt "$HOME"/.local/bin/dtach-router "$HOME"/.local/bin/claude-provider
# Wire the dtach session-resume menu into ~/.profile (idempotent; migrates any prior block).
wire_dtach_profile
# Remove any stale dtach wiring from ~/.profile (the menu now ships in ~/.bashrc; see above).
unwire_dtach_profile
echo "Done. Restart your shell or run: source ~/.bashrc"
echo "If you use zsh, switch to bash to enjoy these settings =)"