@sebastienrousseau/dotfiles 0.2.519 → 0.2.521

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 (306) hide show
  1. package/CHANGELOG.md +200 -0
  2. package/LICENSE-APACHE +190 -0
  3. package/{LICENSE → LICENSE-MIT} +1 -1
  4. package/README.md +1172 -166
  5. package/install.sh +77 -11
  6. package/package.json +8 -8
  7. package/tools/README.md +49 -0
  8. package/tools/ci/install-chezmoi-verified.sh +68 -0
  9. package/docs/.vitepress/reports/localization-readability-audit.md +0 -73
  10. package/docs/AI.md +0 -179
  11. package/docs/CNAME +0 -1
  12. package/docs/CONFIG_STRATEGY.md +0 -124
  13. package/docs/COPYRIGHT +0 -7
  14. package/docs/GOVERNANCE.md +0 -98
  15. package/docs/MAINTAINERS.md +0 -41
  16. package/docs/NAMING_CONVENTIONS.md +0 -102
  17. package/docs/OPENCODE.md +0 -127
  18. package/docs/README.md +0 -84
  19. package/docs/STRUCTURE.md +0 -102
  20. package/docs/adr/ADR-001-ci-cd-pipeline.md +0 -118
  21. package/docs/adr/ADR-002-shell-performance.md +0 -130
  22. package/docs/adr/ADR-003-security-first.md +0 -158
  23. package/docs/adr/ADR-004-cli-architecture.md +0 -171
  24. package/docs/adr/ADR-005-chezmoi-choice.md +0 -99
  25. package/docs/adr/ADR-006-shell-selection.md +0 -124
  26. package/docs/adr/ADR-007-multi-shell-parity.md +0 -62
  27. package/docs/adr/ADR-008-alias-system-architecture.md +0 -95
  28. package/docs/adr/ADR-009-wallpaper-driven-theming.md +0 -131
  29. package/docs/adr/ADR-010-starship-transient-prompt.md +0 -144
  30. package/docs/adr/ADR-011-nushell-tier3-keep.md +0 -144
  31. package/docs/adr/ADR-012-ai-fleet-local-proxy.md +0 -79
  32. package/docs/adr/README.md +0 -40
  33. package/docs/architecture/AI_COST_OPTIMIZATION.md +0 -144
  34. package/docs/architecture/ARCHITECTURE.md +0 -117
  35. package/docs/architecture/INTEROP.md +0 -44
  36. package/docs/architecture/REPO_LAYOUT.md +0 -241
  37. package/docs/architecture/WALKTHROUGH.md +0 -86
  38. package/docs/architecture/fleet-deployment.md +0 -77
  39. package/docs/archive/EUXIS_2026_REVIEW.md +0 -127
  40. package/docs/archive/LEGACY_ROADMAP.md +0 -6
  41. package/docs/archive/MILESTONE_v0.2.493.md +0 -47
  42. package/docs/archive/PLAN.md +0 -199
  43. package/docs/archive/REPO_AUDIT.md +0 -31
  44. package/docs/articles/.pages +0 -6
  45. package/docs/articles/2026-07-05-custom-mkdocs-material-dark-theme.md +0 -216
  46. package/docs/articles/2026-07-05-fish-startup-abbr.md +0 -153
  47. package/docs/articles/2026-07-05-master-to-main-rename-runbook.md +0 -128
  48. package/docs/articles/index.md +0 -36
  49. package/docs/guides/INSTALL.md +0 -144
  50. package/docs/guides/NEOVIM_IDE_GUIDE.md +0 -61
  51. package/docs/guides/THEMING.md +0 -230
  52. package/docs/guides/TROUBLESHOOTING.md +0 -176
  53. package/docs/guides/WSL2_NIX_TROUBLESHOOTING.md +0 -792
  54. package/docs/index.md +0 -132
  55. package/docs/interop/A2A.md +0 -39
  56. package/docs/interop/POWERSHELL.md +0 -102
  57. package/docs/manual/00-introduction.md +0 -89
  58. package/docs/manual/01-concepts/01-architecture.md +0 -138
  59. package/docs/manual/01-concepts/02-trust-model.md +0 -183
  60. package/docs/manual/01-concepts/03-theme-engine.md +0 -186
  61. package/docs/manual/01-concepts/04-fleet.md +0 -148
  62. package/docs/manual/01-concepts/05-self-healing.md +0 -204
  63. package/docs/manual/02-tutorials/01-first-install.md +0 -197
  64. package/docs/manual/02-tutorials/02-add-wallpaper.md +0 -216
  65. package/docs/manual/02-tutorials/03-create-profile.md +0 -244
  66. package/docs/manual/02-tutorials/04-encrypt-secret.md +0 -281
  67. package/docs/manual/02-tutorials/05-deploy-fleet.md +0 -283
  68. package/docs/manual/03-reference/01-dot-cli.md +0 -450
  69. package/docs/manual/03-reference/02-config-files.md +0 -265
  70. package/docs/manual/03-reference/03-environment.md +0 -124
  71. package/docs/manual/03-reference/04-templates.md +0 -190
  72. package/docs/manual/03-reference/05-feature-flags.md +0 -187
  73. package/docs/manual/04-cookbook/01-recipes.md +0 -285
  74. package/docs/manual/04-cookbook/02-troubleshooting.md +0 -351
  75. package/docs/manual/04-cookbook/03-faq.md +0 -175
  76. package/docs/manual/05-appendices/A-platform-matrix.md +0 -101
  77. package/docs/manual/05-appendices/B-security-checklist.md +0 -85
  78. package/docs/manual/05-appendices/C-glossary.md +0 -40
  79. package/docs/manual/05-appendices/D-bibliography.md +0 -58
  80. package/docs/manual/05-appendices/E-license.md +0 -38
  81. package/docs/manual/_toc.yml +0 -58
  82. package/docs/manual/command-index.md +0 -155
  83. package/docs/manual/concept-index.md +0 -168
  84. package/docs/manual/index.md +0 -66
  85. package/docs/operations/ARCHITECTURE_ROADMAP.md +0 -7
  86. package/docs/operations/ATTESTATION.md +0 -44
  87. package/docs/operations/CI_CADENCE.md +0 -107
  88. package/docs/operations/CI_COMPOSITES.md +0 -156
  89. package/docs/operations/COMPLETIONS.md +0 -123
  90. package/docs/operations/COVERAGE.md +0 -204
  91. package/docs/operations/DRIFT.md +0 -107
  92. package/docs/operations/HARD_AUDIT_2026.md +0 -631
  93. package/docs/operations/MAINTENANCE.md +0 -63
  94. package/docs/operations/MANIFEST.md +0 -127
  95. package/docs/operations/MIGRATION.md +0 -109
  96. package/docs/operations/OPERATIONS.md +0 -188
  97. package/docs/operations/PERFORMANCE.md +0 -133
  98. package/docs/operations/REGISTRY.md +0 -90
  99. package/docs/operations/RELEASE_PIPELINE.md +0 -128
  100. package/docs/operations/RELIABILITY.md +0 -122
  101. package/docs/operations/RFC_v0_2_503_reorganization.md +0 -280
  102. package/docs/operations/ROADMAP.md +0 -10
  103. package/docs/operations/ROADMAP_2026.md +0 -7
  104. package/docs/operations/ROADMAP_V0_2_503.md +0 -10
  105. package/docs/operations/TESTING.md +0 -216
  106. package/docs/operations/TRACEABILITY.md +0 -43
  107. package/docs/operations/TRUSTED_AGENT_WORKSTATION.md +0 -65
  108. package/docs/operations/VERSION_SYNC.md +0 -393
  109. package/docs/reference/ALIASES.md +0 -131
  110. package/docs/reference/ALIASES_CHEATSHEET.md +0 -32
  111. package/docs/reference/ALIASES_DEPRECATIONS.md +0 -13
  112. package/docs/reference/FEATURES.md +0 -66
  113. package/docs/reference/FONTS.md +0 -112
  114. package/docs/reference/POWERSHELL_PARITY.md +0 -82
  115. package/docs/reference/PROFILES.md +0 -69
  116. package/docs/reference/SCREENSHOTS.md +0 -121
  117. package/docs/reference/SCRIPTS.md +0 -71
  118. package/docs/reference/SUPPORT_MATRIX.md +0 -80
  119. package/docs/reference/THEMES.md +0 -117
  120. package/docs/reference/TOOLS.md +0 -110
  121. package/docs/reference/UTILS.md +0 -242
  122. package/docs/registry.json +0 -6
  123. package/docs/schema/dot-env-v1.json +0 -110
  124. package/docs/schema/dot-registry-v1.json +0 -33
  125. package/docs/security/AI_ACT_COMPLIANCE.md +0 -94
  126. package/docs/security/AUDIT_BYPASS.md +0 -103
  127. package/docs/security/AUTOMATION_SECRETS.md +0 -26
  128. package/docs/security/CI_EGRESS_ALLOWLIST.md +0 -127
  129. package/docs/security/CI_PINNING.md +0 -129
  130. package/docs/security/COMMIT_SIGNING.md +0 -138
  131. package/docs/security/COMPLIANCE.md +0 -458
  132. package/docs/security/DEPS_DEV_EXCEPTIONS.md +0 -86
  133. package/docs/security/DISCLOSURE.md +0 -130
  134. package/docs/security/ENCRYPTION.md +0 -57
  135. package/docs/security/FMEA.md +0 -159
  136. package/docs/security/FUZZING.md +0 -114
  137. package/docs/security/HISTORY_FILTERING.md +0 -132
  138. package/docs/security/INCIDENT_RESPONSE.md +0 -579
  139. package/docs/security/INSTALL_VERIFICATION.md +0 -122
  140. package/docs/security/KEYS.md +0 -49
  141. package/docs/security/KEY_ROTATION.md +0 -303
  142. package/docs/security/MCP_POLICY.md +0 -78
  143. package/docs/security/POLICY_RELEASES.md +0 -37
  144. package/docs/security/README.md +0 -28
  145. package/docs/security/SCORECARD.md +0 -195
  146. package/docs/security/SECRETS.md +0 -158
  147. package/docs/security/SECURITY.md +0 -45
  148. package/docs/security/SECURITY_CHECKLIST.md +0 -55
  149. package/docs/security/SHELL_EXEMPTIONS.md +0 -145
  150. package/docs/security/SOUP_REGISTER.md +0 -36
  151. package/docs/security/THREAT_MODEL.md +0 -130
  152. package/docs/security/VERIFICATION_VALIDATION.md +0 -228
  153. package/docs/security/VERIFY_RELEASE.md +0 -201
  154. package/docs/security/security-pubkey.asc +0 -15
  155. package/docs/stylesheets/extra.css +0 -444
  156. package/docs/themes/README.md +0 -10
  157. package/docs/themes/VISUAL_INTEGRITY_REPORT.md +0 -30
  158. package/docs/themes/hero-shot.svg +0 -78
  159. package/scripts/README.md +0 -123
  160. package/scripts/ci/check-copyright-headers.sh +0 -8
  161. package/scripts/ci/check-shell-preamble.sh +0 -8
  162. package/scripts/ci/guard-gitleaks-checkout.sh +0 -8
  163. package/scripts/demo/record.sh +0 -43
  164. package/scripts/diagnostics/a2a-conformance.sh +0 -163
  165. package/scripts/diagnostics/alias-governance.sh +0 -138
  166. package/scripts/diagnostics/aliases-cheatsheet.sh +0 -74
  167. package/scripts/diagnostics/aliases-manifest.sh +0 -77
  168. package/scripts/diagnostics/benchmark.sh +0 -408
  169. package/scripts/diagnostics/conflicts.sh +0 -73
  170. package/scripts/diagnostics/doctor-unified.sh +0 -39
  171. package/scripts/diagnostics/doctor.sh +0 -751
  172. package/scripts/diagnostics/drift-dashboard.sh +0 -202
  173. package/scripts/diagnostics/health.sh +0 -623
  174. package/scripts/diagnostics/history-analysis.sh +0 -86
  175. package/scripts/diagnostics/mcp-doctor.sh +0 -582
  176. package/scripts/diagnostics/perf.sh +0 -453
  177. package/scripts/diagnostics/scorecard.sh +0 -119
  178. package/scripts/diagnostics/secret-governance.sh +0 -65
  179. package/scripts/diagnostics/security-score.sh +0 -467
  180. package/scripts/diagnostics/smoke-test.sh +0 -88
  181. package/scripts/diagnostics/snapshot.sh +0 -90
  182. package/scripts/diagnostics/verify.sh +0 -108
  183. package/scripts/diagnostics/verify_state.sh +0 -73
  184. package/scripts/diagnostics/version-locks.sh +0 -94
  185. package/scripts/diagnostics/workstation-attestation.sh +0 -187
  186. package/scripts/dot/commands/agent.sh +0 -485
  187. package/scripts/dot/commands/agents.sh +0 -336
  188. package/scripts/dot/commands/ai.sh +0 -587
  189. package/scripts/dot/commands/aliases.sh +0 -277
  190. package/scripts/dot/commands/appearance.sh +0 -110
  191. package/scripts/dot/commands/completion.sh +0 -134
  192. package/scripts/dot/commands/core.sh +0 -217
  193. package/scripts/dot/commands/diagnostics.sh +0 -265
  194. package/scripts/dot/commands/env-emit.sh +0 -203
  195. package/scripts/dot/commands/fleet.sh +0 -688
  196. package/scripts/dot/commands/init.sh +0 -185
  197. package/scripts/dot/commands/lint.sh +0 -208
  198. package/scripts/dot/commands/manual.sh +0 -169
  199. package/scripts/dot/commands/meta.sh +0 -333
  200. package/scripts/dot/commands/patterns.sh +0 -55
  201. package/scripts/dot/commands/registry.sh +0 -419
  202. package/scripts/dot/commands/restore.sh +0 -232
  203. package/scripts/dot/commands/secrets.sh +0 -296
  204. package/scripts/dot/commands/security.sh +0 -102
  205. package/scripts/dot/commands/tools.sh +0 -556
  206. package/scripts/dot/data/alias-deprecations.tsv +0 -2
  207. package/scripts/dot/powershell/Dot.psm1 +0 -319
  208. package/scripts/fonts/install-nerd-fonts.sh +0 -75
  209. package/scripts/fonts/patch-fonts.sh +0 -36
  210. package/scripts/git-hooks/install.sh +0 -12
  211. package/scripts/git-hooks/pre-commit +0 -12
  212. package/scripts/git-hooks/pre-commit-audit.sh +0 -146
  213. package/scripts/git-hooks/pre-push +0 -105
  214. package/scripts/git-hooks/prepare-commit-msg +0 -29
  215. package/scripts/lib/secrets_provider.sh +0 -185
  216. package/scripts/ops/ai-setup.sh +0 -71
  217. package/scripts/ops/bundle.sh +0 -104
  218. package/scripts/ops/chaos.sh +0 -50
  219. package/scripts/ops/chezmoi-apply.sh +0 -333
  220. package/scripts/ops/chezmoi-diff.sh +0 -16
  221. package/scripts/ops/chezmoi-remove.sh +0 -46
  222. package/scripts/ops/chezmoi-update.sh +0 -63
  223. package/scripts/ops/heal-chezmoi.sh +0 -87
  224. package/scripts/ops/heal-system.sh +0 -129
  225. package/scripts/ops/heal-tools.sh +0 -297
  226. package/scripts/ops/heal.sh +0 -223
  227. package/scripts/ops/post-apply-repair.sh +0 -107
  228. package/scripts/ops/prewarm.sh +0 -128
  229. package/scripts/ops/release.sh +0 -262
  230. package/scripts/ops/rollback.sh +0 -604
  231. package/scripts/ops/setup.sh +0 -138
  232. package/scripts/ops/teleport.sh +0 -34
  233. package/scripts/qa/check-version-consistency.sh +0 -124
  234. package/scripts/qa/coverage-baseline.sh +0 -61
  235. package/scripts/qa/docs-coverage.sh +0 -112
  236. package/scripts/qa/examples-coverage.sh +0 -94
  237. package/scripts/qa/powershell-contract.ps1 +0 -95
  238. package/scripts/qa/reliability-audit.sh +0 -139
  239. package/scripts/qa/scorecard-snapshot.sh +0 -128
  240. package/scripts/qa/traceability-coverage.sh +0 -117
  241. package/scripts/qa/validate-examples.sh +0 -27
  242. package/scripts/qa/wsl-contract.sh +0 -12
  243. package/scripts/secrets/age-init.sh +0 -82
  244. package/scripts/secrets/create-secrets-file.sh +0 -46
  245. package/scripts/secrets/encrypt-ssh-key.sh +0 -44
  246. package/scripts/security/backup.sh +0 -58
  247. package/scripts/security/check-disclosure-key-expiry.sh +0 -111
  248. package/scripts/security/dns-doh.sh +0 -52
  249. package/scripts/security/encryption-check.sh +0 -55
  250. package/scripts/security/enforce-policies.sh +0 -335
  251. package/scripts/security/firewall.sh +0 -91
  252. package/scripts/security/lock-configs.sh +0 -67
  253. package/scripts/security/lock-screen.sh +0 -56
  254. package/scripts/security/manage-secrets.sh +0 -429
  255. package/scripts/security/ssh-cert.sh +0 -204
  256. package/scripts/security/telemetry-kill.sh +0 -51
  257. package/scripts/security/usb-safety.sh +0 -52
  258. package/scripts/theme/apply-gnome-theme.sh +0 -333
  259. package/scripts/theme/extract-heic-frames.sh +0 -115
  260. package/scripts/theme/extract-theme.py +0 -742
  261. package/scripts/theme/install-boot-logo.sh +0 -63
  262. package/scripts/theme/install-catppuccin-themes.sh +0 -371
  263. package/scripts/theme/install-cursors.sh +0 -26
  264. package/scripts/theme/install-file-icons.sh +0 -27
  265. package/scripts/theme/install-grub-theme.sh +0 -62
  266. package/scripts/theme/install-lock-icon.sh +0 -31
  267. package/scripts/theme/merge-wallpaper.sh +0 -146
  268. package/scripts/theme/rebuild-themes.sh +0 -544
  269. package/scripts/theme/switch.sh +0 -449
  270. package/scripts/theme/wallpaper-rotate.sh +0 -137
  271. package/scripts/theme/wallpaper-sync.sh +0 -690
  272. package/scripts/tools/cmatrix.sh +0 -22
  273. package/scripts/tools/detect-collisions.py +0 -103
  274. package/scripts/tools/emoji-picker.sh +0 -49
  275. package/scripts/tools/figlet-banner.sh +0 -19
  276. package/scripts/tools/log-rotate.sh +0 -31
  277. package/scripts/tools/lolcat-wrap.sh +0 -20
  278. package/scripts/tools/pipes.sh +0 -49
  279. package/scripts/tuning/linux.sh +0 -186
  280. package/scripts/tuning/macos.sh +0 -56
  281. package/scripts/uninstall.sh +0 -86
  282. package/scripts/version-sync.sh +0 -654
  283. package/templates/chezmoi-data/geekom-a9.toml.example +0 -21
  284. package/templates/chezmoi-data/mac-m1.toml.example +0 -16
  285. package/templates/chezmoi-data/mac-t2-linux.toml.example +0 -21
  286. package/templates/chezmoi-data/surface-pro-7p.toml.example +0 -21
  287. package/templates/projects/go/.github/workflows/ci.yml +0 -31
  288. package/templates/projects/go/README.md +0 -7
  289. package/templates/projects/go/cmd/__PROJECT_NAME__/main.go +0 -8
  290. package/templates/projects/go/go.mod +0 -3
  291. package/templates/projects/go/go.sum +0 -0
  292. package/templates/projects/molecule/README.md +0 -7
  293. package/templates/projects/molecule/converge.yml +0 -7
  294. package/templates/projects/molecule/molecule.yml +0 -16
  295. package/templates/projects/node/.github/workflows/ci.yml +0 -30
  296. package/templates/projects/node/README.md +0 -7
  297. package/templates/projects/node/package-lock.json +0 -12
  298. package/templates/projects/node/package.json +0 -10
  299. package/templates/projects/node/src/index.js +0 -3
  300. package/templates/projects/packer/README.md +0 -15
  301. package/templates/projects/packer/main.pkr.hcl +0 -15
  302. package/templates/projects/python/.github/workflows/ci.yml +0 -34
  303. package/templates/projects/python/README.md +0 -7
  304. package/templates/projects/python/pyproject.toml +0 -25
  305. package/templates/projects/python/src/__PROJECT_NAME__/__init__.py +0 -2
  306. package/templates/projects/python/tests/test_basic.py +0 -3
@@ -1,751 +0,0 @@
1
- #!/usr/bin/env bash
2
- # SPDX-License-Identifier: MIT
3
- # Copyright (c) 2015-2026 Sebastien Rousseau
4
- ## Dotfiles Doctor.
5
- ##
6
- ## Diagnoses dotfiles environment health by checking dependencies, paths,
7
- ## and configuration integrity. Reports errors, warnings, and suggests
8
- ## remediation steps.
9
- ##
10
- ## # Usage
11
- ## dot doctor
12
- ##
13
- ## # Exit Codes
14
- ## - 0: All checks passed (may have warnings)
15
- ## - 1: Critical errors detected
16
- ##
17
- ## # Idempotency
18
- ## Safe to run repeatedly. Read-only checks.
19
-
20
- set -euo pipefail
21
-
22
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
23
- # shellcheck source=../../lib/dot/ui.sh
24
- # shellcheck disable=SC1091
25
- source "$SCRIPT_DIR/../../lib/dot/ui.sh"
26
- # shellcheck source=../../lib/dot/platform.sh
27
- # shellcheck disable=SC1091
28
- source "$SCRIPT_DIR/../../lib/dot/platform.sh"
29
- # shellcheck source=../../lib/dot/log.sh
30
- # shellcheck disable=SC1091
31
- source "$SCRIPT_DIR/../../lib/dot/log.sh"
32
- export DOT_COMMAND="doctor"
33
-
34
- ui_init
35
-
36
- # Extend PATH to include common non-standard install locations
37
- export PATH="$HOME/.atuin/bin:$HOME/.local/bin:$PATH"
38
-
39
- Errors=0
40
- Warnings=0
41
- AI_DEBUG=0
42
-
43
- for arg in "$@"; do
44
- case "$arg" in
45
- --ai) AI_DEBUG=1 ;;
46
- esac
47
- done
48
-
49
- # --- Output helpers (delegate to shared ui.sh) ---
50
- _ok() { ui_ok "$1" "${2:-}"; }
51
- _fail() {
52
- ui_err "$1" "${2:-}"
53
- Errors=$((Errors + 1))
54
- }
55
- _warn() {
56
- ui_warn "$1" "${2:-}"
57
- Warnings=$((Warnings + 1))
58
- }
59
- _section() {
60
- echo ""
61
- ui_section "$1"
62
- }
63
-
64
- pretty_path() {
65
- local value="${1:-}"
66
- if [[ -n "$value" ]]; then
67
- printf '%s' "${value/#$HOME/\~}"
68
- fi
69
- }
70
-
71
- tool_source() {
72
- local path="${1:-}"
73
- if [[ "$path" == "$HOME/.local/share/mise/"* ]]; then
74
- echo "mise"
75
- elif [[ "$path" == /usr/bin/* || "$path" == /bin/* || "$path" == /usr/sbin/* || "$path" == /sbin/* ]]; then
76
- echo "system"
77
- else
78
- echo "custom"
79
- fi
80
- }
81
-
82
- check_cmd() {
83
- local cmd="$1"
84
- if command -v "$cmd" &>/dev/null; then return 0; fi
85
- if command -v mise &>/dev/null; then
86
- if mise ls --installed 2>/dev/null | grep -qE "($cmd|aqua:.*$cmd)"; then
87
- return 0
88
- fi
89
- fi
90
- return 1
91
- }
92
-
93
- get_cmd_path() {
94
- local cmd="$1"
95
- if command -v "$cmd" &>/dev/null; then
96
- command -v "$cmd"
97
- elif command -v mise &>/dev/null; then
98
- local bin_path
99
- bin_path=$(mise bin-paths 2>/dev/null | grep -E "/$cmd(/|$)" | head -n 1)
100
- if [ -n "$bin_path" ]; then
101
- echo "$bin_path/$cmd"
102
- else
103
- echo "$cmd"
104
- fi
105
- else
106
- echo "$cmd"
107
- fi
108
- }
109
-
110
- # --- Header ---
111
- ui_header "Dotfiles Doctor"
112
-
113
- # --- Core Shells ---
114
- _section "Core Shells"
115
- for cmd in zsh fish starship; do
116
- if check_cmd "$cmd"; then
117
- cmd_path="$(get_cmd_path "$cmd")"
118
- _ok "$cmd" "$(pretty_path "$cmd_path") ($(tool_source "$cmd_path"))"
119
- elif [[ "$cmd" == "fish" ]]; then
120
- _warn "$cmd" "optional"
121
- else
122
- _fail "$cmd" "missing"
123
- fi
124
- done
125
-
126
- if check_cmd "nu"; then
127
- cmd_path="$(get_cmd_path "nu")"
128
- _ok "nu" "$(pretty_path "$cmd_path") ($(tool_source "$cmd_path"))"
129
- elif check_cmd "nushell"; then
130
- cmd_path="$(get_cmd_path "nushell")"
131
- _ok "nu" "$(pretty_path "$cmd_path") ($(tool_source "$cmd_path"))"
132
- else
133
- _warn "nu" "optional"
134
- fi
135
-
136
- # --- Modern CLI Tools ---
137
- _section "Modern CLI Tools"
138
- for cmd in rg bat chezmoi fzf zoxide atuin yazi zellij; do
139
- if check_cmd "$cmd"; then
140
- _ok "$cmd" "$(pretty_path "$(get_cmd_path "$cmd")")"
141
- elif [[ "$cmd" == "bat" ]] && check_cmd "batcat"; then
142
- _ok "$cmd" "$(pretty_path "$(get_cmd_path "batcat")") (batcat)"
143
- else
144
- _fail "$cmd" "missing"
145
- fi
146
- done
147
-
148
- # --- Infrastructure ---
149
- _section "Infrastructure"
150
- for cmd in pueue wasmtime nix sops age hyperfine; do
151
- if check_cmd "$cmd"; then
152
- _ok "$cmd" "$(pretty_path "$(get_cmd_path "$cmd")")"
153
- elif [[ "$cmd" == "nix" ]]; then
154
- _ok "$cmd" "optional (not installed)"
155
- else
156
- _fail "$cmd" "missing"
157
- fi
158
- done
159
-
160
- if check_cmd pueue; then
161
- if "$(get_cmd_path pueue)" status >/dev/null 2>&1; then
162
- _ok "pueue daemon" "running"
163
- elif command -v pueued >/dev/null 2>&1 && pueued -d >/dev/null 2>&1 && "$(get_cmd_path pueue)" status >/dev/null 2>&1; then
164
- _ok "pueue daemon" "started"
165
- else
166
- _warn "pueue daemon" "not running (pueued -d)"
167
- fi
168
- fi
169
-
170
- # --- AI CLIs ---
171
- _section "AI CLIs"
172
- for cmd in claude copilot kimi agy sgpt ollama opencode aider kiro-cli; do
173
- if check_cmd "$cmd"; then
174
- _ok "$cmd" "$(pretty_path "$(get_cmd_path "$cmd")")"
175
- else
176
- _warn "$cmd" "optional"
177
- fi
178
- done
179
-
180
- # --- Environment ---
181
- _section "Environment"
182
- if [[ -n "${XDG_CONFIG_HOME:-}" ]]; then
183
- _ok "XDG_CONFIG_HOME" "$(pretty_path "$XDG_CONFIG_HOME")"
184
- else
185
- _warn "XDG_CONFIG_HOME" "defaulting to ~/.config"
186
- fi
187
-
188
- # Validate XDG paths are absolute
189
- for var in XDG_CONFIG_HOME XDG_DATA_HOME XDG_CACHE_HOME XDG_STATE_HOME; do
190
- val="${!var:-}"
191
- if [[ -n "$val" ]] && [[ "$val" != /* ]]; then
192
- _warn "$var" "not absolute: $val"
193
- fi
194
- done
195
-
196
- if [[ -n "${PIPX_HOME:-}" ]]; then
197
- _ok "PIPX_HOME" "$(pretty_path "$PIPX_HOME")"
198
- else
199
- _warn "PIPX_HOME" "not set"
200
- fi
201
-
202
- # --- Platform ---
203
- _section "Platform"
204
-
205
- platform_id="$(dot_platform_id)"
206
- _os_name="$(uname -s)"
207
- _kernel="$(uname -sr)"
208
- _arch="$(uname -m)"
209
- _user="$(whoami 2>/dev/null || echo "${USER:-unknown}")"
210
- _hostname_val="$(hostname -s 2>/dev/null || hostname 2>/dev/null || echo "unknown")"
211
-
212
- # Shell version
213
- _shell_name="${SHELL##*/}"
214
- _shell_ver="$("$SHELL" --version 2>&1 | head -1 | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' | head -1 || true)"
215
- _shell="${_shell_name}${_shell_ver:+ $_shell_ver}"
216
-
217
- # Terminal
218
- _terminal="${TERM_PROGRAM:-${TERM:-unknown}}"
219
-
220
- # Uptime (portable)
221
- if uptime -p >/dev/null 2>&1; then
222
- _uptime="$(uptime -p 2>/dev/null | sed 's/^up //')"
223
- else
224
- _uptime="$(uptime 2>/dev/null | sed -E 's/^.* up ([^,]+(, [^,]+){0,2}), [0-9]+ users?.*$/\1/' || true)"
225
- fi
226
-
227
- # --- OS-specific detection ---
228
- _os="" _host="" _cpu="" _cpu_cores="" _gpu="" _mem="" _resolution="" _packages="" _de=""
229
- os_release_file="${DOT_DOCTOR_OS_RELEASE:-/etc/os-release}"
230
- proc_root="${DOT_DOCTOR_PROC_ROOT:-/proc}"
231
- sys_root="${DOT_DOCTOR_SYS_ROOT:-/sys}"
232
-
233
- if [[ "$_os_name" == "Darwin" ]]; then
234
- # macOS
235
- _os="macOS $(sw_vers -productVersion 2>/dev/null || echo "unknown")"
236
- _host="$(/usr/sbin/system_profiler SPHardwareDataType 2>/dev/null | awk -F': ' '/Model Name/{print $2}' || sysctl -n hw.model 2>/dev/null || echo "Mac")"
237
- _cpu="$(sysctl -n machdep.cpu.brand_string 2>/dev/null || echo "$_arch")"
238
- _cpu_cores="$(sysctl -n hw.ncpu 2>/dev/null || echo "?")"
239
- _gpu="$(system_profiler SPDisplaysDataType 2>/dev/null | awk -F': ' '/Chipset Model|Chip/{print $2; exit}' || echo "n/a")"
240
- _mem_total="$(sysctl -n hw.memsize 2>/dev/null || echo 0)"
241
- _mem_total_gb="$(awk "BEGIN{printf \"%.2f\", ${_mem_total}/1073741824}")"
242
- _mem_pages="$(vm_stat 2>/dev/null | awk '/Pages active/{gsub(/\./,"",$3); print $3}')"
243
- _mem_used_gb="$(awk "BEGIN{printf \"%.2f\", ${_mem_pages:-0}*4096/1073741824}")"
244
- _mem="${_mem_used_gb} GiB / ${_mem_total_gb} GiB"
245
- _resolution="$(system_profiler SPDisplaysDataType 2>/dev/null | awk '/Resolution/{gsub(/^ +/,""); print; exit}' | sed 's/Resolution: //' || echo "n/a")"
246
- _packages="$(brew list --formula 2>/dev/null | wc -l | tr -d ' ') (brew)"
247
- _de="Aqua"
248
-
249
- elif [[ -r "$os_release_file" ]]; then
250
- # Linux (Debian, Ubuntu, Arch, Fedora, RHEL, etc.)
251
- # shellcheck disable=SC1091
252
- . "$os_release_file"
253
- _os="${PRETTY_NAME:-$ID}"
254
-
255
- _host="$(cat "$sys_root/devices/virtual/dmi/id/product_name" 2>/dev/null || cat "$sys_root/firmware/devicetree/base/model" 2>/dev/null || echo "Linux")"
256
-
257
- if command -v lscpu >/dev/null 2>&1; then
258
- _cpu="$(lscpu | awk -F': +' '/Model name/{print $2}')"
259
- _cpu_cores="$(lscpu | awk -F': +' '/^CPU\(s\):/{print $2}')"
260
- else
261
- _cpu="$(grep -m1 'model name' "$proc_root/cpuinfo" 2>/dev/null | cut -d: -f2 | sed 's/^ //' || uname -p)"
262
- _cpu_cores="$(grep -c '^processor' "$proc_root/cpuinfo" 2>/dev/null || echo "?")"
263
- fi
264
-
265
- if command -v lspci >/dev/null 2>&1; then
266
- # Headless hosts (VMs, containers, CI runners) expose no display
267
- # controller, so `grep` exits 1 and — under `set -euo pipefail` —
268
- # would abort the whole report mid-section. Absorb the miss.
269
- _gpu="$(lspci 2>/dev/null | grep -iE 'vga|3d|display' | head -1 | sed 's/.*: //' || true)"
270
- _gpu="${_gpu:-n/a}"
271
- else
272
- _gpu="n/a"
273
- fi
274
-
275
- if command -v free >/dev/null 2>&1; then
276
- _mem="$(free -b | awk '/Mem:/{printf "%.2f GiB / %.2f GiB", $3/1073741824, $2/1073741824}')"
277
- elif [[ -r "$proc_root/meminfo" ]]; then
278
- _mem_total_kb="$(awk '/MemTotal/{print $2}' "$proc_root/meminfo")"
279
- _mem_avail_kb="$(awk '/MemAvailable/{print $2}' "$proc_root/meminfo")"
280
- _mem_used_kb=$((_mem_total_kb - _mem_avail_kb))
281
- _mem="$(awk "BEGIN{printf \"%.2f GiB / %.2f GiB\", ${_mem_used_kb}/1048576, ${_mem_total_kb}/1048576}")"
282
- else
283
- _mem="n/a"
284
- fi
285
-
286
- # Resolution (Wayland or X11)
287
- if command -v wlr-randr >/dev/null 2>&1; then
288
- _resolution="$(wlr-randr 2>/dev/null | awk '/current/{print $1; exit}' || echo "n/a")"
289
- elif command -v xrandr >/dev/null 2>&1; then
290
- _resolution="$(xrandr 2>/dev/null | awk '/\*/{print $1; exit}' || echo "n/a")"
291
- elif command -v xdpyinfo >/dev/null 2>&1; then
292
- _resolution="$(xdpyinfo 2>/dev/null | awk '/dimensions/{print $2}' || echo "n/a")"
293
- else
294
- _resolution="n/a"
295
- fi
296
-
297
- # Packages (multi-distro)
298
- _pkg_count=0
299
- _pkg_mgr="pkg"
300
- if command -v dpkg >/dev/null 2>&1; then
301
- _pkg_count="$(dpkg --get-selections 2>/dev/null | wc -l | tr -d ' ')"
302
- _pkg_mgr="dpkg"
303
- elif command -v rpm >/dev/null 2>&1; then
304
- _pkg_count="$(rpm -qa 2>/dev/null | wc -l | tr -d ' ')"
305
- _pkg_mgr="rpm"
306
- elif command -v pacman >/dev/null 2>&1; then
307
- _pkg_count="$(pacman -Q 2>/dev/null | wc -l | tr -d ' ')"
308
- _pkg_mgr="pacman"
309
- fi
310
- _packages="${_pkg_count} (${_pkg_mgr})"
311
-
312
- _de="${XDG_CURRENT_DESKTOP:-${DESKTOP_SESSION:-n/a}}"
313
-
314
- else
315
- # Fallback (unknown OS)
316
- _os="$(uname -sr)"
317
- _host="unknown"
318
- _cpu="$(uname -p 2>/dev/null || echo "unknown")"
319
- _cpu_cores="?"
320
- _gpu="n/a"
321
- _mem="n/a"
322
- _resolution="n/a"
323
- _packages="n/a"
324
- _de="n/a"
325
- fi
326
-
327
- # WSL detection and overrides
328
- _wsl=""
329
- if [[ -f "$proc_root/version" ]] && grep -qi microsoft "$proc_root/version" 2>/dev/null; then
330
- _wsl="yes"
331
- _os="${_os} (WSL)"
332
- _de="Windows Desktop (WSL)"
333
- _terminal="${TERM_PROGRAM:-Windows Terminal}"
334
- fi
335
-
336
- # Print platform info
337
- _C='\033[0;36m'
338
- _W='\033[1;37m'
339
- _N='\033[0m'
340
- _D='\033[2m'
341
-
342
- printf '\n'
343
- printf ' %b%s%b@%b%s%b\n' "$_C" "$_user" "$_N" "$_C" "$_hostname_val" "$_N"
344
- printf ' %b%s%b\n' "$_D" "$(printf '%*s' "$((${#_user} + 1 + ${#_hostname_val}))" '' | tr ' ' '-')" "$_N"
345
- printf ' %bOS:%b %s\n' "$_W" "$_N" "$_os"
346
- printf ' %bHost:%b %s\n' "$_W" "$_N" "$_host"
347
- printf ' %bKernel:%b %s\n' "$_W" "$_N" "$_kernel"
348
- printf ' %bUptime:%b %s\n' "$_W" "$_N" "${_uptime:-n/a}"
349
- printf ' %bPackages:%b %s\n' "$_W" "$_N" "$_packages"
350
- printf ' %bShell:%b %s\n' "$_W" "$_N" "$_shell"
351
- printf ' %bResolution:%b %s\n' "$_W" "$_N" "$_resolution"
352
- printf ' %bDE:%b %s\n' "$_W" "$_N" "$_de"
353
- printf ' %bTerminal:%b %s\n' "$_W" "$_N" "$_terminal"
354
- printf ' %bCPU:%b %s (%s)\n' "$_W" "$_N" "$_cpu" "$_cpu_cores"
355
- printf ' %bGPU:%b %s\n' "$_W" "$_N" "${_gpu:-n/a}"
356
- printf ' %bMemory:%b %s\n' "$_W" "$_N" "$_mem"
357
- printf ' %bArch:%b %s\n' "$_W" "$_N" "$_arch"
358
-
359
- if [[ -n "$_wsl" ]]; then
360
- echo ""
361
- if command -v wslpath >/dev/null 2>&1; then
362
- _ok "WSL bridge" "wslpath available"
363
- else
364
- _warn "WSL bridge" "wslpath missing"
365
- fi
366
- if [[ "$PWD" == /mnt/* ]]; then
367
- _warn "WSL filesystem" "/mnt causes IO latency"
368
- else
369
- _ok "WSL filesystem" "native"
370
- fi
371
- fi
372
-
373
- # --- State ---
374
- _section "State"
375
- if chezmoi verify &>/dev/null; then
376
- _ok "chezmoi" "synchronized"
377
- else
378
- _fail "chezmoi" "drifted (run dot drift)"
379
- fi
380
-
381
- if [[ -f "$HOME/.zshrc" ]]; then
382
- _ok ".zshrc" "present"
383
- else
384
- _fail ".zshrc" "missing"
385
- fi
386
-
387
- if command -v dot >/dev/null 2>&1; then
388
- dot_path="$(command -v dot)"
389
- if [[ "$dot_path" == "$HOME/.local/bin/dot" ]]; then
390
- _ok "dot" "$(pretty_path "$dot_path")"
391
- else
392
- _warn "dot" "$(pretty_path "$dot_path") (expected ~/.local/bin/dot)"
393
- fi
394
- else
395
- _fail "dot" "not found in PATH"
396
- fi
397
-
398
- # --- Pre-push audit bypass log (closes #871) ---
399
- _section "Pre-Push Audit Bypass"
400
-
401
- bypass_log="${XDG_STATE_HOME:-$HOME/.local/state}/dotfiles/audit-bypass.log"
402
- if [[ -s "$bypass_log" ]]; then
403
- # Count entries in the last 7 days (timestamps are ISO-8601 UTC).
404
- seven_days_ago=$(date -u -v-7d +%Y-%m-%dT00:00:00Z 2>/dev/null ||
405
- date -u -d '7 days ago' +%Y-%m-%dT00:00:00Z 2>/dev/null ||
406
- echo "1970-01-01T00:00:00Z")
407
- recent=$(awk -v cutoff="$seven_days_ago" '$1 >= cutoff' "$bypass_log" | wc -l | tr -d ' ')
408
- if ((recent > 0)); then
409
- _warn "audit bypass" "$recent push(es) bypassed in last 7 days — see $(pretty_path "$bypass_log")"
410
- else
411
- _ok "audit bypass" "log exists; no recent entries (last 7d)"
412
- fi
413
- else
414
- _ok "audit bypass" "clean — no pre-push audits bypassed"
415
- fi
416
-
417
- # --- Atuin history-filter (closes #872) ---
418
- _section "Atuin History Filter"
419
-
420
- atuin_cfg="$HOME/.config/atuin/config.toml"
421
- if [[ ! -f "$atuin_cfg" ]]; then
422
- _warn "atuin config" "not found at $(pretty_path "$atuin_cfg")"
423
- else
424
- if grep -Eq "^history_filter[[:space:]]*=" "$atuin_cfg"; then
425
- pattern_count=$(awk '
426
- /^history_filter[[:space:]]*=[[:space:]]*\[/ { inside = 1; next }
427
- inside && /^\]/ { inside = 0 }
428
- inside && /^[[:space:]]*"/ { count++ }
429
- END { print count + 0 }
430
- ' "$atuin_cfg")
431
- if ((pattern_count >= 10)); then
432
- _ok "history_filter" "$pattern_count patterns (chezmoi-managed)"
433
- else
434
- _warn "history_filter" "$pattern_count patterns (expected ≥10 from secrets-patterns.toml)"
435
- fi
436
- else
437
- _fail "history_filter" "no history_filter block in $(pretty_path "$atuin_cfg") — secrets may leak into shell history"
438
- fi
439
- fi
440
-
441
- # --- Topgrade Integration ---
442
- _section "Topgrade Integration"
443
-
444
- if command -v antigravity >/dev/null 2>&1; then
445
- ag_path="$(command -v antigravity)"
446
- if [[ "$ag_path" == "$HOME/.local/bin/antigravity" ]]; then
447
- _ok "antigravity wrapper" "$(pretty_path "$ag_path")"
448
- else
449
- _warn "antigravity wrapper" "$(pretty_path "$ag_path") (expected ~/.local/bin/antigravity)"
450
- fi
451
- else
452
- _warn "antigravity" "optional"
453
- fi
454
-
455
- if [[ -f "$HOME/.config/fish/fish_plugins" ]]; then
456
- if grep -qx "jorgebucaran/fisher" "$HOME/.config/fish/fish_plugins"; then
457
- _ok "fish_plugins" "contains jorgebucaran/fisher"
458
- else
459
- _warn "fish_plugins" "present but missing jorgebucaran/fisher"
460
- fi
461
- else
462
- _fail "fish_plugins" "missing (~/.config/fish/fish_plugins)"
463
- fi
464
-
465
- if command -v cargo-install-update >/dev/null 2>&1; then
466
- _ok "cargo-install-update" "$(pretty_path "$(command -v cargo-install-update)")"
467
- else
468
- _warn "cargo-install-update" "missing (install: cargo install cargo-update)"
469
- fi
470
-
471
- # --- Symlinks ---
472
- broken_links=0
473
- for root in "$HOME/.config" "$HOME/.local/bin" "$HOME/.local/share" "$HOME/.ssh"; do
474
- [[ -d "$root" ]] || continue
475
- while IFS= read -r -d '' link; do
476
- link_name="$(basename "$link")"
477
- [[ "$link" == *"google-chrome-backup"* ]] && continue
478
- [[ "$link_name" == SingletonLock || "$link_name" == SingletonCookie || "$link_name" == SingletonSocket ]] && continue
479
- [[ -e "$link" ]] || broken_links=$((broken_links + 1))
480
- done < <(find "$root" -maxdepth 3 -type l -print0 2>/dev/null)
481
- done
482
-
483
- if [[ $broken_links -eq 0 ]]; then
484
- _ok "symlinks" "none broken"
485
- else
486
- _warn "symlinks" "$broken_links broken"
487
- fi
488
-
489
- # --- Portability ---
490
- ghost_paths=0
491
- if command -v chezmoi >/dev/null 2>&1; then
492
- while IFS= read -r managed_path; do
493
- [[ "$managed_path" == "$HOME/.config/"* ]] || continue
494
- [[ -f "$managed_path" ]] || continue
495
- grep -Iq . "$managed_path" 2>/dev/null || continue
496
-
497
- match_count=$(
498
- grep -nE '"/home/(linuxbrew)?[^$]|/Users/[^$]' "$managed_path" 2>/dev/null |
499
- grep -v "linuxbrew" |
500
- grep -v "/mozilla/firefox" |
501
- grep -v "/google-chrome" |
502
- grep -v "/chromium" |
503
- grep -v "/chezmoi/chezmoi.toml" |
504
- grep -v "/bun/" |
505
- grep -v "/.bun/" |
506
- grep -v "/noctalia/" |
507
- grep -c -v -- "-backup/" ||
508
- true
509
- )
510
-
511
- ghost_paths=$((ghost_paths + ${match_count:-0}))
512
- done < <(chezmoi managed --path-style=absolute 2>/dev/null || true)
513
-
514
- if [[ $ghost_paths -gt 0 ]]; then
515
- _warn "portability" "$ghost_paths hardcoded paths in managed ~/.config files"
516
- else
517
- _ok "portability" "no hardcoded paths in managed ~/.config files"
518
- fi
519
- else
520
- _warn "portability" "chezmoi not found (scan skipped)"
521
- fi
522
-
523
- # --- Performance ---
524
- _section "Performance"
525
-
526
- cache_base="${XDG_CACHE_HOME:-$HOME/.cache}"
527
-
528
- # 1. Shell cache freshness for tools the project already wraps in _cached_eval.
529
- # Stale caches force runtime regeneration on next shell start.
530
- stale_caches=0
531
- stale_tools=""
532
- for tool in mise starship zoxide atuin fzf direnv; do
533
- tool_bin="$(command -v "$tool" 2>/dev/null || true)"
534
- [[ -n "$tool_bin" ]] || continue
535
- for shell_dir in zsh bash fish; do
536
- case "$shell_dir" in
537
- fish) cache_file="$cache_base/$shell_dir/${tool}-init.fish" ;;
538
- *) cache_file="$cache_base/$shell_dir/${tool}-init.$shell_dir" ;;
539
- esac
540
- if [[ ! -f "$cache_file" ]] || [[ "$tool_bin" -nt "$cache_file" ]]; then
541
- stale_caches=$((stale_caches + 1))
542
- case "$stale_tools" in
543
- *"$tool"*) ;;
544
- *) stale_tools="${stale_tools:+$stale_tools, }$tool" ;;
545
- esac
546
- break
547
- fi
548
- done
549
- done
550
- if [[ $stale_caches -eq 0 ]]; then
551
- _ok "shell caches" "fresh"
552
- else
553
- _warn "shell caches" "stale ($stale_tools) — run dot prewarm"
554
- fi
555
-
556
- # 2. Slow-init tools that are present but NOT wrapped in _cached_eval.
557
- # Each of these runs uncached on every shell start; common offenders eat
558
- # 100-500ms apiece on a populated dev machine.
559
- #
560
- # Only flag tools that actually emit shell init via `<tool> init <shell>`
561
- # (or equivalent) and would benefit from caching that output. Plain CLIs
562
- # like gh/cargo/pnpm/yarn don't have init eval; their completions are
563
- # cached separately under $ZSH_COMPLETIONS_DIR.
564
- unwrapped=""
565
- for tool in nvm fnm pyenv rbenv jenv asdf sdkman conda kubectl helm thefuck broot mcfly direnv; do
566
- command -v "$tool" >/dev/null 2>&1 || continue
567
- # If the tool is installed but no shell config sources or evals its
568
- # init (no `$tool env`, `$tool init`, `$tool.sh`, lazy-load stub), it
569
- # isn't adding startup cost — skip the warning.
570
- init_referenced=0
571
- if grep -rIlqE "\\b${tool}([[:space:]]+(env|init|hook)|\\.sh|_lazy_load_${tool}|_dot_lazy[[:space:]]+${tool})" \
572
- "$HOME/.config/zsh" "$HOME/.config/fish" "$HOME/.config/shell" 2>/dev/null; then
573
- init_referenced=1
574
- fi
575
- ((init_referenced == 0)) && continue
576
- # Tools we lazy-load via shell stubs don't need init-eval cache files.
577
- case "$tool" in
578
- fnm | nvm | sdkman)
579
- grep -rIlqE "_lazy_load_${tool}|_dot_lazy[[:space:]]+${tool}" \
580
- "$HOME/.config/zsh" "$HOME/.config/fish" 2>/dev/null && continue
581
- ;;
582
- esac
583
- found=0
584
- for shell_dir in zsh bash fish; do
585
- case "$shell_dir" in
586
- fish) [[ -f "$cache_base/$shell_dir/${tool}-init.fish" ]] && found=1 ;;
587
- *) [[ -f "$cache_base/$shell_dir/${tool}-init.$shell_dir" ]] && found=1 ;;
588
- esac
589
- ((found == 1)) && break
590
- done
591
- ((found == 0)) && unwrapped="${unwrapped:+$unwrapped, }$tool"
592
- done
593
- if [[ -z "$unwrapped" ]]; then
594
- _ok "uncached slow-init tools" "none detected"
595
- else
596
- _warn "uncached slow-init tools" "$unwrapped — consider wrapping in _cached_eval"
597
- fi
598
-
599
- # 3. Zsh completion dump health. compinit is usually the single biggest
600
- # cost on a zsh startup; a stale or uncompiled .zcompdump compounds it.
601
- if command -v zsh >/dev/null 2>&1; then
602
- zcompdump="${HOME}/.zcompdump"
603
- if [[ -f "$zcompdump" ]]; then
604
- dump_mtime=$(stat -c %Y "$zcompdump" 2>/dev/null || stat -f %m "$zcompdump" 2>/dev/null || echo 0)
605
- age_days=$((($(date +%s) - dump_mtime) / 86400))
606
- if ((age_days > 7)); then
607
- _warn ".zcompdump" "${age_days}d old — refresh: rm ~/.zcompdump* && zsh -ic exit"
608
- else
609
- _ok ".zcompdump" "fresh (${age_days}d)"
610
- fi
611
- if [[ ! -f "${zcompdump}.zwc" ]]; then
612
- _warn ".zcompdump.zwc" "missing — completion init slower than necessary"
613
- fi
614
- fi
615
- fi
616
-
617
- # 4. PATH length. Each entry is searched on every command resolution.
618
- # A mise-managed 2026 dev machine routinely adds 60-90 entries (one per
619
- # installed tool version + per-shim path), so the warn/fail thresholds
620
- # reflect that baseline rather than a lean default (~40).
621
- path_count=$(printf '%s' "${PATH:-}" | tr ':' '\n' | grep -c . || true)
622
- if [[ "$path_count" -le 75 ]]; then
623
- _ok "PATH length" "$path_count entries"
624
- elif [[ "$path_count" -le 120 ]]; then
625
- _warn "PATH length" "$path_count entries — consider pruning"
626
- else
627
- _fail "PATH length" "$path_count entries — likely slowing every command"
628
- fi
629
-
630
- # 5. Shell coverage. Surface installed shells that the project's caching
631
- # infrastructure doesn't currently maintain caches for.
632
- shells_unmanaged=""
633
- for sh in nu pwsh; do
634
- command -v "$sh" >/dev/null 2>&1 || continue
635
- case "$sh" in
636
- nu)
637
- [[ -f "$HOME/.config/nushell/cached_eval.nu" ]] && continue
638
- ;;
639
- pwsh)
640
- pwsh -NoLogo -NoProfile -NonInteractive -Command 'exit 0' >/dev/null 2>&1 || continue
641
- pwsh_profile="$HOME/.config/powershell/Microsoft.PowerShell_profile.ps1"
642
- [[ -f "$pwsh_profile" ]] && grep -q 'Get-DotfilesCachedInit' "$pwsh_profile" && continue
643
- ;;
644
- esac
645
- shells_unmanaged="${shells_unmanaged:+$shells_unmanaged, }$sh"
646
- done
647
- if [[ -z "$shells_unmanaged" ]]; then
648
- _ok "shell coverage" "all installed shells have _cached_eval support"
649
- else
650
- _warn "shell coverage" "$shells_unmanaged installed — no _cached_eval helper"
651
- fi
652
-
653
- # 6. Zsh hook count. Heavy precmd/preexec functions compound per-prompt.
654
- # Probe an interactive zsh with a hard timeout so a broken zshrc doesn't
655
- # stall doctor; skip cleanly if the probe fails.
656
- if command -v zsh >/dev/null 2>&1 && command -v timeout >/dev/null 2>&1; then
657
- hook_counts=$(timeout 5 zsh -i -c 'echo "$#precmd_functions $#preexec_functions"' 2>/dev/null || echo "")
658
- if [[ -n "$hook_counts" ]]; then
659
- read -r precmd_n preexec_n <<<"$hook_counts"
660
- if [[ "${precmd_n:-0}" -le 5 && "${preexec_n:-0}" -le 5 ]]; then
661
- _ok "zsh hooks" "precmd=$precmd_n preexec=$preexec_n"
662
- else
663
- _warn "zsh hooks" "precmd=$precmd_n preexec=$preexec_n — heavy per-prompt work"
664
- fi
665
- fi
666
- fi
667
-
668
- if command -v hyperfine >/dev/null 2>&1; then
669
- if bash "$SCRIPT_DIR/../../tests/performance/bench.sh" 2>/dev/null; then
670
- _ok "startup latency" "within target thresholds"
671
- else
672
- _warn "startup latency" "threshold exceeded (run dot prewarm)"
673
- fi
674
- else
675
- _warn "hyperfine" "missing (benchmark skipped)"
676
- fi
677
-
678
- # 7. Baseline check + top-3 slowest tools from EVALCACHE_TIMING.
679
- # Closes part of #863. Reads the same baseline file `dot perf` writes,
680
- # and the same eval-timings.jsonl _cached_eval populates. Skipped
681
- # silently when either file is absent (first-run state).
682
- baseline_file="$cache_base/dotfiles/perf-baseline.json"
683
- if [[ -s "$baseline_file" ]] && command -v python3 >/dev/null 2>&1; then
684
- baseline_age_days=$(python3 -c '
685
- import json, sys, datetime
686
- try:
687
- d = json.load(open(sys.argv[1]))
688
- rec = d.get("recorded_at", "")
689
- if not rec: print(-1); sys.exit(0)
690
- rec = rec.replace("Z", "+00:00")
691
- age = (datetime.datetime.now(datetime.timezone.utc) - datetime.datetime.fromisoformat(rec)).days
692
- print(age)
693
- except Exception:
694
- print(-1)
695
- ' "$baseline_file" 2>/dev/null)
696
- if [[ "$baseline_age_days" -ge 0 ]]; then
697
- _ok "perf baseline" "recorded ${baseline_age_days}d ago — run \`dot perf\` to compare"
698
- fi
699
- fi
700
-
701
- timings_file="${XDG_STATE_HOME:-$HOME/.local/state}/dotfiles/eval-timings.jsonl"
702
- if [[ -s "$timings_file" ]] && command -v python3 >/dev/null 2>&1; then
703
- top_tools=$(python3 -c '
704
- import json, sys
705
- from collections import defaultdict
706
- samples = defaultdict(list)
707
- try:
708
- for line in open(sys.argv[1]):
709
- try:
710
- ev = json.loads(line)
711
- ms = int(ev.get("ms", 0) or 0)
712
- samples[ev.get("label", "?")].append(ms)
713
- except Exception:
714
- pass
715
- rows = sorted(samples.items(), key=lambda kv: sum(kv[1]) // max(len(kv[1]), 1), reverse=True)[:3]
716
- print(", ".join(f"{lbl}({sum(v)//max(len(v),1)}ms)" for lbl, v in rows))
717
- except Exception:
718
- pass
719
- ' "$timings_file" 2>/dev/null)
720
- if [[ -n "$top_tools" ]]; then
721
- _ok "perf top-tools" "$top_tools"
722
- fi
723
- fi
724
-
725
- # --- Summary ---
726
- dot_log info "doctor_complete" "errors=$Errors" "warnings=$Warnings"
727
- dot_metric "doctor_errors" "$Errors" "count"
728
- dot_metric "doctor_warnings" "$Warnings" "count"
729
- echo ""
730
- if [[ $Errors -eq 0 ]]; then
731
- if [[ $Warnings -eq 0 ]]; then
732
- ui_ok "Healthy" "All checks passed."
733
- else
734
- ui_ok "Healthy" "$Warnings warning(s)."
735
- fi
736
- else
737
- ui_err "$Errors error(s)" "$Warnings warning(s). Run 'dot heal' to repair."
738
-
739
- if [[ $AI_DEBUG -eq 1 ]]; then
740
- _section "AI Problem Analysis"
741
- doctor_report=$(~/.local/bin/dot doctor | grep -E "(✗|⚠)")
742
- ai_prompt="The dotfiles diagnostic 'dot doctor' found the following issues:
743
- ---
744
- $doctor_report
745
- ---
746
- Suggest specific shell commands to fix these issues according to our architectural standards."
747
- dot ai claude --style hardener "$ai_prompt"
748
- fi
749
- exit 1
750
- fi
751
- echo ""