diff options
| author | Danilo M. <danix@danix.xyz> | 2026-06-24 10:33:08 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-06-24 10:33:08 +0200 |
| commit | 40cd61ce7e0c4a8d35d0e0a505994f9f9b5e4d67 (patch) | |
| tree | 245aba80c137bea9e22662ea4a6363741d4f97fe | |
| parent | 9c301a8eac944083556b3dce297764b4fca7d49a (diff) | |
| download | gitctl-40cd61ce7e0c4a8d35d0e0a505994f9f9b5e4d67.tar.gz gitctl-40cd61ce7e0c4a8d35d0e0a505994f9f9b5e4d67.zip | |
feat: repo-name completion + label helper passthrough output
- Bash completion now completes repo NAMES for desc/delete/add-remote at
the name position, sourced from `gitctl repo list` (first field, header
row dropped). One ssh call, only at that position, so a stray Tab never
hits the network; the leading -y is handled.
- call_helper prefixes every passthrough line from the helper (and the
sync script it runs) with "server: " via _server_prefixed, so output
like "No changes." reads as remote output instead of interleaved local
noise. quiet=True callers (repo list) still get raw stdout to parse.
Client-only, no helper redeploy needed. Self-test pins _server_prefixed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| -rw-r--r-- | TODO.md | 28 | ||||
| -rw-r--r-- | completions/gitctl.bash | 14 | ||||
| -rwxr-xr-x | gitctl | 15 |
3 files changed, 41 insertions, 16 deletions
@@ -69,22 +69,22 @@ Done as part of items 1-3: both SKILL.md files updated (delete + `-y` limit), self-test coverage for every new helper verb, completion actions for `list` and `delete`, README and CLAUDE.md brought current. -## Remaining (not started) +## Remaining -These are the leftovers after the three features shipped. None block anything. +1. Repo-name bash completion -- DONE. `desc`, `delete`, and `add-remote` + complete repo NAMES at the name position from `gitctl repo list` (first + field, header dropped), one ssh call, only at that position so a stray Tab + never hits the network. `-y` before the subcommand is handled. -1. Repo-name bash completion. Now that `repo list` exists, complete repo NAMES - for `desc`, `delete`, and `add-remote` (the way `--section` is completed from - the server today). One ssh call (`gitctl repo list`, cut field 1), only when - completing the repo-name argument, so a stray Tab does not hit the network. - -2. Quiet the stray `No changes.` line. In `repo create` and `repo delete` the - `list-sections` / `add-section` helper passthrough prints a `No changes.` - line out of order, before the cgit-block message. Cosmetic only. Fix by - calling those helper steps with `quiet=True` (the flag already exists) and - printing a clean status, or by suppressing that specific echo. +2. Stray sync-script echo -- DONE. The `No changes.` / `Wrote N change(s).` + lines come from the server sync script, passed through over ssh. Rather than + hide them, `call_helper` now prefixes every passthrough line with `server: ` + (`_server_prefixed`), so they read clearly as remote output instead of + interleaved local noise. 3. FUTURE / YAGNI: `gc-trash --older-than <days>` helper verb to prune `TRASH_DIR` entries past a retention window, runnable from cron. The - timestamped trash layout already supports it; do not build until there is an - actual need to reclaim space. + timestamped trash layout already supports it. It would be the ONLY real + `rm -rf` in the tool, so it needs hard guards (realpath-under-TRASH_DIR + check, no symlink follow, validated timestamp parse) and a dry-run default. + Do not build until trash actually accumulates and space needs reclaiming. diff --git a/completions/gitctl.bash b/completions/gitctl.bash index 929346d..321a917 100644 --- a/completions/gitctl.bash +++ b/completions/gitctl.bash @@ -29,6 +29,20 @@ _gitctl() { return fi + # complete repo names for the name argument of desc/delete/add-remote (one + # ssh call). Only at the name position and not while typing a flag, so a + # stray Tab elsewhere never hits the network. `repo list` prints an aligned + # table (no color when not a TTY, as here); the name is the first field, and + # we drop the NAME header row. + if [[ $cword -eq 3 && $group == repo && $cur != -* \ + && ( $action == desc || $action == delete || $action == add-remote ) ]]; then + local repos + repos=$(gitctl repo list 2>/dev/null | awk 'NR>1 {print $1}') + local IFS=$'\n' + COMPREPLY=($(compgen -W "$repos" -- "$cur")) + return + fi + case $cword in 1) COMPREPLY=($(compgen -W "-y --yes sections sync repo" -- "$cur")) @@ -50,6 +50,14 @@ def helper_command(args): return " ".join(shlex.quote(a) for a in args) +def _server_prefixed(text): + """Prefix each non-empty line with 'server: ' so passthrough output from the + helper (and the sync script it runs) reads as remote output, not interleaved + local noise.""" + return "".join(("server: " + ln if ln.strip() else ln) + "\n" + for ln in text.splitlines()) + + def call_helper(cfg, args, check=True, quiet=False): """Run a helper verb. Returns CompletedProcess. ssh sends the verb string; the restricted key forces gitctl-helper, which reads SSH_ORIGINAL_COMMAND. @@ -57,9 +65,9 @@ def call_helper(cfg, args, check=True, quiet=False): cmd = ["ssh", cfg["helper_ssh_alias"], helper_command(args)] r = subprocess.run(cmd, text=True, capture_output=True) if r.stdout and not quiet: - sys.stdout.write(r.stdout) + sys.stdout.write(_server_prefixed(r.stdout)) if r.stderr: - sys.stderr.write(r.stderr) + sys.stderr.write(_server_prefixed(r.stderr)) if check and r.returncode != 0: sys.exit(f"error: helper '{args[0]}' failed (exit {r.returncode})") return r @@ -157,6 +165,9 @@ def _self_test(): # color on: public row green, private dim, header uncolored. tc = format_repo_table([("cad", "PUB", "G", "d"), ("s", "PRIV", "", "")], color=True) assert GREEN in tc and DIM in tc and tc.startswith("NAME") + # server passthrough: each non-empty line is prefixed, blanks left bare. + assert _server_prefixed("No changes.\n") == "server: No changes.\n" + assert _server_prefixed("a\n\nb") == "server: a\n\nserver: b\n" # global -y/--yes: defaults off, set by either spelling, lands on the namespace # regardless of subcommand (so cmd_create can read a.yes). pr = build_parser() |
