@sebastienrousseau/dotfiles 0.2.511 → 0.2.513

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 (74) hide show
  1. package/CHANGELOG.md +90 -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/manual/00-introduction.md +1 -1
  8. package/docs/manual/01-concepts/02-trust-model.md +2 -1
  9. package/docs/manual/02-tutorials/02-add-wallpaper.md +6 -6
  10. package/docs/manual/02-tutorials/03-create-profile.md +1 -1
  11. package/docs/manual/02-tutorials/05-deploy-fleet.md +1 -1
  12. package/docs/manual/03-reference/02-config-files.md +1 -1
  13. package/docs/manual/03-reference/03-environment.md +1 -1
  14. package/docs/manual/04-cookbook/03-faq.md +1 -1
  15. package/docs/manual/05-appendices/B-security-checklist.md +2 -1
  16. package/docs/manual/command-index.md +1 -0
  17. package/docs/operations/ARCHITECTURE_ROADMAP.md +5 -143
  18. package/docs/operations/COVERAGE.md +30 -8
  19. package/docs/operations/REGISTRY.md +20 -19
  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/POWERSHELL_PARITY.md +28 -27
  24. package/docs/reference/THEMES.md +1 -1
  25. package/docs/reference/TOOLS.md +1 -0
  26. package/docs/reference/UTILS.md +1 -0
  27. package/docs/schema/dot-registry-v1.json +33 -0
  28. package/docs/security/SCORECARD.md +1 -1
  29. package/install.sh +20 -11
  30. package/package.json +1 -1
  31. package/scripts/diagnostics/a2a-conformance.sh +0 -0
  32. package/scripts/diagnostics/aliases-manifest.sh +33 -6
  33. package/scripts/diagnostics/benchmark.sh +27 -1
  34. package/scripts/diagnostics/doctor.sh +18 -11
  35. package/scripts/diagnostics/health.sh +12 -4
  36. package/scripts/diagnostics/mcp-doctor.sh +3 -0
  37. package/scripts/diagnostics/secret-governance.sh +7 -1
  38. package/scripts/diagnostics/security-score.sh +14 -5
  39. package/scripts/diagnostics/verify_state.sh +9 -1
  40. package/scripts/diagnostics/workstation-attestation.sh +0 -0
  41. package/scripts/dot/commands/ai.sh +40 -31
  42. package/scripts/dot/commands/aliases.sh +28 -4
  43. package/scripts/dot/commands/appearance.sh +16 -0
  44. package/scripts/dot/commands/core.sh +17 -0
  45. package/scripts/dot/commands/diagnostics.sh +19 -0
  46. package/scripts/dot/commands/fleet.sh +1 -1
  47. package/scripts/dot/commands/manual.sh +4 -4
  48. package/scripts/dot/commands/meta.sh +87 -12
  49. package/scripts/dot/commands/registry.sh +164 -15
  50. package/scripts/dot/commands/secrets.sh +17 -0
  51. package/scripts/dot/commands/security.sh +17 -0
  52. package/scripts/dot/commands/tools.sh +19 -0
  53. package/scripts/dot/powershell/Dot.psm1 +146 -26
  54. package/scripts/fonts/install-nerd-fonts.sh +16 -10
  55. package/scripts/git-hooks/pre-commit-audit.sh +1 -1
  56. package/scripts/ops/ai-setup.sh +13 -6
  57. package/scripts/ops/bundle.sh +31 -5
  58. package/scripts/ops/chezmoi-apply.sh +5 -0
  59. package/scripts/ops/heal-tools.sh +39 -111
  60. package/scripts/ops/post-apply-repair.sh +0 -0
  61. package/scripts/ops/release.sh +14 -5
  62. package/scripts/qa/docs-coverage.sh +1 -1
  63. package/scripts/qa/reliability-audit.sh +4 -4
  64. package/scripts/qa/scorecard-snapshot.sh +3 -3
  65. package/scripts/qa/validate-examples.sh +0 -0
  66. package/scripts/qa/wsl-contract.sh +0 -0
  67. package/scripts/secrets/age-init.sh +15 -4
  68. package/scripts/theme/apply-gnome-theme.sh +6 -2
  69. package/scripts/theme/extract-theme.py +97 -33
  70. package/scripts/theme/rebuild-themes.sh +240 -35
  71. package/scripts/theme/switch.sh +26 -16
  72. package/scripts/theme/wallpaper-sync.sh +95 -7
  73. package/scripts/tools/detect-collisions.py +0 -0
  74. package/scripts/version-sync.sh +108 -44
@@ -198,6 +198,11 @@ check_dotfiles_core() {
198
198
  check_shell_env() {
199
199
  check_section "Shell Environment"
200
200
  local current_shell="${SHELL##*/}"
201
+ local default_shell="fish"
202
+ if [[ -f "$SCRIPT_DIR/../../defaults/.chezmoidata.toml" ]]; then
203
+ default_shell="$(awk -F= '/^[[:space:]]*default_shell[[:space:]]*=/{split($2, value, "#"); gsub(/[ "]/, "", value[1]); print value[1]; exit}' "$SCRIPT_DIR/../../defaults/.chezmoidata.toml")"
204
+ default_shell="${default_shell:-fish}"
205
+ fi
201
206
 
202
207
  if has_command zsh; then
203
208
  check "Zsh installed" "pass"
@@ -210,12 +215,12 @@ check_shell_env() {
210
215
  check "Zsh installed" "fail"
211
216
  fi
212
217
 
213
- if [[ -d "${ZINIT_HOME:-$HOME/.local/share/zinit}" ]]; then
218
+ if [[ -f "${ZINIT_HOME:-$HOME/.local/share/zinit/zinit.git}/zinit.zsh" ]] || [[ -d "${ZINIT_HOME:-$HOME/.local/share/zinit}" ]]; then
214
219
  check "Zinit plugin manager" "pass"
215
- elif [[ "$current_shell" == "zsh" ]]; then
220
+ elif [[ "$current_shell" == "zsh" && "$default_shell" == "zsh" ]]; then
216
221
  check "Zinit plugin manager" "warn" "Not found"
217
222
  else
218
- check "Zinit plugin manager" "pass" "Not required for $current_shell"
223
+ check "Zinit plugin manager" "pass" "Not required for $default_shell"
219
224
  fi
220
225
 
221
226
  if has_command starship; then
@@ -381,7 +386,10 @@ check_performance() {
381
386
 
382
387
  if has_command zsh; then
383
388
  local startup_time
384
- startup_time=$({ time zsh -i -c exit; } 2>&1 | grep real | awk '{print $2}' | sed 's/[ms]//g')
389
+ # Startup timing is diagnostic only. A user's interactive configuration
390
+ # may exit non-zero or omit a `real` row, which must not abort health under
391
+ # `set -euo pipefail` before the summary is printed.
392
+ startup_time=$({ time zsh -i -c exit; } 2>&1 | awk '/real/ { print $2; exit }' | sed 's/[ms]//g' || true)
385
393
  if [[ -n "$startup_time" ]]; then
386
394
  check "Shell startup time" "pass"
387
395
  else
@@ -77,12 +77,14 @@ log_success() {
77
77
  if [[ "$JSON_MODE" -ne 1 ]]; then
78
78
  ui_ok "$1" "${2:-}"
79
79
  fi
80
+ return 0
80
81
  }
81
82
  log_fail() {
82
83
  if [[ "$JSON_MODE" -ne 1 ]]; then
83
84
  ui_err "$1" "${2:-}"
84
85
  fi
85
86
  Errors=$((Errors + 1))
87
+ return 0
86
88
  }
87
89
  log_warn() {
88
90
  if [[ "$JSON_MODE" -ne 1 ]]; then
@@ -91,6 +93,7 @@ log_warn() {
91
93
  Warnings=$((Warnings + 1))
92
94
  # In strict mode, warnings become errors
93
95
  [[ "$STRICT_MODE" -eq 1 ]] && Errors=$((Errors + 1))
96
+ return 0
94
97
  }
95
98
 
96
99
  MCP_CONFIG="${MCP_CONFIG:-$HOME/.config/claude/mcp_servers.json}"
@@ -13,7 +13,13 @@ STRICT="${DOTFILES_ALIAS_STRICT_MODE:-${DOTFILES_SECRETS_STRICT_MODE:-0}}"
13
13
 
14
14
  cd "$REPO_ROOT"
15
15
 
16
- mapfile -t staged_files < <(git diff --cached --name-only --diff-filter=ACM || true)
16
+ # Portable read `mapfile` is bash 4 only, and this runs from the
17
+ # pre-commit hook on macOS where /bin/bash is 3.2. Without this the
18
+ # hook exits 127 and the secret scan is skipped entirely.
19
+ staged_files=()
20
+ while IFS= read -r _line; do
21
+ [[ -n "$_line" ]] && staged_files+=("$_line")
22
+ done < <(git diff --cached --name-only --diff-filter=ACM || true)
17
23
  if [[ "${#staged_files[@]}" -eq 0 ]]; then
18
24
  echo "Secret governance: no staged files."
19
25
  exit 0
@@ -171,13 +171,20 @@ check_encryption() {
171
171
  add_points "encryption" 0 5 "SOPS secrets manager"
172
172
  fi
173
173
 
174
- # GPG available
174
+ # Signing key material: this repo uses SSH ED25519 signing by default, with
175
+ # GPG still accepted for users who prefer it.
176
+ local signing_format signing_key
177
+ signing_format="$(git config --global gpg.format 2>/dev/null || true)"
178
+ signing_key="$(git config --global user.signingkey 2>/dev/null || true)"
179
+ signing_key="${signing_key/#\~/$HOME}"
175
180
  if command -v gpg >/dev/null 2>&1 && gpg --list-secret-keys 2>/dev/null | grep -q sec; then
176
- add_points "encryption" 10 10 "GPG keys configured"
181
+ add_points "encryption" 10 10 "Signing key configured (GPG)"
182
+ elif [[ "$signing_format" == "ssh" && -n "$signing_key" && -f "$signing_key" ]]; then
183
+ add_points "encryption" 10 10 "Signing key configured (SSH)"
177
184
  elif command -v gpg >/dev/null 2>&1; then
178
- add_points "encryption" 3 10 "GPG keys configured"
185
+ add_points "encryption" 3 10 "Signing key configured"
179
186
  else
180
- add_points "encryption" 0 10 "GPG keys configured"
187
+ add_points "encryption" 0 10 "Signing key configured"
181
188
  fi
182
189
  }
183
190
 
@@ -286,6 +293,8 @@ check_system() {
286
293
  if [[ "$(uname)" == "Darwin" ]]; then
287
294
  if fdesetup status 2>/dev/null | grep -q "FileVault is On"; then
288
295
  add_points "system" 10 10 "Disk encryption (FileVault)"
296
+ elif diskutil apfs list 2>/dev/null | grep -q "FileVault:[[:space:]]*Yes"; then
297
+ add_points "system" 10 10 "Disk encryption (APFS FileVault)"
289
298
  else
290
299
  add_points "system" 0 10 "Disk encryption (FileVault)"
291
300
  fi
@@ -323,7 +332,7 @@ check_secrets() {
323
332
  local secrets_found=false
324
333
  local match=""
325
334
  # Check for common secret patterns, ignore templates/placeholders and variable expansions.
326
- match=$(grep -rE "(api_key|api_secret|password|token)[[:space:]]*=[[:space:]]*['\"][^'\"]+['\"]" \
335
+ match=$(grep -rE "^[[:space:]]*(export[[:space:]]+)?[A-Za-z_]*(api_key|api_secret|password|token)[A-Za-z_]*[[:space:]]*=[[:space:]]*['\"][^'\"]+['\"]" \
327
336
  "${HOME}/.dotfiles" --include="*.sh" --include="*.zsh" --include="*.toml" 2>/dev/null |
328
337
  grep -v "example\\|template\\|placeholder" | grep -F -v "\${" | head -1 || true)
329
338
  if [[ -n "$match" ]]; then
@@ -12,6 +12,8 @@ set -euo pipefail
12
12
  echo " Starting Final Environment Verification..."
13
13
 
14
14
  Errors=0
15
+ ALIASES_CONFIG="$HOME/.config/shell/aliases.sh"
16
+ AliasesConfigReported=0
15
17
 
16
18
  check_file() {
17
19
  if [[ ! -f "$1" ]]; then
@@ -23,7 +25,13 @@ check_file() {
23
25
  }
24
26
 
25
27
  check_alias_in_config() {
26
- if ! grep -q "$1" "$HOME/.config/shell/aliases.sh"; then
28
+ if [[ ! -f "$ALIASES_CONFIG" ]]; then
29
+ if [[ "$AliasesConfigReported" -eq 0 ]]; then
30
+ echo " Missing aliases config: $ALIASES_CONFIG"
31
+ Errors=$((Errors + 1))
32
+ AliasesConfigReported=1
33
+ fi
34
+ elif ! grep -q "$1" "$ALIASES_CONFIG"; then
27
35
  echo " Missing alias definition '$1' in built config"
28
36
  Errors=$((Errors + 1))
29
37
  else
File without changes
@@ -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=()
@@ -98,6 +90,8 @@ _ai_refresh_status_cache() {
98
90
  # shellcheck disable=SC2016
99
91
  local _TO=""
100
92
  command -v timeout >/dev/null 2>&1 && _TO="timeout 8 "
93
+ # Variables expand inside the child Bash probe.
94
+ # shellcheck disable=SC2016
101
95
  local probe_script='
102
96
  payload="$1"; out_dir="$2"; to="$3"
103
97
  i="${payload%%|*}"; entry="${payload#*|}"
@@ -110,14 +104,7 @@ _ai_refresh_status_cache() {
110
104
  printf "%s\t0\t\n" "$bin" >"$out_dir/$i"
111
105
  fi
112
106
  '
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.
107
+ # Use only `-I{}` and null records for BSD xargs and quote-safe descriptions.
121
108
  printf '%s\0' "${indexed[@]}" |
122
109
  xargs -0 -I{} -P"$jobs" \
123
110
  bash -c "$probe_script" _ {} "$probe_dir" "$_TO" \
@@ -130,12 +117,7 @@ _ai_refresh_status_cache() {
130
117
  done
131
118
  rm -rf "$probe_dir"
132
119
 
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.
120
+ # Guard against older ui_spinner_stop implementations returning 1 on a TTY.
139
121
  ui_spinner_stop || true
140
122
 
141
123
  mv "$tmp_file" "$AI_STATUS_CACHE_FILE"
@@ -145,10 +127,6 @@ _ai_get_cached_status() {
145
127
  cat "$AI_STATUS_CACHE_FILE"
146
128
  }
147
129
 
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
130
  # _ai_mise_pkg (binary -> mise package map) is defined in lib/dot/ai-install.sh.
153
131
 
154
132
  cmd_ai_status() {
@@ -164,6 +142,7 @@ cmd_ai_status() {
164
142
  "Agents (autonomous)|agent|Amp|amp|Sourcegraph's agentic coder"
165
143
  "Agents (autonomous)|agent|Cursor CLI|cursor-agent|Cursor's terminal agent"
166
144
  "Agents (autonomous)|agent|Grok Build|grok|xAI's terminal coding agent"
145
+ "Agents (autonomous)|agent|Kimi CLI|kimi|Moonshot AI's terminal coding agent"
167
146
  "Coding (interactive)|coding|Aider|aider|Git-aware AI pair programmer"
168
147
  "Coding (interactive)|coding|OpenCode|opencode|Open-source terminal coding agent"
169
148
  "Coding (interactive)|coding|Autohand Code|autohand|Autonomous multi-file coding agent"
@@ -278,6 +257,10 @@ cmd_ai_status() {
278
257
  install_grok_native "$name"
279
258
  continue
280
259
  ;;
260
+ kimi)
261
+ install_kimi_native "$name"
262
+ continue
263
+ ;;
281
264
  esac
282
265
  local pkg
283
266
  pkg=$(_ai_mise_pkg "$bin")
@@ -437,7 +420,12 @@ ${prompt}"
437
420
  fi
438
421
  elif [[ "$tool_bin" == "agy" ]]; then
439
422
  ui_warn "$tool" "not installed"
440
- ui_info "Install" "curl -fsSL -o /tmp/agy-install.sh https://antigravity.google/cli/install.sh && bash /tmp/agy-install.sh"
423
+ ui_info "Install" "dot ai install agy (checksum verified)"
424
+ exit 1
425
+ elif [[ "$tool_bin" == "kimi" ]]; then
426
+ ui_warn "$tool" "not installed"
427
+ ui_info "Install" "dot ai install kimi"
428
+ ui_info "PATH" "Kimi Code installs to ~/.kimi-code/bin; restart your shell after install"
441
429
  exit 1
442
430
  else
443
431
  ui_err "$tool" "not installed and mise not available"
@@ -478,6 +466,27 @@ ${prompt}"
478
466
 
479
467
  # Dispatch — flat, verb-first surface (see docs/AI.md).
480
468
  case "${1:-}" in
469
+ --help | -h | help)
470
+ cat <<'EOF'
471
+ Usage: ai.sh <command> [args...]
472
+
473
+ Commands:
474
+ ai, ai-setup, ai-query, cl, claude, codex, copilot, goose, crush, amp,
475
+ cursor-agent, grok, kimi, agy, kiro, sgpt, ollama, opencode, aider, autohand,
476
+ vibe, qwen, zai
477
+ EOF
478
+ ;;
479
+ "")
480
+ cat <<'EOF'
481
+ Usage: ai.sh <command> [args...]
482
+
483
+ Commands:
484
+ ai, ai-setup, ai-query, cl, claude, codex, copilot, goose, crush, amp,
485
+ cursor-agent, grok, kimi, agy, kiro, sgpt, ollama, opencode, aider, autohand,
486
+ vibe, qwen, zai
487
+ EOF
488
+ exit 1
489
+ ;;
481
490
  ai)
482
491
  shift
483
492
  case "${1:-}" in
@@ -565,7 +574,7 @@ case "${1:-}" in
565
574
  shift
566
575
  cmd_ai_query "$@"
567
576
  ;;
568
- cl | claude | codex | copilot | goose | crush | amp | cursor-agent | grok | agy | kiro | sgpt | ollama | opencode | aider | autohand | vibe | qwen | zai)
577
+ cl | claude | codex | copilot | goose | crush | amp | cursor-agent | grok | kimi | agy | kiro | sgpt | ollama | opencode | aider | autohand | vibe | qwen | zai)
569
578
  _ai_deprecated "dot ai $1"
570
579
  tool="$1"
571
580
  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 "$@"
@@ -73,7 +73,7 @@ _fleet_emit_event() {
73
73
  endpoint="$(sed -n 's/^endpoint = "\(.*\)"/\1/p' "$data_file" | head -1)"
74
74
  fi
75
75
  if [[ -n "$endpoint" ]] && [[ "$endpoint" == https://* ]]; then
76
- curl -fsSL -X POST -H "Content-Type: application/json" \
76
+ curl --proto '=https' --tlsv1.2 -fsSL -X POST -H "Content-Type: application/json" \
77
77
  -d "$payload" "$endpoint" >/dev/null 2>&1 || true
78
78
  fi
79
79
  }
@@ -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