@sebastienrousseau/dotfiles 0.2.520 → 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 (323) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/LICENSE-APACHE +190 -0
  3. package/LICENSE-MIT +21 -0
  4. package/README.md +43 -37
  5. package/install.sh +76 -10
  6. package/package.json +7 -7
  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/ARCHITECTURE.md +0 -117
  12. package/docs/CNAME +0 -1
  13. package/docs/CONFIG_STRATEGY.md +0 -124
  14. package/docs/COPYRIGHT +0 -7
  15. package/docs/ECOSYSTEM.md +0 -220
  16. package/docs/GOLD-STANDARD-AUDIT.md +0 -352
  17. package/docs/GOVERNANCE.md +0 -98
  18. package/docs/MAINTAINERS.md +0 -41
  19. package/docs/MINIMUM-TOOLCHAIN.md +0 -100
  20. package/docs/NAMING_CONVENTIONS.md +0 -102
  21. package/docs/OPENCODE.md +0 -127
  22. package/docs/README.md +0 -84
  23. package/docs/STRUCTURE.md +0 -102
  24. package/docs/adr/ADR-001-ci-cd-pipeline.md +0 -118
  25. package/docs/adr/ADR-002-shell-performance.md +0 -130
  26. package/docs/adr/ADR-003-security-first.md +0 -158
  27. package/docs/adr/ADR-004-cli-architecture.md +0 -171
  28. package/docs/adr/ADR-005-chezmoi-choice.md +0 -99
  29. package/docs/adr/ADR-006-shell-selection.md +0 -124
  30. package/docs/adr/ADR-007-multi-shell-parity.md +0 -62
  31. package/docs/adr/ADR-008-alias-system-architecture.md +0 -95
  32. package/docs/adr/ADR-009-wallpaper-driven-theming.md +0 -131
  33. package/docs/adr/ADR-010-starship-transient-prompt.md +0 -144
  34. package/docs/adr/ADR-011-nushell-tier3-keep.md +0 -144
  35. package/docs/adr/ADR-012-ai-fleet-local-proxy.md +0 -79
  36. package/docs/adr/README.md +0 -40
  37. package/docs/architecture/AI_COST_OPTIMIZATION.md +0 -144
  38. package/docs/architecture/ARCHITECTURE.md +0 -20
  39. package/docs/architecture/INTEROP.md +0 -44
  40. package/docs/architecture/REPO_LAYOUT.md +0 -241
  41. package/docs/architecture/WALKTHROUGH.md +0 -86
  42. package/docs/architecture/fleet-deployment.md +0 -77
  43. package/docs/archive/EUXIS_2026_REVIEW.md +0 -127
  44. package/docs/archive/LEGACY_ROADMAP.md +0 -6
  45. package/docs/archive/MILESTONE_v0.2.493.md +0 -47
  46. package/docs/archive/PLAN.md +0 -199
  47. package/docs/archive/REPO_AUDIT.md +0 -31
  48. package/docs/articles/.pages +0 -6
  49. package/docs/articles/2026-07-05-custom-mkdocs-material-dark-theme.md +0 -216
  50. package/docs/articles/2026-07-05-fish-startup-abbr.md +0 -153
  51. package/docs/articles/2026-07-05-master-to-main-rename-runbook.md +0 -128
  52. package/docs/articles/index.md +0 -36
  53. package/docs/guides/INSTALL.md +0 -144
  54. package/docs/guides/MACOS_ICLOUD_SYMLINKS.md +0 -121
  55. package/docs/guides/NEOVIM_IDE_GUIDE.md +0 -61
  56. package/docs/guides/THEMING.md +0 -230
  57. package/docs/guides/TROUBLESHOOTING.md +0 -176
  58. package/docs/guides/WSL2_NIX_TROUBLESHOOTING.md +0 -792
  59. package/docs/index.md +0 -132
  60. package/docs/interop/A2A.md +0 -39
  61. package/docs/interop/POWERSHELL.md +0 -102
  62. package/docs/manual/00-introduction.md +0 -89
  63. package/docs/manual/01-concepts/01-architecture.md +0 -138
  64. package/docs/manual/01-concepts/02-trust-model.md +0 -183
  65. package/docs/manual/01-concepts/03-theme-engine.md +0 -186
  66. package/docs/manual/01-concepts/04-fleet.md +0 -148
  67. package/docs/manual/01-concepts/05-self-healing.md +0 -204
  68. package/docs/manual/02-tutorials/01-first-install.md +0 -197
  69. package/docs/manual/02-tutorials/02-add-wallpaper.md +0 -216
  70. package/docs/manual/02-tutorials/03-create-profile.md +0 -244
  71. package/docs/manual/02-tutorials/04-encrypt-secret.md +0 -281
  72. package/docs/manual/02-tutorials/05-deploy-fleet.md +0 -283
  73. package/docs/manual/03-reference/01-dot-cli.md +0 -475
  74. package/docs/manual/03-reference/02-config-files.md +0 -265
  75. package/docs/manual/03-reference/03-environment.md +0 -124
  76. package/docs/manual/03-reference/04-templates.md +0 -190
  77. package/docs/manual/03-reference/05-feature-flags.md +0 -187
  78. package/docs/manual/04-cookbook/01-recipes.md +0 -285
  79. package/docs/manual/04-cookbook/02-troubleshooting.md +0 -351
  80. package/docs/manual/04-cookbook/03-faq.md +0 -175
  81. package/docs/manual/05-appendices/A-platform-matrix.md +0 -101
  82. package/docs/manual/05-appendices/B-security-checklist.md +0 -85
  83. package/docs/manual/05-appendices/C-glossary.md +0 -40
  84. package/docs/manual/05-appendices/D-bibliography.md +0 -58
  85. package/docs/manual/05-appendices/E-license.md +0 -38
  86. package/docs/manual/_toc.yml +0 -58
  87. package/docs/manual/command-index.md +0 -175
  88. package/docs/manual/concept-index.md +0 -170
  89. package/docs/manual/index.md +0 -66
  90. package/docs/migration/README.md +0 -81
  91. package/docs/migration/from-bare-git-repo.md +0 -156
  92. package/docs/migration/from-gnu-stow.md +0 -165
  93. package/docs/migration/from-plain-chezmoi.md +0 -148
  94. package/docs/migration/from-yadm.md +0 -187
  95. package/docs/operations/ARCHITECTURE_ROADMAP.md +0 -7
  96. package/docs/operations/ATTESTATION.md +0 -44
  97. package/docs/operations/CI_CADENCE.md +0 -107
  98. package/docs/operations/CI_COMPOSITES.md +0 -156
  99. package/docs/operations/COMPLETIONS.md +0 -123
  100. package/docs/operations/COVERAGE.md +0 -204
  101. package/docs/operations/DRIFT.md +0 -107
  102. package/docs/operations/HARD_AUDIT_2026.md +0 -631
  103. package/docs/operations/MAINTENANCE.md +0 -63
  104. package/docs/operations/MANIFEST.md +0 -127
  105. package/docs/operations/MIGRATION.md +0 -109
  106. package/docs/operations/OPERATIONS.md +0 -188
  107. package/docs/operations/PERFORMANCE.md +0 -133
  108. package/docs/operations/PERFORMANCE_BUDGETS.md +0 -196
  109. package/docs/operations/REGISTRY.md +0 -90
  110. package/docs/operations/RELEASE_PIPELINE.md +0 -128
  111. package/docs/operations/RELIABILITY.md +0 -122
  112. package/docs/operations/RFC_v0_2_503_reorganization.md +0 -280
  113. package/docs/operations/ROADMAP.md +0 -10
  114. package/docs/operations/ROADMAP_2026.md +0 -7
  115. package/docs/operations/ROADMAP_V0_2_503.md +0 -10
  116. package/docs/operations/TESTING.md +0 -216
  117. package/docs/operations/TRACEABILITY.md +0 -44
  118. package/docs/operations/TRUSTED_AGENT_WORKSTATION.md +0 -65
  119. package/docs/operations/VERSION_SYNC.md +0 -393
  120. package/docs/packaging.md +0 -222
  121. package/docs/reference/ALIASES.md +0 -131
  122. package/docs/reference/ALIASES_CHEATSHEET.md +0 -32
  123. package/docs/reference/ALIASES_DEPRECATIONS.md +0 -13
  124. package/docs/reference/FEATURE-MATRIX.md +0 -646
  125. package/docs/reference/FEATURES.md +0 -66
  126. package/docs/reference/FONTS.md +0 -112
  127. package/docs/reference/POWERSHELL_PARITY.md +0 -82
  128. package/docs/reference/PROFILES.md +0 -69
  129. package/docs/reference/SCREENSHOTS.md +0 -121
  130. package/docs/reference/SCRIPTS.md +0 -71
  131. package/docs/reference/SUPPORT_MATRIX.md +0 -80
  132. package/docs/reference/THEMES.md +0 -117
  133. package/docs/reference/TOOLS.md +0 -110
  134. package/docs/reference/UTILS.md +0 -243
  135. package/docs/registry.json +0 -6
  136. package/docs/schema/dot-env-v1.json +0 -110
  137. package/docs/schema/dot-registry-v1.json +0 -33
  138. package/docs/security/AI_ACT_COMPLIANCE.md +0 -94
  139. package/docs/security/AUDIT_BYPASS.md +0 -103
  140. package/docs/security/AUTOMATION_SECRETS.md +0 -26
  141. package/docs/security/CI_EGRESS_ALLOWLIST.md +0 -127
  142. package/docs/security/CI_PINNING.md +0 -129
  143. package/docs/security/COMMIT_SIGNING.md +0 -138
  144. package/docs/security/COMPLIANCE.md +0 -458
  145. package/docs/security/DEPS_DEV_EXCEPTIONS.md +0 -86
  146. package/docs/security/DISCLOSURE.md +0 -130
  147. package/docs/security/ENCRYPTION.md +0 -57
  148. package/docs/security/FMEA.md +0 -159
  149. package/docs/security/FUZZING.md +0 -209
  150. package/docs/security/HISTORY_FILTERING.md +0 -132
  151. package/docs/security/INCIDENT_RESPONSE.md +0 -579
  152. package/docs/security/INSTALL_VERIFICATION.md +0 -122
  153. package/docs/security/KEYS.md +0 -49
  154. package/docs/security/KEY_ROTATION.md +0 -303
  155. package/docs/security/MCP_POLICY.md +0 -78
  156. package/docs/security/POLICY_RELEASES.md +0 -37
  157. package/docs/security/README.md +0 -28
  158. package/docs/security/SCORECARD.md +0 -195
  159. package/docs/security/SECRETS.md +0 -158
  160. package/docs/security/SECURITY.md +0 -45
  161. package/docs/security/SECURITY_CHECKLIST.md +0 -55
  162. package/docs/security/SHELL_EXEMPTIONS.md +0 -145
  163. package/docs/security/SOUP_REGISTER.md +0 -36
  164. package/docs/security/THREAT_MODEL.md +0 -130
  165. package/docs/security/VERIFICATION_VALIDATION.md +0 -228
  166. package/docs/security/VERIFY_RELEASE.md +0 -201
  167. package/docs/security/security-pubkey.asc +0 -15
  168. package/docs/stylesheets/extra.css +0 -444
  169. package/docs/themes/README.md +0 -10
  170. package/docs/themes/VISUAL_INTEGRITY_REPORT.md +0 -30
  171. package/docs/themes/hero-shot.svg +0 -78
  172. package/scripts/README.md +0 -123
  173. package/scripts/ci/check-copyright-headers.sh +0 -8
  174. package/scripts/ci/check-shell-preamble.sh +0 -8
  175. package/scripts/ci/guard-gitleaks-checkout.sh +0 -8
  176. package/scripts/demo/record.sh +0 -43
  177. package/scripts/diagnostics/a2a-conformance.sh +0 -163
  178. package/scripts/diagnostics/alias-governance.sh +0 -165
  179. package/scripts/diagnostics/aliases-cheatsheet.sh +0 -74
  180. package/scripts/diagnostics/aliases-manifest.sh +0 -77
  181. package/scripts/diagnostics/attest-verify.sh +0 -147
  182. package/scripts/diagnostics/benchmark.sh +0 -408
  183. package/scripts/diagnostics/conflicts.sh +0 -73
  184. package/scripts/diagnostics/doctor-unified.sh +0 -43
  185. package/scripts/diagnostics/doctor.sh +0 -797
  186. package/scripts/diagnostics/drift-dashboard.sh +0 -203
  187. package/scripts/diagnostics/health.sh +0 -656
  188. package/scripts/diagnostics/history-analysis.sh +0 -86
  189. package/scripts/diagnostics/mcp-doctor.sh +0 -582
  190. package/scripts/diagnostics/perf.sh +0 -453
  191. package/scripts/diagnostics/scorecard.sh +0 -120
  192. package/scripts/diagnostics/secret-governance.sh +0 -65
  193. package/scripts/diagnostics/security-score.sh +0 -467
  194. package/scripts/diagnostics/smoke-test.sh +0 -88
  195. package/scripts/diagnostics/snapshot.sh +0 -90
  196. package/scripts/diagnostics/verify.sh +0 -108
  197. package/scripts/diagnostics/verify_state.sh +0 -73
  198. package/scripts/diagnostics/version-locks.sh +0 -94
  199. package/scripts/diagnostics/workstation-attestation.sh +0 -212
  200. package/scripts/dot/commands/agent.sh +0 -535
  201. package/scripts/dot/commands/agents.sh +0 -352
  202. package/scripts/dot/commands/ai.sh +0 -600
  203. package/scripts/dot/commands/aliases.sh +0 -277
  204. package/scripts/dot/commands/appearance.sh +0 -110
  205. package/scripts/dot/commands/completion.sh +0 -171
  206. package/scripts/dot/commands/core.sh +0 -217
  207. package/scripts/dot/commands/diagnostics.sh +0 -265
  208. package/scripts/dot/commands/env-emit.sh +0 -203
  209. package/scripts/dot/commands/fleet.sh +0 -711
  210. package/scripts/dot/commands/init.sh +0 -185
  211. package/scripts/dot/commands/lint.sh +0 -208
  212. package/scripts/dot/commands/manual.sh +0 -169
  213. package/scripts/dot/commands/meta.sh +0 -438
  214. package/scripts/dot/commands/patterns.sh +0 -55
  215. package/scripts/dot/commands/registry.sh +0 -455
  216. package/scripts/dot/commands/restore.sh +0 -232
  217. package/scripts/dot/commands/secrets.sh +0 -296
  218. package/scripts/dot/commands/security.sh +0 -102
  219. package/scripts/dot/commands/tools.sh +0 -570
  220. package/scripts/dot/data/alias-deprecations.tsv +0 -2
  221. package/scripts/dot/powershell/Dot.psm1 +0 -319
  222. package/scripts/fonts/install-nerd-fonts.sh +0 -75
  223. package/scripts/fonts/patch-fonts.sh +0 -36
  224. package/scripts/git-hooks/install.sh +0 -12
  225. package/scripts/git-hooks/pre-commit +0 -12
  226. package/scripts/git-hooks/pre-commit-audit.sh +0 -146
  227. package/scripts/git-hooks/pre-push +0 -105
  228. package/scripts/git-hooks/prepare-commit-msg +0 -29
  229. package/scripts/lib/secrets_provider.sh +0 -200
  230. package/scripts/nvim/headless-upgrade.lua +0 -81
  231. package/scripts/ops/ai-setup.sh +0 -71
  232. package/scripts/ops/bundle.sh +0 -104
  233. package/scripts/ops/chaos.sh +0 -50
  234. package/scripts/ops/chezmoi-apply.sh +0 -333
  235. package/scripts/ops/chezmoi-diff.sh +0 -16
  236. package/scripts/ops/chezmoi-remove.sh +0 -46
  237. package/scripts/ops/chezmoi-update.sh +0 -67
  238. package/scripts/ops/heal-chezmoi.sh +0 -87
  239. package/scripts/ops/heal-system.sh +0 -129
  240. package/scripts/ops/heal-tools.sh +0 -297
  241. package/scripts/ops/heal.sh +0 -223
  242. package/scripts/ops/post-apply-repair.sh +0 -107
  243. package/scripts/ops/prewarm.sh +0 -128
  244. package/scripts/ops/release.sh +0 -262
  245. package/scripts/ops/rollback.sh +0 -613
  246. package/scripts/ops/setup.sh +0 -138
  247. package/scripts/ops/teleport.sh +0 -34
  248. package/scripts/qa/check-feature-matrix.sh +0 -296
  249. package/scripts/qa/check-version-consistency.sh +0 -12
  250. package/scripts/qa/coverage-baseline.sh +0 -61
  251. package/scripts/qa/docs-coverage.sh +0 -118
  252. package/scripts/qa/examples-coverage.sh +0 -94
  253. package/scripts/qa/powershell-contract.ps1 +0 -95
  254. package/scripts/qa/reliability-audit.sh +0 -139
  255. package/scripts/qa/scorecard-snapshot.sh +0 -128
  256. package/scripts/qa/traceability-coverage.sh +0 -124
  257. package/scripts/qa/validate-examples.sh +0 -90
  258. package/scripts/qa/wsl-contract.sh +0 -12
  259. package/scripts/secrets/age-init.sh +0 -82
  260. package/scripts/secrets/create-secrets-file.sh +0 -46
  261. package/scripts/secrets/encrypt-ssh-key.sh +0 -44
  262. package/scripts/security/backup.sh +0 -58
  263. package/scripts/security/check-disclosure-key-expiry.sh +0 -111
  264. package/scripts/security/dns-doh.sh +0 -52
  265. package/scripts/security/encryption-check.sh +0 -55
  266. package/scripts/security/enforce-policies.sh +0 -552
  267. package/scripts/security/firewall.sh +0 -91
  268. package/scripts/security/lock-configs.sh +0 -67
  269. package/scripts/security/lock-screen.sh +0 -56
  270. package/scripts/security/manage-secrets.sh +0 -429
  271. package/scripts/security/ssh-cert.sh +0 -204
  272. package/scripts/security/telemetry-kill.sh +0 -51
  273. package/scripts/security/usb-safety.sh +0 -52
  274. package/scripts/theme/apply-gnome-theme.sh +0 -333
  275. package/scripts/theme/extract-heic-frames.sh +0 -115
  276. package/scripts/theme/extract-theme.py +0 -1020
  277. package/scripts/theme/install-boot-logo.sh +0 -63
  278. package/scripts/theme/install-catppuccin-themes.sh +0 -371
  279. package/scripts/theme/install-cursors.sh +0 -26
  280. package/scripts/theme/install-file-icons.sh +0 -27
  281. package/scripts/theme/install-grub-theme.sh +0 -62
  282. package/scripts/theme/install-lock-icon.sh +0 -31
  283. package/scripts/theme/merge-wallpaper.sh +0 -146
  284. package/scripts/theme/rebuild-themes.sh +0 -603
  285. package/scripts/theme/switch.sh +0 -476
  286. package/scripts/theme/wallpaper-rotate.sh +0 -137
  287. package/scripts/theme/wallpaper-sync.sh +0 -690
  288. package/scripts/tools/cmatrix.sh +0 -22
  289. package/scripts/tools/detect-collisions.py +0 -103
  290. package/scripts/tools/emoji-picker.sh +0 -49
  291. package/scripts/tools/figlet-banner.sh +0 -19
  292. package/scripts/tools/log-rotate.sh +0 -31
  293. package/scripts/tools/lolcat-wrap.sh +0 -20
  294. package/scripts/tools/pipes.sh +0 -49
  295. package/scripts/tuning/linux.sh +0 -186
  296. package/scripts/tuning/macos.sh +0 -56
  297. package/scripts/uninstall.sh +0 -86
  298. package/scripts/verify-release-versions +0 -156
  299. package/scripts/version-sync.sh +0 -714
  300. package/templates/chezmoi-data/geekom-a9.toml.example +0 -21
  301. package/templates/chezmoi-data/mac-m1.toml.example +0 -16
  302. package/templates/chezmoi-data/mac-t2-linux.toml.example +0 -21
  303. package/templates/chezmoi-data/surface-pro-7p.toml.example +0 -21
  304. package/templates/projects/go/.github/workflows/ci.yml +0 -31
  305. package/templates/projects/go/README.md +0 -7
  306. package/templates/projects/go/cmd/__PROJECT_NAME__/main.go +0 -8
  307. package/templates/projects/go/go.mod +0 -3
  308. package/templates/projects/go/go.sum +0 -0
  309. package/templates/projects/molecule/README.md +0 -7
  310. package/templates/projects/molecule/converge.yml +0 -7
  311. package/templates/projects/molecule/molecule.yml +0 -16
  312. package/templates/projects/node/.github/workflows/ci.yml +0 -30
  313. package/templates/projects/node/README.md +0 -7
  314. package/templates/projects/node/package-lock.json +0 -12
  315. package/templates/projects/node/package.json +0 -10
  316. package/templates/projects/node/src/index.js +0 -3
  317. package/templates/projects/packer/README.md +0 -15
  318. package/templates/projects/packer/main.pkr.hcl +0 -15
  319. package/templates/projects/python/.github/workflows/ci.yml +0 -34
  320. package/templates/projects/python/README.md +0 -7
  321. package/templates/projects/python/pyproject.toml +0 -25
  322. package/templates/projects/python/src/__PROJECT_NAME__/__init__.py +0 -2
  323. package/templates/projects/python/tests/test_basic.py +0 -3
@@ -1,86 +0,0 @@
1
- #!/usr/bin/env bash
2
- # SPDX-License-Identifier: Apache-2.0 OR MIT
3
- # Copyright (c) 2015-2026 Sebastien Rousseau
4
- set -euo pipefail
5
-
6
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
- # shellcheck source=../../lib/dot/ui.sh
8
- # shellcheck disable=SC1091
9
- source "$SCRIPT_DIR/../../lib/dot/ui.sh"
10
-
11
- histfile="${HISTFILE:-$HOME/.zsh_history}"
12
- db="${DOTFILES_HISTORY_DB:-$HOME/.local/share/dotfiles/history.sqlite}"
13
-
14
- ui_init
15
-
16
- if [[ ! -f "$histfile" ]]; then
17
- ui_err "History file" "not found: $histfile" >&2
18
- exit 1
19
- fi
20
-
21
- if ! command -v python3 >/dev/null; then
22
- ui_warn "python3" "not found; falling back to hstats if available" >&2
23
- if command -v hstats >/dev/null; then
24
- hstats
25
- exit 0
26
- fi
27
- exit 1
28
- fi
29
-
30
- mkdir -p "$(dirname "$db")"
31
-
32
- ui_header "History Analysis"
33
-
34
- python3 - "$histfile" "$db" <<'PY'
35
- import sys
36
- import sqlite3
37
- import re
38
- from pathlib import Path
39
-
40
- histfile = Path(sys.argv[1])
41
- db = Path(sys.argv[2])
42
-
43
- conn = sqlite3.connect(db)
44
- cur = conn.cursor()
45
- cur.execute("CREATE TABLE IF NOT EXISTS history (ts INTEGER, cmd TEXT)")
46
- cur.execute("DELETE FROM history")
47
-
48
- pattern = re.compile(r"^: (\d+):\d+;(.*)$")
49
-
50
- with histfile.open("r", encoding="utf-8", errors="ignore") as f:
51
- for line in f:
52
- line = line.rstrip("\n")
53
- m = pattern.match(line)
54
- if not m:
55
- continue
56
- ts = int(m.group(1))
57
- cmd = m.group(2).strip()
58
- if not cmd:
59
- continue
60
- cur.execute("INSERT INTO history (ts, cmd) VALUES (?, ?)", (ts, cmd))
61
-
62
- conn.commit()
63
-
64
- print("\nTop commands:")
65
- for row in cur.execute("""
66
- SELECT substr(cmd, 1, instr(cmd || ' ', ' ') - 1) AS base, COUNT(*)
67
- FROM history
68
- GROUP BY base
69
- ORDER BY COUNT(*) DESC
70
- LIMIT 15
71
- """):
72
- print(f"{row[1]:>6} {row[0]}")
73
-
74
- print("\nTop directories (cd):")
75
- for row in cur.execute("""
76
- SELECT trim(substr(cmd, 4)) AS dir, COUNT(*)
77
- FROM history
78
- WHERE cmd LIKE 'cd %'
79
- GROUP BY dir
80
- ORDER BY COUNT(*) DESC
81
- LIMIT 10
82
- """):
83
- print(f"{row[1]:>6} {row[0]}")
84
-
85
- conn.close()
86
- PY
@@ -1,582 +0,0 @@
1
- #!/usr/bin/env bash
2
- # SPDX-License-Identifier: Apache-2.0 OR MIT
3
- # Copyright (c) 2015-2026 Sebastien Rousseau
4
- ## MCP configuration diagnostics and hardening checks.
5
- ##
6
- ## Validates MCP (Model Context Protocol) server configurations for security
7
- ## policy compliance: launcher allowlist, filesystem scope, token requirements.
8
- ##
9
- ## # Usage
10
- ## dot mcp [--strict|-s] [--json|-j]
11
- ##
12
- ## # Options
13
- ## --strict, -s: Treat policy warnings as errors (for CI enforcement)
14
- ## --json, -j: Emit a machine-readable summary
15
- ##
16
- ## # Dependencies
17
- ## - jq: JSON parsing (optional but recommended)
18
- ##
19
- ## # Checks Performed
20
- ## | Check | Description |
21
- ## |-------|-------------|
22
- ## | Launcher policy | Only npx/node/uvx allowed |
23
- ## | Filesystem scope | No broad access (/, /home, /Users) |
24
- ## | Arg policy | No wildcards or --unsafe flags |
25
- ## | Token check | Required tokens set (GITHUB_TOKEN, BRAVE_API_KEY) |
26
- ## | Env placeholders | All ${VAR} references resolved |
27
- ##
28
- ## # Exit Codes
29
- ## - 0: All checks passed
30
- ## - 1: Errors found (or warnings in strict mode)
31
-
32
- set -euo pipefail
33
-
34
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
35
- # shellcheck source=../../lib/dot/ui.sh
36
- # shellcheck disable=SC1091
37
- source "$SCRIPT_DIR/../../lib/dot/ui.sh"
38
-
39
- # Parse arguments
40
- STRICT_MODE=0
41
- JSON_MODE=0
42
- while [[ $# -gt 0 ]]; do
43
- case "$1" in
44
- --strict | -s)
45
- STRICT_MODE=1
46
- shift
47
- ;;
48
- --json | -j)
49
- JSON_MODE=1
50
- shift
51
- ;;
52
- *) shift ;;
53
- esac
54
- done
55
-
56
- Errors=0
57
- Warnings=0
58
- SUMMARY_STATUS="healthy"
59
- CONFIG_OK=0
60
- JSON_VALID=0
61
- SERVER_COUNT=0
62
- POLICY_WARN_ON_UNPINNED_NPX=0
63
- REQUIRE_APPROVED_PACKAGE_LOCK=0
64
- REQUIRE_REGISTRY_ENTRY=0
65
- REQUIRE_HTTPS_FOR_HTTP=0
66
- REQUIRE_OAUTH_FOR_HTTP=0
67
- ALLOWED_LAUNCHERS='["npx","node","uvx"]'
68
- TRUSTED_TRANSPORTS='["stdio"]'
69
- BLOCKED_PATHS='["/","/home","/Users"]'
70
- BLOCKED_ARG_PATTERNS='["^--allow-.*","^--unsafe$","^\\\\*$"]'
71
- FORBIDDEN_DEFAULT_SERVERS='[]'
72
- REQUIRED_ENV_RULES='{}'
73
- APPROVED_PACKAGE_LOCK='{}'
74
- APPROVED_REGISTRY='{}'
75
-
76
- log_success() {
77
- if [[ "$JSON_MODE" -ne 1 ]]; then
78
- ui_ok "$1" "${2:-}"
79
- fi
80
- return 0
81
- }
82
- log_fail() {
83
- if [[ "$JSON_MODE" -ne 1 ]]; then
84
- ui_err "$1" "${2:-}"
85
- fi
86
- Errors=$((Errors + 1))
87
- return 0
88
- }
89
- log_warn() {
90
- if [[ "$JSON_MODE" -ne 1 ]]; then
91
- ui_warn "$1" "${2:-}"
92
- fi
93
- Warnings=$((Warnings + 1))
94
- # In strict mode, warnings become errors
95
- [[ "$STRICT_MODE" -eq 1 ]] && Errors=$((Errors + 1))
96
- return 0
97
- }
98
-
99
- MCP_CONFIG="${MCP_CONFIG:-$HOME/.config/claude/mcp_servers.json}"
100
- if [[ ! -f "$MCP_CONFIG" ]]; then
101
- for cand in "$HOME/.dotfiles/defaults/dot_config/claude/mcp_servers.json" \
102
- "$HOME/.dotfiles/dot_config/claude/mcp_servers.json"; do
103
- [[ -f "$cand" ]] && MCP_CONFIG="$cand" && break
104
- done
105
- fi
106
-
107
- REPO_ROOT="${REPO_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
108
- MCP_POLICY_CONFIG="${MCP_POLICY_CONFIG:-$REPO_ROOT/defaults/dot_config/dotfiles/mcp-policy.json}"
109
- MCP_LOCK_CONFIG="${MCP_LOCK_CONFIG:-$REPO_ROOT/defaults/dot_config/dotfiles/mcp-lock.json}"
110
- MCP_REGISTRY_CONFIG="${MCP_REGISTRY_CONFIG:-$REPO_ROOT/defaults/dot_config/dotfiles/mcp-registry.json}"
111
-
112
- if [[ "$JSON_MODE" -ne 1 ]]; then
113
- ui_init
114
- ui_dot_banner "AI and Agents"
115
- ui_header "MCP Doctor"
116
- echo ""
117
- fi
118
-
119
- if command -v jq >/dev/null 2>&1 && [[ -f "$MCP_POLICY_CONFIG" ]] && jq empty "$MCP_POLICY_CONFIG" >/dev/null 2>&1; then
120
- ALLOWED_LAUNCHERS="$(jq -c '.profiles[.defaultProfile].allowedLaunchers // ["npx","node","uvx"]' "$MCP_POLICY_CONFIG")"
121
- BLOCKED_PATHS="$(jq -c '.profiles[.defaultProfile].blockedFilesystemRoots // ["/","/home","/Users"]' "$MCP_POLICY_CONFIG")"
122
- BLOCKED_ARG_PATTERNS="$(jq -c '.profiles[.defaultProfile].blockedArgPatterns // ["^--allow-.*","^--unsafe$","^\\\\*$"]' "$MCP_POLICY_CONFIG")"
123
- FORBIDDEN_DEFAULT_SERVERS="$(jq -c '.profiles[.defaultProfile].forbidNetworkServersByDefault // []' "$MCP_POLICY_CONFIG")"
124
- REQUIRED_ENV_RULES="$(jq -c '.profiles[.defaultProfile].requiredEnvByServer // {}' "$MCP_POLICY_CONFIG")"
125
- TRUSTED_TRANSPORTS="$(jq -c '.profiles[.defaultProfile].trustedTransports // ["stdio"]' "$MCP_POLICY_CONFIG")"
126
- if [[ "$(jq -r '.profiles[.defaultProfile].warnOnUnpinnedNpx // false' "$MCP_POLICY_CONFIG")" == "true" ]]; then
127
- POLICY_WARN_ON_UNPINNED_NPX=1
128
- fi
129
- if [[ "$(jq -r '.profiles[.defaultProfile].requireApprovedPackageLock // false' "$MCP_POLICY_CONFIG")" == "true" ]]; then
130
- REQUIRE_APPROVED_PACKAGE_LOCK=1
131
- fi
132
- if [[ "$(jq -r '.profiles[.defaultProfile].requireRegistryEntry // false' "$MCP_POLICY_CONFIG")" == "true" ]]; then
133
- REQUIRE_REGISTRY_ENTRY=1
134
- fi
135
- if [[ "$(jq -r '.profiles[.defaultProfile].requireHttpsForHttpTransports // false' "$MCP_POLICY_CONFIG")" == "true" ]]; then
136
- REQUIRE_HTTPS_FOR_HTTP=1
137
- fi
138
- if [[ "$(jq -r '.profiles[.defaultProfile].requireOauthForHttpTransports // false' "$MCP_POLICY_CONFIG")" == "true" ]]; then
139
- REQUIRE_OAUTH_FOR_HTTP=1
140
- fi
141
- fi
142
-
143
- if command -v jq >/dev/null 2>&1 && [[ -f "$MCP_LOCK_CONFIG" ]] && jq empty "$MCP_LOCK_CONFIG" >/dev/null 2>&1; then
144
- APPROVED_PACKAGE_LOCK="$(jq -c '.packages // {}' "$MCP_LOCK_CONFIG")"
145
- fi
146
-
147
- if command -v jq >/dev/null 2>&1 && [[ -f "$MCP_REGISTRY_CONFIG" ]] && jq empty "$MCP_REGISTRY_CONFIG" >/dev/null 2>&1; then
148
- APPROVED_REGISTRY="$(jq -c '.servers // {}' "$MCP_REGISTRY_CONFIG")"
149
- fi
150
-
151
- [[ "$JSON_MODE" -ne 1 ]] && ui_header "Config File"
152
- if [[ -f "$MCP_CONFIG" ]]; then
153
- CONFIG_OK=1
154
- log_success "MCP config" "$MCP_CONFIG"
155
- else
156
- log_fail "MCP config" "not found (expected $MCP_CONFIG)"
157
- fi
158
-
159
- if [[ "$JSON_MODE" -ne 1 ]]; then
160
- echo ""
161
- ui_header "Policy"
162
- fi
163
- if [[ -f "$MCP_POLICY_CONFIG" ]]; then
164
- log_success "Policy config" "$MCP_POLICY_CONFIG"
165
- else
166
- log_warn "Policy config" "not found (using built-in defaults)"
167
- fi
168
-
169
- if [[ "$JSON_MODE" -ne 1 ]]; then
170
- echo ""
171
- ui_header "Supply Chain"
172
- fi
173
- if [[ -f "$MCP_LOCK_CONFIG" ]]; then
174
- log_success "Package lock" "$MCP_LOCK_CONFIG"
175
- else
176
- if [[ "$REQUIRE_APPROVED_PACKAGE_LOCK" -eq 1 ]]; then
177
- log_warn "Package lock" "not found (strict-local expects a tracked lock file)"
178
- else
179
- log_success "Package lock" "not required"
180
- fi
181
- fi
182
- if [[ -f "$MCP_REGISTRY_CONFIG" ]]; then
183
- log_success "Registry" "$MCP_REGISTRY_CONFIG"
184
- else
185
- if [[ "$REQUIRE_REGISTRY_ENTRY" -eq 1 ]]; then
186
- log_warn "Registry" "not found (strict-local expects a tracked registry file)"
187
- else
188
- log_success "Registry" "not required"
189
- fi
190
- fi
191
-
192
- MCP_SERVER_CARD="${MCP_SERVER_CARD:-$REPO_ROOT/.well-known/mcp/server-card.json}"
193
- if [[ "$JSON_MODE" -ne 1 ]]; then
194
- echo ""
195
- ui_header "Server Card (SEP-1649)"
196
- fi
197
- if [[ -f "$MCP_SERVER_CARD" ]]; then
198
- if command -v jq >/dev/null 2>&1 && jq empty "$MCP_SERVER_CARD" >/dev/null 2>&1; then
199
- log_success "Server card" "$MCP_SERVER_CARD"
200
- has_card_version="$(jq -r '.cardVersion // empty' "$MCP_SERVER_CARD")"
201
- has_card_name="$(jq -r '.name // empty' "$MCP_SERVER_CARD")"
202
- has_card_capabilities="$(jq -e '.capabilities' "$MCP_SERVER_CARD" >/dev/null 2>&1 && echo "yes" || echo "")"
203
- has_card_transport="$(jq -e '.transport' "$MCP_SERVER_CARD" >/dev/null 2>&1 && echo "yes" || echo "")"
204
- if [[ -n "$has_card_version" ]]; then
205
- log_success "Card version" "$has_card_version"
206
- else
207
- log_warn "Card version" "missing cardVersion field"
208
- fi
209
- if [[ -n "$has_card_name" ]]; then
210
- log_success "Card name" "$has_card_name"
211
- else
212
- log_warn "Card name" "missing name field"
213
- fi
214
- if [[ -n "$has_card_capabilities" ]]; then
215
- log_success "Card capabilities" "present"
216
- else
217
- log_warn "Card capabilities" "missing capabilities block"
218
- fi
219
- if [[ -n "$has_card_transport" ]]; then
220
- log_success "Card transport" "present"
221
- else
222
- log_warn "Card transport" "missing transport block"
223
- fi
224
- else
225
- log_fail "Server card" "invalid JSON"
226
- fi
227
- else
228
- log_warn "Server card" "not found (expected $MCP_SERVER_CARD)"
229
- fi
230
-
231
- if [[ "$JSON_MODE" -ne 1 ]]; then
232
- echo ""
233
- ui_header "Validation"
234
- fi
235
- if command -v jq >/dev/null 2>&1; then
236
- if [[ -f "$MCP_CONFIG" ]] && jq empty "$MCP_CONFIG" >/dev/null 2>&1; then
237
- log_success "JSON syntax" "valid"
238
- JSON_VALID=1
239
- else
240
- log_fail "JSON syntax" "invalid"
241
- fi
242
-
243
- if [[ "$JSON_VALID" -eq 1 ]]; then
244
- SERVER_COUNT="$(jq '.mcpServers | keys | length' "$MCP_CONFIG" 2>/dev/null || echo 0)"
245
- if [[ "$SERVER_COUNT" -gt 0 ]]; then
246
- log_success "MCP servers" "$SERVER_COUNT configured"
247
- else
248
- log_fail "MCP servers" "none configured"
249
- fi
250
-
251
- if jq -e --argjson blocked "$BLOCKED_PATHS" '.mcpServers.filesystem.args[]? as $arg | $blocked[] | select(. == $arg)' "$MCP_CONFIG" >/dev/null 2>&1; then
252
- log_warn "Filesystem scope" "too broad (use a project-scoped directory)"
253
- else
254
- log_success "Filesystem scope" "not globally broad"
255
- fi
256
-
257
- # MCP operational policy: allow known launchers only.
258
- unknown_launchers="$(jq -r --argjson allowed "$ALLOWED_LAUNCHERS" '.mcpServers | to_entries[]? | select((.value.command as $cmd | [$allowed[] | select(. == $cmd)] | length) == 0) | "\(.key):\(.value.command)"' "$MCP_CONFIG" 2>/dev/null || true)"
259
- if [[ -n "$unknown_launchers" ]]; then
260
- while IFS= read -r item; do
261
- [[ -z "$item" ]] && continue
262
- log_warn "Launcher policy" "review non-standard command $item"
263
- done <<<"$unknown_launchers"
264
- else
265
- log_success "Launcher policy" "all server launchers are allowlisted (npx/node/uvx)"
266
- fi
267
-
268
- # MCP policy: flag wildcard/potentially risky args for review.
269
- risky_args="$(jq -r --argjson blocked "$BLOCKED_ARG_PATTERNS" '
270
- .mcpServers
271
- | to_entries[]?
272
- | .key as $name
273
- | (.value.args // [])[]?
274
- | . as $arg
275
- | select(any($blocked[]; . as $pattern | ($arg | test($pattern))))
276
- | "\($name):\($arg)"
277
- ' "$MCP_CONFIG" 2>/dev/null || true)"
278
- if [[ -n "$risky_args" ]]; then
279
- while IFS= read -r item; do
280
- [[ -z "$item" ]] && continue
281
- log_warn "Arg policy" "review risky argument $item"
282
- done <<<"$risky_args"
283
- else
284
- log_success "Arg policy" "no high-risk wildcard/unsafe args found"
285
- fi
286
-
287
- forbidden_default_servers="$(jq -r --argjson forbidden "$FORBIDDEN_DEFAULT_SERVERS" '
288
- .mcpServers
289
- | keys[]
290
- | . as $server
291
- | select(any($forbidden[]; . == $server))
292
- ' "$MCP_CONFIG" 2>/dev/null || true)"
293
- if [[ -n "$forbidden_default_servers" ]]; then
294
- while IFS= read -r item; do
295
- [[ -z "$item" ]] && continue
296
- log_warn "Default server policy" "$item enabled in strict-local profile"
297
- done <<<"$forbidden_default_servers"
298
- else
299
- log_success "Default server policy" "local-only default set"
300
- fi
301
-
302
- invalid_transports="$(jq -r --argjson trusted "$TRUSTED_TRANSPORTS" '
303
- .mcpServers
304
- | to_entries[]?
305
- | .key as $name
306
- | (.value.transport // "stdio") as $transport
307
- | select(([$trusted[] | select(. == $transport)] | length) == 0)
308
- | "\($name):\($transport)"
309
- ' "$MCP_CONFIG" 2>/dev/null || true)"
310
- if [[ -n "$invalid_transports" ]]; then
311
- while IFS= read -r item; do
312
- [[ -z "$item" ]] && continue
313
- log_warn "Transport policy" "review untrusted transport $item"
314
- done <<<"$invalid_transports"
315
- else
316
- log_success "Transport policy" "all servers use trusted transports"
317
- fi
318
-
319
- if [[ "$REQUIRE_HTTPS_FOR_HTTP" -eq 1 ]]; then
320
- insecure_http_servers="$(jq -r '
321
- .mcpServers
322
- | to_entries[]?
323
- | select((.value.transport // "") == "http")
324
- | select((.value.url // "") | startswith("https://") | not)
325
- | .key
326
- ' "$MCP_CONFIG" 2>/dev/null || true)"
327
- if [[ -n "$insecure_http_servers" ]]; then
328
- while IFS= read -r item; do
329
- [[ -z "$item" ]] && continue
330
- log_warn "Transport security" "$item uses non-HTTPS HTTP transport"
331
- done <<<"$insecure_http_servers"
332
- else
333
- log_success "Transport security" "HTTP transports are HTTPS"
334
- fi
335
- fi
336
-
337
- # Streamable-HTTP HTTPS validation
338
- insecure_streamable_servers="$(jq -r '
339
- .mcpServers
340
- | to_entries[]?
341
- | select((.value.transport // "") == "streamable-http")
342
- | select((.value.url // "") | startswith("https://") | not)
343
- | .key
344
- ' "$MCP_CONFIG" 2>/dev/null || true)"
345
- if [[ -n "$insecure_streamable_servers" ]]; then
346
- while IFS= read -r item; do
347
- [[ -z "$item" ]] && continue
348
- log_warn "Transport security" "$item streamable-http transport must use HTTPS"
349
- done <<<"$insecure_streamable_servers"
350
- fi
351
-
352
- # Auth Profiles validation
353
- if command -v jq >/dev/null 2>&1 && [[ -f "$MCP_POLICY_CONFIG" ]]; then
354
- declared_auth="$(jq -c '.profiles[.defaultProfile].authProfiles // []' "$MCP_POLICY_CONFIG" 2>/dev/null)"
355
- if [[ "$declared_auth" != "[]" ]]; then
356
- log_success "Auth profiles" "declared: $declared_auth"
357
- # Check transport/auth compatibility: streamable-http/http servers need more than "none"
358
- incompatible_auth="$(jq -r --argjson registry "$APPROVED_REGISTRY" --argjson allowed_auth "$declared_auth" '
359
- .mcpServers
360
- | to_entries[]?
361
- | .key as $name
362
- | (.value.transport // "stdio") as $t
363
- | select($t == "http" or $t == "streamable-http")
364
- | select($registry[$name].authProfile // "none" | IN($allowed_auth[]) | not)
365
- | "\($name):\($registry[$name].authProfile // "none")"
366
- ' "$MCP_CONFIG" 2>/dev/null || true)"
367
- if [[ -n "$incompatible_auth" ]]; then
368
- while IFS= read -r item; do
369
- [[ -z "$item" ]] && continue
370
- log_warn "Auth compatibility" "$item uses auth profile not in policy"
371
- done <<<"$incompatible_auth"
372
- else
373
- log_success "Auth compatibility" "all server auth profiles match policy"
374
- fi
375
- fi
376
- fi
377
-
378
- if [[ "$REQUIRE_OAUTH_FOR_HTTP" -eq 1 ]]; then
379
- non_oauth_http_servers="$(jq -r --argjson registry "$APPROVED_REGISTRY" '
380
- .mcpServers
381
- | to_entries[]?
382
- | .key as $name
383
- | select((.value.transport // "") == "http")
384
- | select(($registry[$name].auth // "") != "oauth2")
385
- | $name
386
- ' "$MCP_CONFIG" 2>/dev/null || true)"
387
- if [[ -n "$non_oauth_http_servers" ]]; then
388
- while IFS= read -r item; do
389
- [[ -z "$item" ]] && continue
390
- log_warn "Auth policy" "$item HTTP transport is not registered for OAuth2"
391
- done <<<"$non_oauth_http_servers"
392
- else
393
- log_success "Auth policy" "HTTP transports are registry-approved for OAuth2"
394
- fi
395
- fi
396
-
397
- env_vars="$(
398
- {
399
- jq -r '.mcpServers | to_entries[]? | (.value.env // {}) | to_entries[]?.value' "$MCP_CONFIG"
400
- jq -r '.mcpServers | to_entries[]? | (.value.args // [])[]?' "$MCP_CONFIG"
401
- } | sed -n 's/^\${\([A-Z0-9_][A-Z0-9_]*\)}$/\1/p' | sort -u
402
- )"
403
- if [[ -z "$env_vars" ]]; then
404
- log_success "Server env placeholders" "none declared"
405
- else
406
- missing=0
407
- while IFS= read -r key; do
408
- [[ -z "$key" ]] && continue
409
- if [[ -z "${!key:-}" ]]; then
410
- log_warn "Env variable" "$key is not set"
411
- missing=$((missing + 1))
412
- fi
413
- done <<<"$env_vars"
414
- if [[ "$missing" -eq 0 ]]; then
415
- log_success "Env variables" "all referenced placeholders are set"
416
- fi
417
- fi
418
-
419
- while IFS=$'\t' read -r server env_key; do
420
- [[ -z "${server:-}" ]] && continue
421
- if ! jq -e --arg server "$server" '.mcpServers[$server]' "$MCP_CONFIG" >/dev/null 2>&1; then
422
- continue
423
- fi
424
- if [[ -n "${!env_key:-}" ]]; then
425
- log_success "Token check" "$env_key is set for $server MCP server"
426
- else
427
- log_warn "Token check" "$env_key missing for $server MCP server"
428
- fi
429
- done < <(jq -r '
430
- to_entries[]
431
- | .key as $server
432
- | (.value // [])[]
433
- | [$server, .]
434
- | @tsv
435
- ' <<<"$REQUIRED_ENV_RULES" 2>/dev/null || true)
436
-
437
- if [[ "$POLICY_WARN_ON_UNPINNED_NPX" -eq 1 ]]; then
438
- unpinned_npx_servers="$(jq -r '
439
- .mcpServers
440
- | to_entries[]?
441
- | select(.value.command == "npx")
442
- | select((.value.args // []) | any(
443
- test("^[A-Za-z0-9@._/-]+$")
444
- and (startswith("-") | not)
445
- and (test("^(@[^/]+/[^@]+|[^@]+)@[^@]+$") | not)
446
- ))
447
- | .key
448
- ' "$MCP_CONFIG" 2>/dev/null || true)"
449
- if [[ -n "$unpinned_npx_servers" ]]; then
450
- while IFS= read -r item; do
451
- [[ -z "$item" ]] && continue
452
- log_warn "Package pinning" "$item uses unpinned npx package"
453
- done <<<"$unpinned_npx_servers"
454
- else
455
- log_success "Package pinning" "no unpinned npx packages found"
456
- fi
457
- fi
458
-
459
- if [[ "$REQUIRE_APPROVED_PACKAGE_LOCK" -eq 1 ]]; then
460
- approved_package_mismatches="$(jq -r --argjson approved "$APPROVED_PACKAGE_LOCK" '
461
- .mcpServers
462
- | to_entries[]?
463
- | .key as $server
464
- | .value.command as $command
465
- | ((.value.args // []) | map(select(test("^[A-Za-z0-9@._/-]+@[A-Za-z0-9._-]+$"))) | .[0] // "") as $pkg
466
- | select($command == "npx")
467
- | select(($approved[$server].package // "") != $pkg)
468
- | "\($server)\t\($pkg)\t\($approved[$server].package // "untracked")"
469
- ' "$MCP_CONFIG" 2>/dev/null || true)"
470
- if [[ -n "$approved_package_mismatches" ]]; then
471
- while IFS=$'\t' read -r server actual expected; do
472
- [[ -z "${server:-}" ]] && continue
473
- log_warn "Package lock" "$server uses $actual (approved: $expected)"
474
- done <<<"$approved_package_mismatches"
475
- else
476
- log_success "Package lock" "all active servers match approved package refs"
477
- fi
478
- fi
479
-
480
- if [[ "$REQUIRE_REGISTRY_ENTRY" -eq 1 ]]; then
481
- registry_mismatches="$(jq -r --argjson registry "$APPROVED_REGISTRY" '
482
- .mcpServers
483
- | to_entries[]?
484
- | .key as $server
485
- | (.value.command // "") as $command
486
- | (.value.transport // "stdio") as $transport
487
- | ((.value.args // []) | map(select(test("^[A-Za-z0-9@._/-]+@[A-Za-z0-9._-]+$"))) | .[0] // "") as $pkg
488
- | (.value.url // "") as $url
489
- | select(($registry[$server] | type) != "object"
490
- or ($registry[$server].transport // "stdio") != $transport
491
- or ($registry[$server].launcher // "") != $command
492
- or (($command == "npx") and (($registry[$server].package // "") != $pkg))
493
- or (($transport == "http") and (($registry[$server].url // "") != $url)))
494
- | "\($server)\t\($transport)\t\($command)\t\($pkg)\t\($url)"
495
- ' "$MCP_CONFIG" 2>/dev/null || true)"
496
- if [[ -n "$registry_mismatches" ]]; then
497
- while IFS=$'\t' read -r server _transport _command _pkg _url; do
498
- [[ -z "${server:-}" ]] && continue
499
- log_warn "Registry policy" "$server is missing or diverges from the tracked MCP registry"
500
- done <<<"$registry_mismatches"
501
- else
502
- log_success "Registry policy" "all active servers match the tracked MCP registry"
503
- fi
504
- fi
505
- fi
506
- else
507
- log_warn "jq" "not installed, running limited checks"
508
- if grep -q '"mcpServers"' "$MCP_CONFIG" 2>/dev/null; then
509
- log_success "mcpServers key" "present"
510
- else
511
- log_fail "mcpServers key" "missing"
512
- fi
513
- fi
514
-
515
- if [[ "$Errors" -eq 0 ]]; then
516
- if [[ "$Warnings" -eq 0 ]]; then
517
- SUMMARY_STATUS="healthy"
518
- else
519
- if [[ "$STRICT_MODE" -eq 1 ]]; then
520
- SUMMARY_STATUS="failed"
521
- else
522
- SUMMARY_STATUS="warning"
523
- fi
524
- fi
525
- else
526
- SUMMARY_STATUS="failed"
527
- fi
528
-
529
- if [[ "$JSON_MODE" -eq 1 ]]; then
530
- jq -n \
531
- --arg status "$SUMMARY_STATUS" \
532
- --arg config "$MCP_CONFIG" \
533
- --arg policy "$MCP_POLICY_CONFIG" \
534
- --argjson strict "$STRICT_MODE" \
535
- --argjson server_count "$SERVER_COUNT" \
536
- --argjson errors "$Errors" \
537
- --argjson warnings "$Warnings" \
538
- --argjson config_ok "$CONFIG_OK" \
539
- --argjson json_valid "$JSON_VALID" \
540
- '{
541
- status: $status,
542
- strict: ($strict == 1),
543
- config_path: $config,
544
- policy_path: $policy,
545
- server_count: $server_count,
546
- checks: {
547
- config_present: ($config_ok == 1),
548
- json_valid: ($json_valid == 1)
549
- },
550
- summary: {
551
- errors: $errors,
552
- warnings: $warnings
553
- }
554
- }'
555
- else
556
- echo ""
557
- ui_header "Summary"
558
- fi
559
-
560
- if [[ "$Errors" -eq 0 ]]; then
561
- if [[ "$Warnings" -eq 0 ]]; then
562
- if [[ "$JSON_MODE" -ne 1 ]]; then
563
- ui_ok "MCP configuration healthy"
564
- fi
565
- else
566
- if [[ "$STRICT_MODE" -eq 1 ]]; then
567
- if [[ "$JSON_MODE" -ne 1 ]]; then
568
- ui_err "MCP issues found" "$Warnings policy warnings (strict mode)"
569
- fi
570
- exit 1
571
- else
572
- if [[ "$JSON_MODE" -ne 1 ]]; then
573
- ui_warn "MCP configuration healthy" "$Warnings warnings"
574
- fi
575
- fi
576
- fi
577
- else
578
- if [[ "$JSON_MODE" -ne 1 ]]; then
579
- ui_err "MCP issues found" "$Errors errors, $Warnings warnings"
580
- fi
581
- exit 1
582
- fi