@sebastienrousseau/dotfiles 0.2.510 → 0.2.512

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 (64) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +2 -2
  3. package/docs/AI.md +2 -1
  4. package/docs/COPYRIGHT +1 -1
  5. package/docs/architecture/AI_COST_OPTIMIZATION.md +1 -0
  6. package/docs/archive/LEGACY_ROADMAP.md +4 -158
  7. package/docs/articles/.pages +6 -0
  8. package/docs/articles/2026-07-05-custom-mkdocs-material-dark-theme.md +216 -0
  9. package/docs/articles/2026-07-05-fish-startup-abbr.md +153 -0
  10. package/docs/articles/2026-07-05-master-to-main-rename-runbook.md +128 -0
  11. package/docs/articles/index.md +36 -0
  12. package/docs/manual/00-introduction.md +1 -1
  13. package/docs/manual/02-tutorials/02-add-wallpaper.md +6 -6
  14. package/docs/manual/02-tutorials/03-create-profile.md +1 -1
  15. package/docs/manual/02-tutorials/05-deploy-fleet.md +1 -1
  16. package/docs/manual/03-reference/02-config-files.md +1 -1
  17. package/docs/manual/command-index.md +1 -0
  18. package/docs/operations/ARCHITECTURE_ROADMAP.md +5 -143
  19. package/docs/operations/COVERAGE.md +27 -4
  20. package/docs/operations/ROADMAP.md +3 -159
  21. package/docs/operations/ROADMAP_2026.md +5 -663
  22. package/docs/operations/ROADMAP_V0_2_503.md +5 -129
  23. package/docs/reference/THEMES.md +1 -1
  24. package/docs/reference/TOOLS.md +1 -0
  25. package/docs/reference/UTILS.md +1 -0
  26. package/install.sh +5 -5
  27. package/package.json +1 -1
  28. package/scripts/diagnostics/aliases-manifest.sh +52 -7
  29. package/scripts/diagnostics/benchmark.sh +27 -1
  30. package/scripts/diagnostics/doctor.sh +18 -11
  31. package/scripts/diagnostics/health.sh +8 -3
  32. package/scripts/diagnostics/mcp-doctor.sh +3 -0
  33. package/scripts/diagnostics/secret-governance.sh +7 -1
  34. package/scripts/diagnostics/security-score.sh +14 -5
  35. package/scripts/diagnostics/verify_state.sh +15 -3
  36. package/scripts/diagnostics/version-locks.sh +10 -6
  37. package/scripts/dot/commands/agent.sh +5 -1
  38. package/scripts/dot/commands/ai.sh +37 -30
  39. package/scripts/dot/commands/aliases.sh +28 -4
  40. package/scripts/dot/commands/appearance.sh +16 -0
  41. package/scripts/dot/commands/core.sh +17 -0
  42. package/scripts/dot/commands/diagnostics.sh +19 -0
  43. package/scripts/dot/commands/fleet.sh +4 -1
  44. package/scripts/dot/commands/manual.sh +4 -4
  45. package/scripts/dot/commands/meta.sh +87 -12
  46. package/scripts/dot/commands/registry.sh +8 -2
  47. package/scripts/dot/commands/secrets.sh +17 -0
  48. package/scripts/dot/commands/security.sh +17 -0
  49. package/scripts/dot/commands/tools.sh +30 -2
  50. package/scripts/git-hooks/pre-commit-audit.sh +1 -1
  51. package/scripts/ops/ai-setup.sh +13 -6
  52. package/scripts/ops/bundle.sh +31 -5
  53. package/scripts/ops/chezmoi-apply.sh +5 -0
  54. package/scripts/ops/release.sh +14 -5
  55. package/scripts/qa/docs-coverage.sh +1 -1
  56. package/scripts/qa/reliability-audit.sh +2 -2
  57. package/scripts/secrets/age-init.sh +15 -4
  58. package/scripts/theme/apply-gnome-theme.sh +6 -2
  59. package/scripts/theme/extract-theme.py +97 -33
  60. package/scripts/theme/rebuild-themes.sh +240 -35
  61. package/scripts/theme/switch.sh +26 -16
  62. package/scripts/theme/wallpaper-sync.sh +95 -7
  63. package/scripts/tools/detect-collisions.py +19 -1
  64. package/scripts/version-sync.sh +87 -43
@@ -5,7 +5,7 @@
5
5
  ##
6
6
  ## Provides AI CLI status, setup, RAG query, and bridge commands.
7
7
  ## Wraps AI CLI tools with contextual patterns and system metadata.
8
- ## Usage: dot ai [delegate|cost|dashboard|proxy|local|status]|ai-setup|ai-query|cl|copilot|agy|kiro|sgpt|ollama|opencode|aider|autohand|vibe|qwen|zai
8
+ ## Usage: dot ai [delegate|cost|dashboard|proxy|local|status]|ai-setup|ai-query|cl|copilot|kimi|agy|kiro|sgpt|ollama|opencode|aider|autohand|vibe|qwen|zai
9
9
 
10
10
  set -euo pipefail
11
11
 
@@ -36,7 +36,7 @@ _show_ai_bridge_usage() {
36
36
  echo " dot ai <tool> --style <name> \"<prompt>\""
37
37
  echo " dot ai chat [tool] # interactive session"
38
38
  echo ""
39
- echo "Tools: cl codex copilot goose crush amp cursor-agent grok agy kiro sgpt ollama opencode aider autohand vibe qwen zai"
39
+ echo "Tools: cl codex copilot goose crush amp cursor-agent grok kimi agy kiro sgpt ollama opencode aider autohand vibe qwen zai"
40
40
  echo ""
41
41
  echo "Available styles:"
42
42
  # shellcheck disable=SC2012
@@ -69,21 +69,13 @@ _ai_refresh_status_cache() {
69
69
 
70
70
  local total=${#ai_entries[@]}
71
71
 
72
- # Cold-cache refresh runs `$bin --version` for every tool node-
73
- # based ones are slow to start, so the total can hit 15-30s. Show a
74
- # spinner so the user has feedback.
72
+ # Cold-cache refresh can take 15-30s across node-based tools; show progress.
75
73
  ui_spinner_start "Probing $total AI tools (cached for ${AI_STATUS_TTL}s)"
76
74
 
77
75
  local jobs="${DOTFILES_AI_PROBE_JOBS:-$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)}"
78
76
  local probe_dir
79
77
  probe_dir="$(mktemp -d)"
80
78
 
81
- # Probe via xargs -P. The probe logic lives INLINE in the bash -c
82
- # string rather than as an exported function, because `export -f`
83
- # doesn't always survive across bash invocations on every platform
84
- # (notably macOS bash 3.2 needs the BASH_FUNC_*() env-var format,
85
- # which subtly breaks for some shells in PATH). Inlining sidesteps
86
- # the inheritance question entirely.
87
79
  local i=0
88
80
  local entry
89
81
  local indexed=()
@@ -110,14 +102,7 @@ _ai_refresh_status_cache() {
110
102
  printf "%s\t0\t\n" "$bin" >"$out_dir/$i"
111
103
  fi
112
104
  '
113
- # NOTE: `-I{}` already implies one input line per invocation. Adding
114
- # `-n1` on top triggers a BSD-xargs quirk where the input line is
115
- # word-split on whitespace ("0|Agents (autonomous)|..." → multiple
116
- # entries). Use only `-I{}`.
117
- #
118
- # ALSO: feed null-delimited records (`-0`) so apostrophes and
119
- # other quote-like characters in the descriptions ("Block's coding
120
- # agent") don't trigger xargs's "unterminated quote" parser.
105
+ # Use only `-I{}` and null records for BSD xargs and quote-safe descriptions.
121
106
  printf '%s\0' "${indexed[@]}" |
122
107
  xargs -0 -I{} -P"$jobs" \
123
108
  bash -c "$probe_script" _ {} "$probe_dir" "$_TO" \
@@ -130,12 +115,7 @@ _ai_refresh_status_cache() {
130
115
  done
131
116
  rm -rf "$probe_dir"
132
117
 
133
- # Guard with `|| true` because ui_spinner_stop's last line evaluates
134
- # to rc=1 when stdout is a TTY (the `[[ ! -t 1 ]] && printf` short-
135
- # circuit). Under `set -euo pipefail` that rc would kill the script
136
- # right before we get to write the cache, leaving the user with a
137
- # silent broken cold-cache run. Defence in depth — the function
138
- # now also has an explicit `return 0` upstream.
118
+ # Guard against older ui_spinner_stop implementations returning 1 on a TTY.
139
119
  ui_spinner_stop || true
140
120
 
141
121
  mv "$tmp_file" "$AI_STATUS_CACHE_FILE"
@@ -145,10 +125,6 @@ _ai_get_cached_status() {
145
125
  cat "$AI_STATUS_CACHE_FILE"
146
126
  }
147
127
 
148
- # Look up field $2 (2=present 0/1, 3=version) for binary $1 from the
149
- # cached TSV. Replaces a bash-4 associative array for macOS bash 3.2.
150
- # (Removed _ai_status_field — cmd_ai_status now reads both fields in one awk.)
151
-
152
128
  # _ai_mise_pkg (binary -> mise package map) is defined in lib/dot/ai-install.sh.
153
129
 
154
130
  cmd_ai_status() {
@@ -164,6 +140,7 @@ cmd_ai_status() {
164
140
  "Agents (autonomous)|agent|Amp|amp|Sourcegraph's agentic coder"
165
141
  "Agents (autonomous)|agent|Cursor CLI|cursor-agent|Cursor's terminal agent"
166
142
  "Agents (autonomous)|agent|Grok Build|grok|xAI's terminal coding agent"
143
+ "Agents (autonomous)|agent|Kimi CLI|kimi|Moonshot AI's terminal coding agent"
167
144
  "Coding (interactive)|coding|Aider|aider|Git-aware AI pair programmer"
168
145
  "Coding (interactive)|coding|OpenCode|opencode|Open-source terminal coding agent"
169
146
  "Coding (interactive)|coding|Autohand Code|autohand|Autonomous multi-file coding agent"
@@ -278,6 +255,10 @@ cmd_ai_status() {
278
255
  install_grok_native "$name"
279
256
  continue
280
257
  ;;
258
+ kimi)
259
+ install_kimi_native "$name"
260
+ continue
261
+ ;;
281
262
  esac
282
263
  local pkg
283
264
  pkg=$(_ai_mise_pkg "$bin")
@@ -439,6 +420,11 @@ ${prompt}"
439
420
  ui_warn "$tool" "not installed"
440
421
  ui_info "Install" "curl -fsSL -o /tmp/agy-install.sh https://antigravity.google/cli/install.sh && bash /tmp/agy-install.sh"
441
422
  exit 1
423
+ elif [[ "$tool_bin" == "kimi" ]]; then
424
+ ui_warn "$tool" "not installed"
425
+ ui_info "Install" "dot ai install kimi"
426
+ ui_info "PATH" "Kimi Code installs to ~/.kimi-code/bin; restart your shell after install"
427
+ exit 1
442
428
  else
443
429
  ui_err "$tool" "not installed and mise not available"
444
430
  exit 1
@@ -478,6 +464,27 @@ ${prompt}"
478
464
 
479
465
  # Dispatch — flat, verb-first surface (see docs/AI.md).
480
466
  case "${1:-}" in
467
+ --help | -h | help)
468
+ cat <<'EOF'
469
+ Usage: ai.sh <command> [args...]
470
+
471
+ Commands:
472
+ ai, ai-setup, ai-query, cl, claude, codex, copilot, goose, crush, amp,
473
+ cursor-agent, grok, kimi, agy, kiro, sgpt, ollama, opencode, aider, autohand,
474
+ vibe, qwen, zai
475
+ EOF
476
+ ;;
477
+ "")
478
+ cat <<'EOF'
479
+ Usage: ai.sh <command> [args...]
480
+
481
+ Commands:
482
+ ai, ai-setup, ai-query, cl, claude, codex, copilot, goose, crush, amp,
483
+ cursor-agent, grok, kimi, agy, kiro, sgpt, ollama, opencode, aider, autohand,
484
+ vibe, qwen, zai
485
+ EOF
486
+ exit 1
487
+ ;;
481
488
  ai)
482
489
  shift
483
490
  case "${1:-}" in
@@ -565,7 +572,7 @@ case "${1:-}" in
565
572
  shift
566
573
  cmd_ai_query "$@"
567
574
  ;;
568
- cl | claude | codex | copilot | goose | crush | amp | cursor-agent | grok | agy | kiro | sgpt | ollama | opencode | aider | autohand | vibe | qwen | zai)
575
+ cl | claude | codex | copilot | goose | crush | amp | cursor-agent | grok | kimi | agy | kiro | sgpt | ollama | opencode | aider | autohand | vibe | qwen | zai)
569
576
  _ai_deprecated "dot ai $1"
570
577
  tool="$1"
571
578
  shift
@@ -133,12 +133,36 @@ cmd_aliases() {
133
133
  rm -f "$tmp_aliases"
134
134
  ;;
135
135
  cheatsheet)
136
+ # Destination defaults to the checkout's docs/ but can be
137
+ # redirected with `--output PATH` (or `-` for stdout). The
138
+ # hardcoded path made this the one subcommand that could not be
139
+ # exercised without writing into the working tree:
140
+ # require_source_dir() resolves from the location of the lib that
141
+ # was sourced, so it always pointed at the real repo no matter
142
+ # how the sandbox redirected $HOME/.dotfiles.
136
143
  ui_header "Alias Cheatsheet"
137
- local src_dir out
144
+ local src_dir out=""
138
145
  src_dir="$(require_source_dir)"
139
- out="$src_dir/docs/ALIASES_CHEATSHEET.md"
140
- bash "$src_dir/scripts/diagnostics/aliases-cheatsheet.sh" >"$out"
141
- ui_ok "Generated" "$out"
146
+ while (($#)); do
147
+ case "$1" in
148
+ --output | -o)
149
+ out="${2:-}"
150
+ [[ -n "$out" ]] || die "Usage: dot aliases cheatsheet [--output PATH|-]"
151
+ shift 2
152
+ ;;
153
+ *)
154
+ die "Unknown option for cheatsheet: $1"
155
+ ;;
156
+ esac
157
+ done
158
+ out="${out:-$src_dir/docs/ALIASES_CHEATSHEET.md}"
159
+ if [[ "$out" == "-" ]]; then
160
+ bash "$src_dir/scripts/diagnostics/aliases-cheatsheet.sh"
161
+ else
162
+ mkdir -p "$(dirname "$out")"
163
+ bash "$src_dir/scripts/diagnostics/aliases-cheatsheet.sh" >"$out"
164
+ ui_ok "Generated" "$out"
165
+ fi
142
166
  ;;
143
167
  tiers)
144
168
  local profile ecosystems security_mode dangerous buckets
@@ -12,6 +12,15 @@ source "$SCRIPT_DIR/../../../lib/dot/utils.sh"
12
12
 
13
13
  dot_ui_command_banner "Appearance" "${1:-}"
14
14
 
15
+ usage() {
16
+ cat <<'EOF'
17
+ Usage: appearance.sh <command> [args...]
18
+
19
+ Commands:
20
+ theme, wallpaper, fonts, tune
21
+ EOF
22
+ }
23
+
15
24
  cmd_theme() {
16
25
  run_script "scripts/theme/switch.sh" "Theme switcher" "$@"
17
26
  }
@@ -71,6 +80,13 @@ cmd_tune() {
71
80
 
72
81
  # Dispatch
73
82
  case "${1:-}" in
83
+ --help | -h | help)
84
+ usage
85
+ ;;
86
+ "")
87
+ usage
88
+ exit 1
89
+ ;;
74
90
  theme)
75
91
  shift
76
92
  cmd_theme "$@"
@@ -34,6 +34,16 @@ if [[ "${1:-}" != "cd" ]]; then
34
34
  dot_ui_command_banner "Core" "${1:-}"
35
35
  fi
36
36
 
37
+ usage() {
38
+ cat <<'EOF'
39
+ Usage: core.sh <command> [args...]
40
+
41
+ Commands:
42
+ apply, sync, update, add, diff, status, remove, cd, edit, commit,
43
+ clean-cache, uninstall
44
+ EOF
45
+ }
46
+
37
47
  cmd_apply() {
38
48
  local src_dir
39
49
  src_dir="$(resolve_source_dir)"
@@ -145,6 +155,13 @@ cmd_clean_cache() {
145
155
 
146
156
  # Dispatch
147
157
  case "${1:-}" in
158
+ --help | -h | help)
159
+ usage
160
+ ;;
161
+ "")
162
+ usage
163
+ exit 1
164
+ ;;
148
165
  apply)
149
166
  shift
150
167
  cmd_apply "$@"
@@ -13,6 +13,18 @@ source "$SCRIPT_DIR/../../../lib/dot/utils.sh"
13
13
 
14
14
  dot_ui_command_banner "Diagnostics" "${1:-}" "$@"
15
15
 
16
+ usage() {
17
+ cat <<'EOF'
18
+ Usage: diagnostics.sh <command> [args...]
19
+
20
+ Commands:
21
+ doctor, heal, health, security-score, scorecard, perf, conflicts, locks,
22
+ snapshot, attest, rollback, drift, history, benchmark, verify, restore,
23
+ load-bench, load-bench-pty, chaos, teleport, secret-audit, bundle, metrics,
24
+ smoke-test, intelligence
25
+ EOF
26
+ }
27
+
16
28
  cmd_doctor() {
17
29
  local src_dir
18
30
  src_dir="$(resolve_source_dir)"
@@ -139,6 +151,13 @@ cmd_metrics() {
139
151
 
140
152
  # Dispatch
141
153
  case "${1:-}" in
154
+ --help | -h | help)
155
+ usage
156
+ ;;
157
+ "")
158
+ usage
159
+ exit 1
160
+ ;;
142
161
  doctor)
143
162
  shift
144
163
  cmd_doctor "$@"
@@ -102,7 +102,10 @@ cmd_fleet_status() {
102
102
  local last_apply=""
103
103
  local state_log="${XDG_STATE_HOME:-$HOME/.local/state}/dotfiles/dot.log"
104
104
  if [[ -f "$state_log" ]]; then
105
- last_apply="$(grep 'apply' "$state_log" 2>/dev/null | tail -1 | sed -n 's/^\[\([^]]*\)\].*/\1/p')"
105
+ # `set -euo pipefail` at the top of this script kills the whole
106
+ # command when grep matches nothing (rc=1). Wrap the pipeline so
107
+ # `last_apply` cleanly becomes empty and the UI still renders.
108
+ last_apply="$(grep 'apply' "$state_log" 2>/dev/null | tail -1 | sed -n 's/^\[\([^]]*\)\].*/\1/p' || true)"
106
109
  fi
107
110
 
108
111
  if [[ "$json_mode" -eq 1 ]]; then
@@ -67,7 +67,7 @@ resolve_source() {
67
67
  echo "$src_dir/$fname"
68
68
  return 0
69
69
  fi
70
- ui_err "manual" "local build not found at $src_dir/$fname — run bash tools/docs/build-manual.sh"
70
+ ui_err "manual" "local build not found at $src_dir/$fname — run bash tools/docs/build-manual.sh" >&2
71
71
  return 1
72
72
  fi
73
73
 
@@ -76,7 +76,7 @@ resolve_source() {
76
76
  echo "$OFFLINE_DIR/$fname"
77
77
  return 0
78
78
  fi
79
- ui_err "manual" "offline copy not found at $OFFLINE_DIR/$fname"
79
+ ui_err "manual" "offline copy not found at $OFFLINE_DIR/$fname" >&2
80
80
  return 1
81
81
  fi
82
82
 
@@ -84,9 +84,9 @@ resolve_source() {
84
84
  if [[ ! -f "$cache_path" ]] || [[ $(find "$cache_path" -mtime +7 -print 2>/dev/null | wc -l) -gt 0 ]]; then
85
85
  mkdir -p "$(dirname "$cache_path")"
86
86
  local url="$MANUAL_URL/$fname"
87
- ui_info "manual" "fetching $url"
87
+ ui_info "manual" "fetching $url" >&2
88
88
  if ! curl -fsSL "$url" -o "$cache_path"; then
89
- ui_err "manual" "failed to download $url"
89
+ ui_err "manual" "failed to download $url" >&2
90
90
  return 1
91
91
  fi
92
92
  fi
@@ -30,30 +30,88 @@ meta_banner_section() {
30
30
 
31
31
  dot_ui_command_banner "$(meta_banner_section "${1:-}")" "${1:-}" "$@"
32
32
 
33
+ # Last meaningful line of a captured log, used as a step's `ok` detail.
34
+ # Collapses carriage-return progress (git/nvim spam \r), strips ANSI,
35
+ # takes the last non-empty line, and clips it so the rendered step stays
36
+ # on one line.
37
+ _upgrade_last_line() {
38
+ tr '\r' '\n' <"$1" 2>/dev/null |
39
+ sed -E 's/\x1b\[[0-9;?]*[a-zA-Z]//g' |
40
+ awk 'NF{last=$0} END{print last}' |
41
+ cut -c1-56
42
+ }
43
+
33
44
  cmd_upgrade() {
34
45
  local src_dir
35
46
  src_dir="$(require_source_dir)"
36
47
 
48
+ # Per-step logs. Each phase's stdout+stderr is captured here rather
49
+ # than streamed to the terminal: the git/nvim/chezmoi output is noisy
50
+ # (carriage-return progress bars, remote counters) and, in rich mode,
51
+ # writing it to the terminal corrupts the dot-ui renderer that owns
52
+ # the screen. Failing steps get their tail dumped after the run.
53
+ local log_dir
54
+ log_dir="$(mktemp -d "${TMPDIR:-/tmp}/dot-upgrade.XXXXXX")"
55
+ local fail_labels=() fail_logs=()
56
+
57
+ # _upgrade_step <id> <label> <running-detail> -- cmd [args...]
58
+ # Renders one tracked step: spinner while it runs, then ok (with the
59
+ # log's last line) or fail. Never aborts the run — a failed phase is
60
+ # recorded and surfaced at the end, matching the previous `|| true`
61
+ # behaviour but without the raw flood.
62
+ _upgrade_step() {
63
+ local id="$1" label="$2" running="$3"
64
+ shift 3
65
+ [[ "${1:-}" == "--" ]] && shift
66
+ local log="$log_dir/$id.log" rc=0
67
+ ui_step "$id" "$label" run "$running"
68
+ "$@" >"$log" 2>&1 || rc=$?
69
+ if [[ "$rc" -eq 0 ]]; then
70
+ ui_step "$id" "" ok "$(_upgrade_last_line "$log")"
71
+ else
72
+ ui_step "$id" "" fail "exited $rc"
73
+ fail_labels+=("$label")
74
+ fail_logs+=("$log")
75
+ fi
76
+ return 0
77
+ }
78
+
79
+ ui_steps_begin "Upgrade" ""
80
+
37
81
  if [ -f "$src_dir/nix/flake.nix" ] && has_command nix; then
38
- ui_info "Updating Nix flake"
39
- (cd "$src_dir" && nix flake update) || true
40
- ui_info "Running Nix garbage collection"
41
- nix-collect-garbage -d || true
82
+ _upgrade_step nix-flake "Nix flake" "updating…" -- \
83
+ sh -c 'cd "$1" && nix flake update' _ "$src_dir"
84
+ _upgrade_step nix-gc "Nix GC" "collecting…" -- nix-collect-garbage -d
42
85
  fi
43
86
 
44
- ui_info "Updating dotfiles"
45
- chezmoi update || true
87
+ _upgrade_step dotfiles "Dotfiles" "chezmoi update…" -- chezmoi update
46
88
 
47
89
  if has_command nvim; then
48
- ui_info "Updating Neovim plugins"
49
- nvim --headless "+Lazy! sync" +qa || true
90
+ _upgrade_step nvim "Neovim plugins" "Lazy sync…" -- \
91
+ nvim --headless "+Lazy! sync" +qa
50
92
  fi
51
93
 
52
- if [ "${DOTFILES_FONTS:-}" = "1" ]; then
53
- if [ -f "$src_dir/scripts/fonts/install-nerd-fonts.sh" ]; then
54
- ui_info "Installing Nerd Fonts"
94
+ if [ "${DOTFILES_FONTS:-}" = "1" ] &&
95
+ [ -f "$src_dir/scripts/fonts/install-nerd-fonts.sh" ]; then
96
+ _upgrade_step fonts "Nerd Fonts" "installing…" -- \
55
97
  sh "$src_dir/scripts/fonts/install-nerd-fonts.sh"
56
- fi
98
+ fi
99
+
100
+ local n=${#fail_labels[@]}
101
+ if [[ "$n" -eq 0 ]]; then
102
+ ui_steps_end "toolchains, plugins, and dotfiles up to date"
103
+ rm -rf "$log_dir"
104
+ else
105
+ ui_steps_end "$n step(s) failed"
106
+ # Surfaced after ui_steps_end so the rich renderer has torn down —
107
+ # printing mid-render would garble the display.
108
+ local i=0
109
+ while ((i < n)); do
110
+ ui_err "${fail_labels[$i]}" "log tail:"
111
+ tail -n 15 "${fail_logs[$i]}" | sed 's/^/ /'
112
+ ((i++)) || true
113
+ done
114
+ ui_info "Logs" "$log_dir"
57
115
  fi
58
116
  }
59
117
 
@@ -251,6 +309,23 @@ case "${1:-}" in
251
309
  shift
252
310
  cmd_mode "$@"
253
311
  ;;
312
+ --help | -h | help)
313
+ cat <<'EOF'
314
+ Usage: meta.sh <command> [args...]
315
+
316
+ Commands:
317
+ cache-refresh, prewarm, docs, learn, keys, sandbox, mcp, mode, agent
318
+ EOF
319
+ ;;
320
+ "")
321
+ cat <<'EOF'
322
+ Usage: meta.sh <command> [args...]
323
+
324
+ Commands:
325
+ cache-refresh, prewarm, docs, learn, keys, sandbox, mcp, mode, agent
326
+ EOF
327
+ exit 1
328
+ ;;
254
329
  *)
255
330
  echo "Unknown meta command: ${1:-}" >&2
256
331
  exit 1
@@ -89,7 +89,7 @@ _registry_fetch() {
89
89
  local now mtime
90
90
  now="$(date +%s)"
91
91
  if [[ -s "$cache_file" ]]; then
92
- if mtime="$(stat -f %m "$cache_file" 2>/dev/null || stat -c %Y "$cache_file" 2>/dev/null)"; then
92
+ if mtime="$(stat -c %Y "$cache_file" 2>/dev/null || stat -f %m "$cache_file" 2>/dev/null)"; then
93
93
  if ((now - mtime < 21600)); then
94
94
  printf '%s\n' "$cache_file"
95
95
  return 0
@@ -102,7 +102,13 @@ _registry_fetch() {
102
102
  fi
103
103
  local tmp
104
104
  tmp="$(mktemp "${cache_file}.XXXXXX")"
105
- if ! curl -fsSL --max-time 15 -o "$tmp" "$url"; then
105
+ # `-fsSL -o file` should be silent, but with some upstreams (e.g.
106
+ # GitHub Pages) curl still writes a stray newline to stdout. That
107
+ # newline leaks into the caller's `$(_registry_fetch)` and later
108
+ # into `jq FILE` as a two-argument invocation
109
+ # (`jq \n /path/to/file`), producing a confusing
110
+ # "Could not open file" error. Silence stdout explicitly.
111
+ if ! curl -fsSL --max-time 15 -o "$tmp" "$url" >/dev/null; then
106
112
  rm -f "$tmp"
107
113
  if [[ -s "$cache_file" ]]; then
108
114
  ui_warn "registry" "fetch failed; using stale cache at $cache_file"
@@ -231,6 +231,23 @@ cmd_secrets_load() {
231
231
  # Dispatch
232
232
  top="${1:-}"
233
233
  case "$top" in
234
+ --help | -h | help)
235
+ cat <<'EOF'
236
+ Usage: secrets.sh <command> [args...]
237
+
238
+ Commands:
239
+ secrets-init, secrets, secrets-create, ssh-key, ssh-cert, env
240
+ EOF
241
+ ;;
242
+ "")
243
+ cat <<'EOF'
244
+ Usage: secrets.sh <command> [args...]
245
+
246
+ Commands:
247
+ secrets-init, secrets, secrets-create, ssh-key, ssh-cert, env
248
+ EOF
249
+ exit 1
250
+ ;;
234
251
  secrets-init)
235
252
  shift
236
253
  cmd_secrets_init "$@"
@@ -12,6 +12,16 @@ source "$SCRIPT_DIR/../../../lib/dot/utils.sh"
12
12
 
13
13
  dot_ui_command_banner "Security" "${1:-}"
14
14
 
15
+ usage() {
16
+ cat <<'EOF'
17
+ Usage: security.sh <command> [args...]
18
+
19
+ Commands:
20
+ backup, encrypt-check, firewall, telemetry, dns-doh, lock-screen,
21
+ usb-safety, policy
22
+ EOF
23
+ }
24
+
15
25
  cmd_backup() {
16
26
  run_script "scripts/security/backup.sh" "Backup script" "$@"
17
27
  }
@@ -46,6 +56,13 @@ cmd_policy() {
46
56
 
47
57
  # Dispatch
48
58
  case "${1:-}" in
59
+ --help | -h | help)
60
+ usage
61
+ ;;
62
+ "")
63
+ usage
64
+ exit 1
65
+ ;;
49
66
  backup)
50
67
  shift
51
68
  cmd_backup "$@"
@@ -189,8 +189,17 @@ show_language_package_managers() {
189
189
  echo " pip: $(pip3 --version | cut -d' ' -f2)"
190
190
  fi
191
191
  if has_command pipx; then
192
- local pipx_installed
193
- pipx_installed=$(pipx list --short 2>/dev/null | wc -l | tr -d ' ')
192
+ local pipx_installed pipx_list_out
193
+ # `pipx list --short` returns non-zero when any installed package
194
+ # has a broken interpreter (common). Under `set -euo pipefail` a
195
+ # naive `local x=$(pipx …)` cascades and kills `dot packages`
196
+ # mid-output. Capture the pipeline separately so we can fall back
197
+ # cleanly without stray "N/A" lines from pipe-with-|| tricks.
198
+ if pipx_list_out="$(pipx list --short 2>/dev/null)"; then
199
+ pipx_installed="$(printf '%s' "$pipx_list_out" | wc -l | tr -d ' ')"
200
+ else
201
+ pipx_installed="N/A"
202
+ fi
194
203
  echo " pipx: $(pipx --version)"
195
204
  echo " Installed: $pipx_installed"
196
205
  fi
@@ -481,6 +490,25 @@ cmd_profile() {
481
490
 
482
491
  # Dispatch — aliases and alias-check are defined in aliases.sh (sourced above)
483
492
  case "${1:-}" in
493
+ --help | -h | help)
494
+ cat <<'EOF'
495
+ Usage: tools.sh <command> [args...]
496
+
497
+ Commands:
498
+ tools, alias-check, new, packages, log-rotate, setup, aliases, env,
499
+ profile, lint
500
+ EOF
501
+ ;;
502
+ "")
503
+ cat <<'EOF'
504
+ Usage: tools.sh <command> [args...]
505
+
506
+ Commands:
507
+ tools, alias-check, new, packages, log-rotate, setup, aliases, env,
508
+ profile, lint
509
+ EOF
510
+ exit 1
511
+ ;;
484
512
  tools)
485
513
  shift
486
514
  cmd_tools "$@"
@@ -141,6 +141,6 @@ if [[ $FAILED -eq 1 ]]; then
141
141
  printf '%b\\n' " (Use --no-verify to bypass if absolutely necessary)"
142
142
  exit 1
143
143
  else
144
- printf '%b\n' "${GREEN}${BOLD}✅ Audit passed.${NC} v0.2.510 standards maintained."
144
+ printf '%b\n' "${GREEN}${BOLD}✅ Audit passed.${NC} v0.2.512 standards maintained."
145
145
  exit 0
146
146
  fi
@@ -20,6 +20,10 @@ setup_tool() {
20
20
 
21
21
  ui_section "Setting up $name"
22
22
  if command -v "$cmd" >/dev/null 2>&1; then
23
+ if [[ ! -t 0 && "${auth_argv[*]}" != *"--version"* ]]; then
24
+ ui_info "$name" "non-interactive shell; skipping login. Run '${auth_argv[*]}' in a terminal."
25
+ return 0
26
+ fi
23
27
  ui_info "Tool found, initiating authentication..."
24
28
  "${auth_argv[@]}" || ui_warn "$name" "Setup/Auth skipped or failed."
25
29
  else
@@ -43,21 +47,24 @@ setup_tool "Copilot CLI" "copilot" copilot --version
43
47
  setup_tool "Goose" "goose" goose --version
44
48
 
45
49
  # 6. Kiro
46
- setup_tool "Kiro CLI" "kiro-cli" kiro-cli auth login
50
+ setup_tool "Kiro CLI" "kiro-cli" kiro-cli login
47
51
 
48
- # 7. Aider
52
+ # 7. Kimi
53
+ setup_tool "Kimi CLI" "kimi" kimi --version
54
+
55
+ # 8. Aider
49
56
  setup_tool "Aider" "aider" aider --version
50
57
 
51
- # 7. Autohand Code
58
+ # 9. Autohand Code
52
59
  setup_tool "Autohand Code" "autohand" autohand --version
53
60
 
54
- # 8. Mistral Vibe
61
+ # 10. Mistral Vibe
55
62
  setup_tool "Mistral Vibe" "vibe" vibe --version
56
63
 
57
- # 9. Qwen Code
64
+ # 11. Qwen Code
58
65
  setup_tool "Qwen Code" "qwen" qwen --version
59
66
 
60
- # 10. ZAI
67
+ # 12. ZAI
61
68
  setup_tool "ZAI" "zai" zai --version
62
69
 
63
70
  ui_header "AI Setup Complete"
@@ -15,12 +15,38 @@ source "$SCRIPT_DIR/../lib/dot/ui.sh"
15
15
  source "$SCRIPT_DIR/../lib/dot/log.sh"
16
16
  export DOT_COMMAND="bundle"
17
17
 
18
+ usage() {
19
+ cat <<'EOF'
20
+ Usage: bundle.sh [output-dir]
21
+
22
+ Create an offline dotfiles bundle as a .tar.zst archive.
23
+ EOF
24
+ }
25
+
26
+ case "${1:-}" in
27
+ --help | -h)
28
+ usage
29
+ exit 0
30
+ ;;
31
+ -*)
32
+ printf 'Unknown option: %s\n' "$1" >&2
33
+ usage >&2
34
+ exit 2
35
+ ;;
36
+ esac
37
+
18
38
  # Concurrency guard
19
- LOCK_FILE="${XDG_RUNTIME_DIR:-/tmp}/dotfiles-bundle.lock"
20
- exec 9>"$LOCK_FILE"
21
- if ! flock -n 9; then
22
- ui_warn "Already running" "Another bundle instance is active"
23
- exit 0
39
+ LOCK_DIR="${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}"
40
+ if [[ ! -d "$LOCK_DIR" || ! -w "$LOCK_DIR" ]]; then
41
+ LOCK_DIR="${TMPDIR:-/tmp}"
42
+ fi
43
+ LOCK_FILE="$LOCK_DIR/dotfiles-bundle.lock"
44
+ if command -v flock >/dev/null 2>&1; then
45
+ exec 9>"$LOCK_FILE"
46
+ if ! flock -n 9; then
47
+ ui_warn "Already running" "Another bundle instance is active"
48
+ exit 0
49
+ fi
24
50
  fi
25
51
 
26
52
  OUTPUT_DIR="${1:-$HOME/Downloads}"