@sebastienrousseau/dotfiles 0.2.501 → 0.2.502
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.
- package/CHANGELOG.md +77 -0
- package/README.md +38 -8
- package/docs/COPYRIGHT +1 -1
- package/docs/index.md +2 -2
- package/docs/manual/00-introduction.md +1 -1
- package/docs/manual/03-reference/01-dot-cli.md +63 -45
- package/docs/manual/03-reference/02-config-files.md +2 -2
- package/docs/manual/03-reference/05-feature-flags.md +62 -3
- package/docs/manual/_toc.yml +1 -1
- package/docs/manual/command-index.md +12 -8
- package/docs/manual/index.md +66 -0
- package/docs/operations/COVERAGE.md +36 -2
- package/docs/operations/HARD_AUDIT_2026.md +631 -0
- package/docs/operations/REGISTRY.md +89 -0
- package/docs/operations/ROADMAP_2026.md +665 -0
- package/docs/operations/TRACEABILITY.md +3 -0
- package/docs/operations/VERSION_SYNC.md +4 -4
- package/docs/reference/POWERSHELL_PARITY.md +80 -0
- package/docs/registry.json +6 -0
- package/docs/security/CI_PINNING.md +113 -0
- package/docs/security/COMMIT_SIGNING.md +138 -0
- package/docs/security/DISCLOSURE.md +130 -0
- package/docs/security/KEY_ROTATION.md +81 -1
- package/docs/security/SCORECARD.md +74 -14
- package/docs/security/security-pubkey.asc +15 -0
- package/dot_config/fish/conf.d/direnv.fish +4 -0
- package/dot_config/fish/conf.d/mise-activate.fish +5 -0
- package/dot_config/git/hooks/executable_commit-msg +1 -1
- package/dot_config/shell/00-core-paths.sh.tmpl +8 -1
- package/dot_config/shell/README.md +1 -1
- package/dot_config/zsh/dot_zshrc.tmpl +57 -4
- package/dot_config/zsh/rc.d/30-options.zsh.tmpl +1 -1
- package/dot_local/bin/executable_dot +49 -9
- package/dot_local/bin/executable_dot-bootstrap +0 -1
- package/dot_local/bin/executable_dot-theme-sync +8 -8
- package/dot_local/bin/executable_tour +2 -2
- package/dot_local/share/man/man1/dot.1 +1 -1
- package/dot_local/share/zsh/completions/_dot +4 -0
- package/install.sh +44 -37
- package/package.json +1 -1
- package/scripts/ci/dot-cli-startup-bench.sh +126 -0
- package/scripts/ci/install-chezmoi-verified.sh +4 -1
- package/scripts/ci/lint-reusable-pins.sh +78 -0
- package/scripts/ci/run-coverage.sh +89 -0
- package/scripts/ci/windows-smoke-test.ps1 +136 -0
- package/scripts/diagnostics/doctor.sh +39 -9
- package/scripts/dot/commands/agent.sh +19 -22
- package/scripts/dot/commands/agents.sh +325 -0
- package/scripts/dot/commands/aliases.sh +10 -8
- package/scripts/dot/commands/core.sh +10 -4
- package/scripts/dot/commands/fleet.sh +278 -3
- package/scripts/dot/commands/init.sh +184 -0
- package/scripts/dot/commands/meta.sh +7 -4
- package/scripts/dot/commands/registry.sh +263 -0
- package/scripts/dot/commands/tools.sh +49 -1
- package/scripts/dot/lib/bento.sh +1 -1
- package/scripts/dot/lib/platform.sh +21 -8
- package/scripts/dot/lib/ui.sh +134 -2
- package/scripts/dot/lib/utils.sh +1 -1
- package/scripts/git-hooks/pre-commit-audit.sh +1 -1
- package/scripts/lib/secrets_provider.sh +32 -6
- package/scripts/ops/rollback.sh +14 -0
- package/scripts/security/check-disclosure-key-expiry.sh +110 -0
- package/scripts/security/lock-configs.sh +11 -2
- package/scripts/version-sync.sh +3 -0
|
@@ -30,7 +30,7 @@ Regressions are triaged within one Monday cycle: identify the check
|
|
|
30
30
|
that dropped, either remediate or file an exception entry below with
|
|
31
31
|
a rationale and an expiry date.
|
|
32
32
|
|
|
33
|
-
## Per-check posture (snapshot)
|
|
33
|
+
## Per-check posture (snapshot, master HEAD)
|
|
34
34
|
|
|
35
35
|
The checks below come from OpenSSF Scorecard's published rubric. Tick
|
|
36
36
|
marks reflect this repo's posture at the time of writing — the
|
|
@@ -38,30 +38,75 @@ marks reflect this repo's posture at the time of writing — the
|
|
|
38
38
|
|
|
39
39
|
| Check | Status | Notes |
|
|
40
40
|
|---|---|---|
|
|
41
|
-
| Branch-Protection |
|
|
42
|
-
| Code-Review |
|
|
41
|
+
| Branch-Protection | ⚠ | `master` requires signed commits, linear history, required checks (`.github/rulesets/master.json`). Scorecard can't read classic protection rules without a fine-grained PAT, so this check reports as `-1` ("internal error"). |
|
|
42
|
+
| Code-Review | ⚠ | Single-maintainer repo; PRs are self-merged after CI. Scorecard scores 0/30 here by design — see `Open findings` below. |
|
|
43
43
|
| Signed-Commits | ✓ | Enforced by pre-push hook (`scripts/git-hooks/pre-push`) and at-push by branch protection (#853 + #857). |
|
|
44
44
|
| Dependency-Update-Tool | ✓ | Dependabot configured for github-actions / npm / docker / devcontainers / uv. |
|
|
45
|
-
| Fuzzing | ⚠ | `install.sh` fuzz harness lives under `tests/fuzz/` (closes #881)
|
|
45
|
+
| Fuzzing | ⚠ | `install.sh` fuzz harness lives under `tests/fuzz/` (closes #881), but it's shell-based and Scorecard's heuristic only recognizes OSS-Fuzz / ClusterFuzzLite / native Go fuzz / libFuzzer / Atheris. None support shell. Property tests under `tests/unit/functions/test_property_*.sh` cover the closest equivalent surface. |
|
|
46
46
|
| License | ✓ | Apache-2.0 at repo root. |
|
|
47
|
-
| Maintained | ✓ | Active commit cadence; the [README](../../README.md) lists the current `dotfiles_version
|
|
48
|
-
| Pinned-Dependencies |
|
|
47
|
+
| Maintained | ✓ | Active commit cadence; the [README](../../README.md) lists the current `dotfiles_version`. |
|
|
48
|
+
| Pinned-Dependencies | ⚠ | Closed 8 of 14 findings this cycle (every Dockerfile base + every workflow action + 2 `curl \| sh` installers + the `npm install -g npm` upgrade step). 5 residual findings stay open by design — see `Open findings`. |
|
|
49
49
|
| SAST | ✓ | CodeQL (`.github/workflows/codeql.yml`) + Checkov + Grype. |
|
|
50
50
|
| SBOM | ✓ | Generated per PR by `sbom-diff.yml` and per release by `security-release.yml`. |
|
|
51
51
|
| Security-Policy | ✓ | `.github/SECURITY.md` + this page + `docs/security/THREAT_MODEL.md`. |
|
|
52
|
-
| Token-Permissions | ✓ |
|
|
52
|
+
| Token-Permissions | ✓ | Top-level `permissions:` blocks restricted to `contents: read`. `write` scopes scoped to the jobs that need them (#886). |
|
|
53
53
|
| Vulnerabilities | ✓ | Grype gate hard-fails on `high` / `critical` on `master` (#852). |
|
|
54
54
|
| Webhooks | n/a | No external webhooks configured. |
|
|
55
|
-
| CI-Tests | ✓ |
|
|
56
|
-
| CII-Best-Practices | ☐ | Not applied yet — see
|
|
55
|
+
| CI-Tests | ✓ | Linux + macOS Intel + Apple Silicon matrix; optional Windows. |
|
|
56
|
+
| CII-Best-Practices | ☐ | Not applied yet — see `Open work` below. |
|
|
57
57
|
| Dangerous-Workflow | ✓ | No `pull_request_target` with checkout-PR-code anti-pattern. |
|
|
58
58
|
| Packaging | ✓ | `npm publish --provenance` via OIDC (`.github/workflows/npm-publish.yml`); `policy-bundle-release.yml` for the policy artifact. |
|
|
59
|
+
| Signed-Releases | ⚠ | SLSA provenance + minisig attached via `security-release.yml`; Cosign keyless signing tracked at #876. |
|
|
60
|
+
|
|
61
|
+
## Open findings
|
|
62
|
+
|
|
63
|
+
The eight items below are surfaced as alerts at
|
|
64
|
+
<https://github.com/sebastienrousseau/dotfiles/security/code-scanning>.
|
|
65
|
+
Each row gives the dismissal **reason** and the exact **comment text**
|
|
66
|
+
to paste when triaging.
|
|
67
|
+
|
|
68
|
+
### Bucket 1 — Dismiss in the UI (5 items)
|
|
69
|
+
|
|
70
|
+
Scorecard flags every `npm install`, `pip install`, `go install`, and
|
|
71
|
+
`curl | sh` invocation under the `Pinned-Dependencies` check. The five
|
|
72
|
+
below either belong to user-facing "update everything" aliases or are
|
|
73
|
+
false positives in Scorecard's regex.
|
|
74
|
+
|
|
75
|
+
| Path | Reason | Comment to paste |
|
|
76
|
+
|---|---|---|
|
|
77
|
+
| `.chezmoitemplates/aliases/legal/legal.aliases.sh:64` | **Won't fix** | User-facing convenience alias — `go install <fortune-style joke command>@latest`. Pinning by SHA would require users to update the alias manually on every upstream change. Intentional. |
|
|
78
|
+
| `.chezmoitemplates/aliases/update/update.aliases.sh:173` | **Won't fix** | User-facing convenience alias — bulk-update wrapper around `npm`. The alias's purpose is to update everything; pinning defeats it. Intentional. |
|
|
79
|
+
| `dot_local/bin/executable_update:99` | **Won't fix** | `dot_local/bin/executable_update` is the user-invoked "update everything" command. The literal point of `npm update -g` is to update to whatever's latest. Pinning defeats it. Intentional. |
|
|
80
|
+
| `scripts/dot/commands/tools.sh:121` | **False positive** | `npm install --package-lock-only --ignore-scripts --silent` generates a lockfile and does NOT fetch packages. Scorecard's regex matches `npm install` blindly; this invocation has no supply-chain surface. |
|
|
81
|
+
| `install/provision/run_onchange_10-linux-packages.sh.tmpl:369` | **Won't fix** | Already exact-version-pins `aider-chat==0.86.2`. Scorecard wants sha256-hash pinning, which would require a ~2,700-line lockfile covering all 108 transitive deps and re-generated on every aider release — disproportionate maintenance for a single optional provisioning install. |
|
|
82
|
+
|
|
83
|
+
### Bucket 2 — Architectural (1 item)
|
|
84
|
+
|
|
85
|
+
| Alert | Why architectural |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `CodeReviewID` (high) — score 0 | Scorecard requires every merged PR to record an approval review by a different GitHub user than the author. This repo is single-maintainer; PRs are self-merged after CI. The fix path is either (a) onboard a co-maintainer who reviews PRs before merge, or (b) accept the finding. Dismiss as **Won't fix** with comment: "Single-maintainer project — no second reviewer available. CI gates (47 required checks per PR) substitute for human review." |
|
|
88
|
+
|
|
89
|
+
### Bucket 3 — External action (2 items)
|
|
90
|
+
|
|
91
|
+
| Alert | Action |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `FuzzingID` (medium) | Dismiss as **Won't fix** with comment: "Repo is bash + Go-template + Lua. ClusterFuzzLite / OSS-Fuzz / native Go fuzz / libFuzzer / Atheris (the frameworks Scorecard recognises) all target compiled languages, none support shell. The `tests/fuzz/fuzz_install.sh` harness + property tests under `tests/unit/functions/test_property_*.sh` cover the equivalent surface." |
|
|
94
|
+
| `CIIBestPracticesID` (low) | Apply at <https://www.bestpractices.dev/projects/new>. ~67 self-attested questions, free, 1-2 hours. After silver-tier approval, paste the badge into `README.md` next to the existing Scorecard badge. Most criteria already met (signed commits, CI, security policy, license). |
|
|
95
|
+
|
|
96
|
+
## Closed this cycle
|
|
97
|
+
|
|
98
|
+
| Date | Score | Alerts open | Change |
|
|
99
|
+
|---|---|---|---|
|
|
100
|
+
| 2026-05-14 | 6.5 | 28 | First clean publish after fixing the scorecard.yml `uses:`-only restriction (#885). Findings had been hidden by publish-step 400s until then. |
|
|
101
|
+
| 2026-05-14 | 6.5 | 11 | Closed 17 of 28: 10× TokenPermissions (#886), 6× Dockerfile bases (#886), 1× gitleaks fixture (#884). |
|
|
102
|
+
| 2026-05-14 | 6.5 | 9 | Closed 2× `curl \| sh` installers (#888). |
|
|
103
|
+
| 2026-05-14 | 6.5 | 8 | Closed 1× `npm install -g npm@…` via Node 24 bump (#889). |
|
|
59
104
|
|
|
60
105
|
## Open work
|
|
61
106
|
|
|
62
|
-
- Apply for the
|
|
63
|
-
-
|
|
64
|
-
-
|
|
107
|
+
- **Apply for the OpenSSF Best Practices Badge** — see Bucket 3 above.
|
|
108
|
+
- **Wire Cosign keyless signing into the release pipeline (#876)** — should push `Signed-Releases` to its maximum.
|
|
109
|
+
- **`harden-runner` block-mode adoption (#878)** — should tighten the `Token-Permissions` check further.
|
|
65
110
|
|
|
66
111
|
## Exceptions
|
|
67
112
|
|
|
@@ -72,9 +117,24 @@ marks reflect this repo's posture at the time of writing — the
|
|
|
72
117
|
If you add an exception, include the check name, an expiry date
|
|
73
118
|
(don't allow indefinite), and the rationale. Re-evaluate every quarter.
|
|
74
119
|
|
|
120
|
+
## Refreshing this document
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Re-count open alerts:
|
|
124
|
+
gh api 'repos/sebastienrousseau/dotfiles/code-scanning/alerts?state=open&per_page=50' \
|
|
125
|
+
--jq '[.[].rule.id] | group_by(.) | map({rule: .[0], count: length})'
|
|
126
|
+
|
|
127
|
+
# Trigger Scorecard manually:
|
|
128
|
+
gh workflow run scorecard.yml --ref master
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
If a new category appears, add a row to the matching bucket. If a
|
|
132
|
+
remediation closes one of the rows above, delete the row.
|
|
133
|
+
|
|
75
134
|
## References
|
|
76
135
|
|
|
77
|
-
- `.github/workflows/scorecard.yml` — the scanner
|
|
136
|
+
- `.github/workflows/scorecard.yml` — the scanner workflow (split into
|
|
137
|
+
`analysis` + `track-regression` jobs per #885).
|
|
78
138
|
- [Scorecard project](https://github.com/ossf/scorecard).
|
|
79
139
|
- [Scorecard checks reference](https://github.com/ossf/scorecard/blob/main/docs/checks.md).
|
|
80
|
-
-
|
|
140
|
+
- Tracking issue [#869](https://github.com/sebastienrousseau/dotfiles/issues/869).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
2
|
+
|
|
3
|
+
mDMEagg20hYJKwYBBAHaRw8BAQdAk2HMi5IFTHBGhCtSFzqwsyuVb7mO0iIhoUXR
|
|
4
|
+
mTv/PNK0R1NlYmFzdGllbiBSb3Vzc2VhdSAoRG90ZmlsZXMgU2VjdXJpdHkpIDxz
|
|
5
|
+
ZWN1cml0eUBzZWJhc3RpZW5yb3Vzc2VhdS5jb20+iLUEExYKAF0WIQRVr602T9nb
|
|
6
|
+
OBnmHwyNaI+vqRRGkwUCagg20hsUgAAAAAAEAA5tYW51MiwyLjUrMS4xMiwwLDMC
|
|
7
|
+
GwMFCQWjmoAFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQjWiPr6kURpOH
|
|
8
|
+
qQEA4gl4pfhkR9Y3IM6oMHydavrx3CE6mX4YRIkYJ7fYj4UA/iO/54bdjG+wmIrE
|
|
9
|
+
cQ077+ijTTCMnHrosn3E13oQ6NIJuDgEagg20hIKKwYBBAGXVQEFAQEHQKdKJLxe
|
|
10
|
+
xsXBXELbxGHUz2asoTuOnsKk8WEdOyESH3dnAwEIB4iUBBgWCgA8FiEEVa+tNk/Z
|
|
11
|
+
2zgZ5h8MjWiPr6kURpMFAmoINtIbFIAAAAAABAAObWFudTIsMi41KzEuMTIsMCwz
|
|
12
|
+
AhsMAAoJEI1oj6+pFEaTUw0A/14oNMWsx1gcyx9wl/vy+OgEXoOksxSV+FetAI2Q
|
|
13
|
+
Q7qOAP9pohjVAi40dqfBXv3vet7CV0V4p2kQ+qpQTOPTVafkDA==
|
|
14
|
+
=UtsL
|
|
15
|
+
-----END PGP PUBLIC KEY BLOCK-----
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# Shadow vendor `/opt/homebrew/share/fish/vendor_conf.d/direnv.fish`.
|
|
2
|
+
# fish dedupes conf.d files by basename — user conf.d wins. Direnv is
|
|
3
|
+
# loaded lazily by `_dotfiles_async_init` in init.fish via `_cached_eval
|
|
4
|
+
# direnv hook fish`, avoiding the unconditional cost on every shell start.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Shadow vendor `/opt/homebrew/share/fish/vendor_conf.d/mise-activate.fish`.
|
|
2
|
+
# fish dedupes conf.d files by basename — user conf.d wins. Mise is loaded
|
|
3
|
+
# lazily by `_dotfiles_async_init` in init.fish via `_cached_eval mise
|
|
4
|
+
# activate fish`, which is ~10× faster than the vendor's eager hook-env
|
|
5
|
+
# call on every shell start (saves ~140ms in fish startup).
|
|
@@ -106,7 +106,7 @@ detect_ai_agent() {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
# ── Branding Signature ────────────────────────────────────────────────
|
|
109
|
-
sig_file="
|
|
109
|
+
sig_file="${HOME}/.euxis/data/config/branding/signature.txt"
|
|
110
110
|
msg_file="${1:-}"
|
|
111
111
|
|
|
112
112
|
[[ -n "$msg_file" && -f "$msg_file" ]] || exit 0
|
|
@@ -41,7 +41,14 @@ path_prepend() {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
# 1. System Paths (Prepend, but will be pushed down by later prepends)
|
|
44
|
-
|
|
44
|
+
# Use path_prepend so existing entries are removed first — avoids the
|
|
45
|
+
# duplicate-system-path bloat that `PATH="...:${PATH}"` produces when
|
|
46
|
+
# the parent shell already had these (e.g. macOS launchd default PATH).
|
|
47
|
+
path_prepend "/sbin"
|
|
48
|
+
path_prepend "/usr/sbin"
|
|
49
|
+
path_prepend "/bin"
|
|
50
|
+
path_prepend "/usr/bin"
|
|
51
|
+
path_prepend "/usr/local/bin"
|
|
45
52
|
|
|
46
53
|
# 2. Homebrew Paths (Prepend -> Higher Priority)
|
|
47
54
|
path_prepend "/opt/homebrew/sbin"
|
|
@@ -27,7 +27,7 @@ Welcome to your universally compatible, high-performance dotfiles configuration,
|
|
|
27
27
|
To install these dotfiles on a new machine, run:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
sh -c "$(curl -fsSL https://raw.githubusercontent.com/sebastienrousseau/dotfiles/v0.2.
|
|
30
|
+
sh -c "$(curl -fsSL https://raw.githubusercontent.com/sebastienrousseau/dotfiles/v0.2.502/install.sh)"
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
This command will:
|
|
@@ -217,11 +217,17 @@ _cached_eval_impl() {
|
|
|
217
217
|
return 1
|
|
218
218
|
fi
|
|
219
219
|
|
|
220
|
-
|
|
221
|
-
|
|
220
|
+
# `mktemp` instead of `${cache}.tmp.$$` — under PID recycling (very
|
|
221
|
+
# rare but possible) two concurrent shells with the same $$ could
|
|
222
|
+
# otherwise race on the same temp file. mktemp gives a unique name.
|
|
223
|
+
# Use `>|` so we overwrite the just-created mktemp file even when the
|
|
224
|
+
# user has `setopt noclobber` (see dot_config/shell/05-core-safety.sh).
|
|
225
|
+
local _tmp _tmp_pin
|
|
226
|
+
_tmp="$(mktemp "${cache}.XXXXXX")" || return 1
|
|
227
|
+
print -r -- "$init_output" >| "$_tmp" && mv "$_tmp" "$cache" || { rm -f "$_tmp"; return 1; }
|
|
222
228
|
if [[ -n "$real_bin" ]]; then
|
|
223
|
-
|
|
224
|
-
mv "$
|
|
229
|
+
_tmp_pin="$(mktemp "${pin}.XXXXXX")" || return 1
|
|
230
|
+
print -r -- "$real_bin" >| "$_tmp_pin" && mv "$_tmp_pin" "$pin" || { rm -f "$_tmp_pin"; return 1; }
|
|
225
231
|
fi
|
|
226
232
|
source "$cache"
|
|
227
233
|
{ zcompile "$cache" } &!
|
|
@@ -245,6 +251,48 @@ _cached_eval_clear() {
|
|
|
245
251
|
printf 'Cleared %d cached eval file(s) from %s\n' "$count" "$cache_dir"
|
|
246
252
|
}
|
|
247
253
|
|
|
254
|
+
# Lazy-load helper for user-supplied slow tools.
|
|
255
|
+
#
|
|
256
|
+
# Usage in ~/.config/zsh/rc.d/99-local.zsh:
|
|
257
|
+
# _dot_lazy nvm nvm node npm npx # nvm.sh sourced on first nvm/node/npm/npx call
|
|
258
|
+
# _dot_lazy pyenv pyenv python python3 pip pip3
|
|
259
|
+
# _dot_lazy conda conda
|
|
260
|
+
#
|
|
261
|
+
# First arg is the "trigger" — also the function name that bootstraps
|
|
262
|
+
# the real tool. Remaining args are the commands that, when invoked,
|
|
263
|
+
# bootstrap the trigger then re-exec the command. Bootstrap functions
|
|
264
|
+
# must export `_DOT_LAZY_<TRIGGER>_LOADED` so subsequent calls bypass
|
|
265
|
+
# the stub.
|
|
266
|
+
#
|
|
267
|
+
# Why this matters: every `eval "$(slowtool init zsh)"` at startup
|
|
268
|
+
# costs 50-200ms; lazy-load defers that to first use, keeping
|
|
269
|
+
# `dot version` cold-start under 100ms.
|
|
270
|
+
_dot_lazy() {
|
|
271
|
+
local trigger="$1"; shift || return 1
|
|
272
|
+
local cmd
|
|
273
|
+
for cmd in "$trigger" "$@"; do
|
|
274
|
+
# shellcheck disable=SC2139,SC2140
|
|
275
|
+
eval "$cmd() {
|
|
276
|
+
local _trigger=\"$trigger\"
|
|
277
|
+
local _loaded_var=\"_DOT_LAZY_\${(U)_trigger//[^A-Z0-9]/_}_LOADED\"
|
|
278
|
+
if [[ -z \${(P)_loaded_var} ]]; then
|
|
279
|
+
# Unset every stub so the real binary takes over.
|
|
280
|
+
local _c
|
|
281
|
+
for _c in \"$trigger\" $@; do
|
|
282
|
+
unset -f -- \"\$_c\" 2>/dev/null
|
|
283
|
+
done
|
|
284
|
+
# User must define a __dot_lazy_init_<trigger> bootstrap.
|
|
285
|
+
local _init=\"__dot_lazy_init_\${_trigger//-/_}\"
|
|
286
|
+
if (( \${+functions[\$_init]} )); then
|
|
287
|
+
\"\$_init\" || return \$?
|
|
288
|
+
fi
|
|
289
|
+
export \"\$_loaded_var=1\"
|
|
290
|
+
fi
|
|
291
|
+
command \"$cmd\" \"\$@\"
|
|
292
|
+
}"
|
|
293
|
+
done
|
|
294
|
+
}
|
|
295
|
+
|
|
248
296
|
# Lightweight precmd hook manager (faster than add-zsh-hook)
|
|
249
297
|
_dotfiles_add_precmd() {
|
|
250
298
|
local fn="$1"
|
|
@@ -603,6 +651,11 @@ _dotfiles_async_init() {
|
|
|
603
651
|
esac
|
|
604
652
|
fi
|
|
605
653
|
done
|
|
654
|
+
# mise/atuin/etc emit `export PATH=...` which bypasses zsh's -U flag on
|
|
655
|
+
# the `path` array — re-apply uniqueness to drop any duplicates baked
|
|
656
|
+
# into the cached output (e.g. system paths inherited from a dirty
|
|
657
|
+
# parent shell).
|
|
658
|
+
typeset -U path
|
|
606
659
|
}
|
|
607
660
|
|
|
608
661
|
# Deferred Hydration for Zero-Jank Experience
|
|
@@ -64,7 +64,7 @@ fi
|
|
|
64
64
|
{{- end }}
|
|
65
65
|
{{- /* When node_manager = "mise", Node.js is handled by mise activate in dot_zshrc.tmpl */}}
|
|
66
66
|
|
|
67
|
-
# Completions:
|
|
67
|
+
# Completions: generated on-demand on first interactive shell and cached
|
|
68
68
|
if [[ "${DOTFILES_FAST:-0}" != "1" ]] && [[ "${DOTFILES_ULTRA_FAST:-0}" != "1" ]]; then
|
|
69
69
|
[[ ! -f "${ZSH_COMPLETIONS_DIR}/_gh" ]] && command -v gh >/dev/null &&
|
|
70
70
|
command gh completion -s zsh >| "${ZSH_COMPLETIONS_DIR}/_gh"
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# Copyright (c) 2015-2026 Dotfiles. All rights reserved.
|
|
4
4
|
set -euo pipefail
|
|
5
5
|
|
|
6
|
-
# Dotfiles CLI Entry Point - v0.2.
|
|
6
|
+
# Dotfiles CLI Entry Point - v0.2.502
|
|
7
7
|
|
|
8
8
|
# Ensure XDG Base Directory variables are set
|
|
9
9
|
export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
|
|
@@ -21,7 +21,7 @@ if [ -e "${HOME}/.nix-profile/etc/profile.d/nix.sh" ]; then
|
|
|
21
21
|
. "${HOME}/.nix-profile/etc/profile.d/nix.sh"
|
|
22
22
|
fi
|
|
23
23
|
|
|
24
|
-
VERSION="0.2.
|
|
24
|
+
VERSION="0.2.502"
|
|
25
25
|
|
|
26
26
|
COMMAND="${1:-}"
|
|
27
27
|
if [ -n "${1:-}" ]; then
|
|
@@ -160,6 +160,7 @@ Start Here|sync|Apply dotfiles to this machine.|Use after clone or after changes
|
|
|
160
160
|
Start Here|doctor|Check the environment and surface issues.|Use when something feels off.
|
|
161
161
|
Start Here|learn|Open the guided tour.|Best first stop for new users.
|
|
162
162
|
Start Here|help|Show the overview or help for one command.|Use `dot help <command>` for detail.
|
|
163
|
+
Daily Use|init|Bootstrap a foreign dotfiles repo through chezmoi + dot.|Usage: `dot init <github-user>`.
|
|
163
164
|
Daily Use|status|Show local drift.|See what changed since the last apply.
|
|
164
165
|
Daily Use|diff|Preview pending changes.|Safe way to inspect before applying.
|
|
165
166
|
Daily Use|edit|Open the source directory.|Use the configured editor.
|
|
@@ -172,6 +173,8 @@ AI and Agents|ai|Show installed AI tools.|Launches the AI status view.
|
|
|
172
173
|
AI and Agents|mcp|Inspect MCP policy and registry.|Supports `--strict|-s` and `--json|-j`.
|
|
173
174
|
AI and Agents|mode|Show or set the agent profile.|Profiles: ask, plan, apply, audit.
|
|
174
175
|
AI and Agents|agent|Show agent metadata, logs, checkpoints, and conformance.|Use for card, log, checkpoint, and conformance.
|
|
176
|
+
AI and Agents|agents|Multi-harness agent context (AGENTS.md sync, Cursor/Codex stubs).|Subcommands: list, check, render.
|
|
177
|
+
Fleet|registry|Browse reusable dotfile modules from the dot registry.|Subcommands: list, search, info, install, url, set-url.
|
|
175
178
|
Fleet|fleet|Show fleet node status, drift, and namespace.|Enterprise fleet management.
|
|
176
179
|
Configuration|env|Show managed tool versions.|Backed by mise.
|
|
177
180
|
Configuration|profile|Show or switch the active profile.|Use for machine-specific behavior.
|
|
@@ -269,7 +272,6 @@ health|Run the health dashboard with optional fixes.|dot health -v\ndot health -
|
|
|
269
272
|
perf|Measure shell performance and emit structured timing.|dot perf -j -r 1\ndot perf -p\ndot perf -t 200
|
|
270
273
|
security-score|Assess workstation security posture.|dot security-score -j\ndot security-score -q
|
|
271
274
|
snapshot|Capture the current workstation snapshot.|dot snapshot -b\ndot snapshot -b -f
|
|
272
|
-
verify|Run post-change verification checks.|dot verify\ndot verify -s
|
|
273
275
|
restore|Restore from a backup or git ref.|dot restore -l\ndot restore -L\ndot restore -g HEAD~1
|
|
274
276
|
rollback|Restore a previous configuration snapshot.|dot rollback
|
|
275
277
|
lint|Run shell quality checks for the repo.|dot lint\ndot lint -c\ndot lint -f
|
|
@@ -304,15 +306,14 @@ _dot_command_routes() {
|
|
|
304
306
|
cat <<'EOF'
|
|
305
307
|
sync|core
|
|
306
308
|
apply|core
|
|
309
|
+
init|init
|
|
307
310
|
update|core
|
|
308
311
|
add|core
|
|
309
312
|
diff|core
|
|
310
313
|
status|core
|
|
311
|
-
remove|core
|
|
312
314
|
cd|core
|
|
313
315
|
edit|core
|
|
314
316
|
commit|core
|
|
315
|
-
clean-cache|core
|
|
316
317
|
doctor|diagnostics
|
|
317
318
|
scorecard|diagnostics
|
|
318
319
|
score|diagnostics
|
|
@@ -328,12 +329,10 @@ heal|diagnostics
|
|
|
328
329
|
health|diagnostics
|
|
329
330
|
health-check|diagnostics
|
|
330
331
|
smoke-test|diagnostics
|
|
331
|
-
verify|diagnostics
|
|
332
332
|
attest|diagnostics
|
|
333
333
|
attestation|diagnostics
|
|
334
334
|
security-score|diagnostics
|
|
335
335
|
drift|diagnostics
|
|
336
|
-
benchmark|diagnostics
|
|
337
336
|
intelligence|diagnostics
|
|
338
337
|
conflicts|diagnostics
|
|
339
338
|
locks|diagnostics
|
|
@@ -385,7 +384,6 @@ lock-screen|security
|
|
|
385
384
|
usb-safety|security
|
|
386
385
|
upgrade|meta
|
|
387
386
|
cache-refresh|meta
|
|
388
|
-
prewarm|meta
|
|
389
387
|
docs|meta
|
|
390
388
|
learn|meta
|
|
391
389
|
keys|meta
|
|
@@ -393,7 +391,9 @@ sandbox|meta
|
|
|
393
391
|
mcp|meta
|
|
394
392
|
mode|meta
|
|
395
393
|
agent|meta
|
|
394
|
+
agents|agents
|
|
396
395
|
fleet|fleet
|
|
396
|
+
registry|registry
|
|
397
397
|
patterns|patterns
|
|
398
398
|
manual|manual
|
|
399
399
|
search|search
|
|
@@ -539,6 +539,8 @@ if [[ -z "$COMMAND" ]]; then
|
|
|
539
539
|
exit 0
|
|
540
540
|
fi
|
|
541
541
|
|
|
542
|
+
# `|| true` is intentional: an unknown command yields an empty route, which falls
|
|
543
|
+
# through the case below into the user-command path that emits the proper error.
|
|
542
544
|
route="$(_dot_command_route "$COMMAND" || true)"
|
|
543
545
|
|
|
544
546
|
case "$route" in
|
|
@@ -546,6 +548,42 @@ case "$route" in
|
|
|
546
548
|
_dispatch "$route" "$@"
|
|
547
549
|
;;
|
|
548
550
|
|
|
551
|
+
agents)
|
|
552
|
+
if [[ -n "$_SOURCE_DIR" ]] && [[ -f "$_SOURCE_DIR/scripts/dot/commands/agents.sh" ]]; then
|
|
553
|
+
# shellcheck disable=SC1091
|
|
554
|
+
source "$_SOURCE_DIR/scripts/dot/commands/agents.sh"
|
|
555
|
+
cmd_agents "$@"
|
|
556
|
+
exit $?
|
|
557
|
+
else
|
|
558
|
+
ui_fail "agents.sh not found"
|
|
559
|
+
exit 1
|
|
560
|
+
fi
|
|
561
|
+
;;
|
|
562
|
+
|
|
563
|
+
init)
|
|
564
|
+
if [[ -n "$_SOURCE_DIR" ]] && [[ -f "$_SOURCE_DIR/scripts/dot/commands/init.sh" ]]; then
|
|
565
|
+
# shellcheck disable=SC1091
|
|
566
|
+
source "$_SOURCE_DIR/scripts/dot/commands/init.sh"
|
|
567
|
+
cmd_init "$@"
|
|
568
|
+
exit $?
|
|
569
|
+
else
|
|
570
|
+
ui_fail "init.sh not found"
|
|
571
|
+
exit 1
|
|
572
|
+
fi
|
|
573
|
+
;;
|
|
574
|
+
|
|
575
|
+
registry)
|
|
576
|
+
if [[ -n "$_SOURCE_DIR" ]] && [[ -f "$_SOURCE_DIR/scripts/dot/commands/registry.sh" ]]; then
|
|
577
|
+
# shellcheck disable=SC1091
|
|
578
|
+
source "$_SOURCE_DIR/scripts/dot/commands/registry.sh"
|
|
579
|
+
cmd_registry "$@"
|
|
580
|
+
exit $?
|
|
581
|
+
else
|
|
582
|
+
ui_fail "registry.sh not found"
|
|
583
|
+
exit 1
|
|
584
|
+
fi
|
|
585
|
+
;;
|
|
586
|
+
|
|
549
587
|
manual)
|
|
550
588
|
if [[ -n "$_SOURCE_DIR" ]] && [[ -f "$_SOURCE_DIR/scripts/dot/commands/manual.sh" ]]; then
|
|
551
589
|
exec bash "$_SOURCE_DIR/scripts/dot/commands/manual.sh" "$@"
|
|
@@ -586,7 +624,9 @@ case "$route" in
|
|
|
586
624
|
*)
|
|
587
625
|
_user_cmd="${XDG_CONFIG_HOME:-$HOME/.config}/dotfiles/commands/${COMMAND}.sh"
|
|
588
626
|
if [[ -f "$_user_cmd" ]]; then
|
|
589
|
-
|
|
627
|
+
# Run in a subshell so a user `exit` doesn't kill the dot CLI.
|
|
628
|
+
(source "$_user_cmd" "$@")
|
|
629
|
+
exit $?
|
|
590
630
|
else
|
|
591
631
|
echo "Unknown command: ${COMMAND:-}" >&2
|
|
592
632
|
echo "Run 'dot help' for available commands." >&2
|
|
@@ -30,7 +30,6 @@ step "Starting Deterministic Bootstrap (v0.2.493 Frontier)"
|
|
|
30
30
|
# 1. Install Nix (if not present)
|
|
31
31
|
if ! command -v nix >/dev/null 2>&1; then
|
|
32
32
|
step "Installing Nix Package Manager..."
|
|
33
|
-
local nix_installer
|
|
34
33
|
nix_installer=$(umask 077 && mktemp)
|
|
35
34
|
if ! curl -fsSL -o "$nix_installer" https://nixos.org/nix/install; then
|
|
36
35
|
rm -f "$nix_installer"
|
|
@@ -466,14 +466,14 @@ reload_desktop() {
|
|
|
466
466
|
local highlight_color
|
|
467
467
|
case "$accent" in
|
|
468
468
|
-1) highlight_color="0.847059 0.847059 0.862745 Graphite" ;;
|
|
469
|
-
0)
|
|
470
|
-
1)
|
|
471
|
-
2)
|
|
472
|
-
3)
|
|
473
|
-
4)
|
|
474
|
-
5)
|
|
475
|
-
6)
|
|
476
|
-
*)
|
|
469
|
+
0) highlight_color="0.968627 0.831373 0.792157 Red" ;;
|
|
470
|
+
1) highlight_color="0.968627 0.890196 0.745098 Orange" ;;
|
|
471
|
+
2) highlight_color="1.000000 0.937255 0.690196 Yellow" ;;
|
|
472
|
+
3) highlight_color="0.752941 0.964706 0.678431 Green" ;;
|
|
473
|
+
4) highlight_color="0.698039 0.843137 1.000000 Blue" ;;
|
|
474
|
+
5) highlight_color="0.968627 0.831373 1.000000 Purple" ;;
|
|
475
|
+
6) highlight_color="1.000000 0.749020 0.823529 Pink" ;;
|
|
476
|
+
*) highlight_color="0.698039 0.843137 1.000000 Blue" ;;
|
|
477
477
|
esac
|
|
478
478
|
|
|
479
479
|
if defaults write -g AppleAccentColor -int "$accent" >/dev/null 2>&1 &&
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
# Interactive Tour using Gum
|
|
3
|
-
# Part of Dotfiles v0.2.
|
|
3
|
+
# Part of Dotfiles v0.2.502
|
|
4
4
|
|
|
5
5
|
set -eu
|
|
6
6
|
|
|
@@ -39,7 +39,7 @@ banner='██████╗ ██████╗ ████████╗
|
|
|
39
39
|
██████╔╝╚██████╔╝ ██║ ██║ ██║███████╗███████╗███████║
|
|
40
40
|
╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝
|
|
41
41
|
|
|
42
|
-
v0.2.
|
|
42
|
+
v0.2.502 · Infrastructure‑grade shell environment'
|
|
43
43
|
|
|
44
44
|
gum style \
|
|
45
45
|
--foreground 212 --border-foreground 212 --border double \
|
|
@@ -29,6 +29,10 @@ _dot() {
|
|
|
29
29
|
'edit:Open chezmoi source in \$EDITOR'
|
|
30
30
|
'commit:AI-powered conventional commit'
|
|
31
31
|
'patterns:Manage AI steering patterns'
|
|
32
|
+
'init:Bootstrap a foreign dotfiles repo through chezmoi + dot'
|
|
33
|
+
'agents:Multi-harness agent context (AGENTS.md sync, Cursor/Codex stubs)'
|
|
34
|
+
'manual:Open or download the multi-format manual'
|
|
35
|
+
'search:Find commands by keyword'
|
|
32
36
|
|
|
33
37
|
# Diagnostics
|
|
34
38
|
'doctor:Check system health and configuration'
|
package/install.sh
CHANGED
|
@@ -52,7 +52,7 @@ show_help() {
|
|
|
52
52
|
Usage: install.sh [version] [options]
|
|
53
53
|
|
|
54
54
|
Arguments:
|
|
55
|
-
version The version (tag or branch) to install (default: v0.2.
|
|
55
|
+
version The version (tag or branch) to install (default: v0.2.502)
|
|
56
56
|
|
|
57
57
|
Options:
|
|
58
58
|
--help Show this help message
|
|
@@ -64,7 +64,7 @@ EOF
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
main() {
|
|
67
|
-
local version="v0.2.
|
|
67
|
+
local version="v0.2.502"
|
|
68
68
|
local version_set=0
|
|
69
69
|
local minimal=0
|
|
70
70
|
local _cleanup_files=()
|
|
@@ -92,7 +92,7 @@ main() {
|
|
|
92
92
|
# like `foobar` doesn't trigger a 30s+ network download attempt.
|
|
93
93
|
# Caught by the install.sh fuzz harness (#881).
|
|
94
94
|
if [[ ! "$arg" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+([-+][a-zA-Z0-9.-]+)?$ ]]; then
|
|
95
|
-
error "Unrecognized positional argument '$arg' — expected a semver version (e.g. v0.2.
|
|
95
|
+
error "Unrecognized positional argument '$arg' — expected a semver version (e.g. v0.2.502)."
|
|
96
96
|
fi
|
|
97
97
|
version="$arg"
|
|
98
98
|
version_set=1
|
|
@@ -135,10 +135,29 @@ main() {
|
|
|
135
135
|
sudo mkdir -p /etc/apt/keyrings
|
|
136
136
|
curl -fsSL https://repo.charm.sh/apt/gpg.key |
|
|
137
137
|
sudo gpg --batch --yes --dearmor -o /etc/apt/keyrings/charm.gpg
|
|
138
|
-
#
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
138
|
+
# Pin the Charm GPG key fingerprint — a DNS attacker swapping
|
|
139
|
+
# repo.charm.sh/apt/gpg.key for a forged key with the same uid
|
|
140
|
+
# would otherwise pass the prior "any fid: present" check.
|
|
141
|
+
# Pinned value taken from https://repo.charm.sh/apt/gpg.key on
|
|
142
|
+
# 2026-05-16. If Charm rotates, update this and call it out in
|
|
143
|
+
# CHANGELOG.md.
|
|
144
|
+
CHARM_GPG_EXPECTED_FPR="C026D31B92F9BBE91D5DB75AB07AE17C9E0A6585"
|
|
145
|
+
charm_actual_fpr="$(gpg --no-default-keyring \
|
|
146
|
+
--keyring /etc/apt/keyrings/charm.gpg \
|
|
147
|
+
--with-colons --fingerprint 2>/dev/null |
|
|
148
|
+
awk -F: '/^fpr:/{print $10; exit}')"
|
|
149
|
+
if [[ -z "$charm_actual_fpr" ]]; then
|
|
150
|
+
echo "Error: Could not extract Charm GPG fingerprint — aborting" >&2
|
|
151
|
+
sudo rm -f /etc/apt/keyrings/charm.gpg
|
|
152
|
+
return 1
|
|
153
|
+
fi
|
|
154
|
+
if [[ "$charm_actual_fpr" != "$CHARM_GPG_EXPECTED_FPR" ]]; then
|
|
155
|
+
echo "Error: Charm GPG fingerprint mismatch" >&2
|
|
156
|
+
echo " expected: $CHARM_GPG_EXPECTED_FPR" >&2
|
|
157
|
+
echo " got: $charm_actual_fpr" >&2
|
|
158
|
+
echo " aborting (possible DNS / keyserver hijack)" >&2
|
|
159
|
+
sudo rm -f /etc/apt/keyrings/charm.gpg
|
|
160
|
+
return 1
|
|
142
161
|
fi
|
|
143
162
|
echo "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" | sudo tee /etc/apt/sources.list.d/charm.list
|
|
144
163
|
sudo apt-get update && sudo apt-get install gum -y >/dev/null 2>&1
|
|
@@ -160,43 +179,31 @@ main() {
|
|
|
160
179
|
mkdir -p "$bin_dir"
|
|
161
180
|
echo " Installing chezmoi via binary download..."
|
|
162
181
|
|
|
163
|
-
# Prefer verified installer with SHA256 checksum when available
|
|
182
|
+
# Prefer verified installer with SHA256 checksum when available.
|
|
183
|
+
# When verification fails or the verified installer isn't present
|
|
184
|
+
# we refuse to bootstrap rather than silently downloading and
|
|
185
|
+
# executing an unverified script (the previous fall-back to
|
|
186
|
+
# `get.chezmoi.io` was an unsigned bootstrap and a security hole).
|
|
164
187
|
local verified_installer
|
|
165
188
|
verified_installer="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scripts/ci/install-chezmoi-verified.sh"
|
|
166
189
|
if [[ -x "$verified_installer" ]] || [[ -f "$verified_installer" ]]; then
|
|
167
190
|
echo " Using checksum-verified installer..."
|
|
168
191
|
if ! bash "$verified_installer" "${CHEZMOI_VERSION:-2.47.1}" "$bin_dir"; then
|
|
169
|
-
echo "
|
|
170
|
-
|
|
171
|
-
|
|
192
|
+
echo "" >&2
|
|
193
|
+
echo " The verified chezmoi installer failed." >&2
|
|
194
|
+
echo " Refusing to fall back to an unverified bootstrap path." >&2
|
|
195
|
+
echo " Install chezmoi manually from a trusted source, then re-run install.sh:" >&2
|
|
196
|
+
echo " macOS: brew install chezmoi" >&2
|
|
197
|
+
echo " Linux: see https://www.chezmoi.io/install/" >&2
|
|
198
|
+
return 1
|
|
172
199
|
fi
|
|
200
|
+
return 0
|
|
173
201
|
fi
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
rm -f "$installer"
|
|
180
|
-
echo " Failed to download chezmoi installer" >&2
|
|
181
|
-
return 1
|
|
182
|
-
fi
|
|
183
|
-
# Validate: must be a shell script and not suspiciously large
|
|
184
|
-
if [[ "$(wc -c <"$installer")" -gt 102400 ]]; then
|
|
185
|
-
rm -f "$installer"
|
|
186
|
-
echo " Chezmoi installer suspiciously large. Aborting." >&2
|
|
187
|
-
return 1
|
|
188
|
-
fi
|
|
189
|
-
if ! head -1 "$installer" | grep -q '^#!/'; then
|
|
190
|
-
rm -f "$installer"
|
|
191
|
-
echo " Chezmoi installer doesn't look like a shell script. Aborting." >&2
|
|
192
|
-
return 1
|
|
193
|
-
fi
|
|
194
|
-
if ! BINDIR="$bin_dir" sh "$installer"; then
|
|
195
|
-
rm -f "$installer"
|
|
196
|
-
echo " chezmoi binary installer failed" >&2
|
|
197
|
-
return 1
|
|
198
|
-
fi
|
|
199
|
-
rm -f "$installer"
|
|
202
|
+
echo "" >&2
|
|
203
|
+
echo " scripts/ci/install-chezmoi-verified.sh is missing; cannot verify chezmoi bootstrap." >&2
|
|
204
|
+
echo " Install chezmoi manually (brew install chezmoi / official binary)" >&2
|
|
205
|
+
echo " and re-run install.sh." >&2
|
|
206
|
+
return 1
|
|
200
207
|
fi
|
|
201
208
|
}
|
|
202
209
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sebastienrousseau/dotfiles",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.502",
|
|
4
4
|
"description": "The Trusted Shell Platform — Universal dotfiles managed by Chezmoi. Features Bash & Zsh for macOS, Linux & WSL. Rust modern tooling & enterprise-grade security.",
|
|
5
5
|
"main": "install.sh",
|
|
6
6
|
"bin": {
|