jekyll-theme-zer0 1.8.2 → 1.9.1

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -3
  3. data/README.md +98 -7
  4. data/_data/content_statistics.yml +253 -251
  5. data/_includes/components/nav-export.html +61 -0
  6. data/_includes/components/nav-overview.html +54 -0
  7. data/scripts/bin/install +52 -705
  8. data/scripts/github-setup.sh +0 -0
  9. data/scripts/install/README.md +162 -0
  10. data/scripts/install/ai/client.sh +164 -0
  11. data/scripts/install/ai/diagnose.sh +81 -0
  12. data/scripts/install/ai/prompts/diagnose.system.md +42 -0
  13. data/scripts/install/ai/prompts/spec.schema.json +129 -0
  14. data/scripts/install/ai/prompts/suggest.system.md +43 -0
  15. data/scripts/install/ai/prompts/wizard.system.md +142 -0
  16. data/scripts/install/ai/suggest.sh +57 -0
  17. data/scripts/install/ai/wizard.sh +150 -0
  18. data/scripts/install/apply.sh +156 -0
  19. data/scripts/install/cli.sh +561 -0
  20. data/scripts/install/diff.sh +128 -0
  21. data/scripts/install/doctor.sh +168 -0
  22. data/scripts/install/fs.sh +138 -0
  23. data/scripts/install/log.sh +119 -0
  24. data/scripts/install/plan.sh +299 -0
  25. data/scripts/install/platform.sh +122 -0
  26. data/scripts/install/prompt.sh +124 -0
  27. data/scripts/install/repair.sh +45 -0
  28. data/scripts/install/scrape.sh +535 -0
  29. data/scripts/install/scrape_html.py +764 -0
  30. data/scripts/install/spec.sh +486 -0
  31. data/scripts/install/tasks/_registry.sh +65 -0
  32. data/scripts/install/tasks/agents.sh +60 -0
  33. data/scripts/install/tasks/config.sh +37 -0
  34. data/scripts/install/tasks/data.sh +18 -0
  35. data/scripts/install/tasks/deploy_azure-swa.sh +17 -0
  36. data/scripts/install/tasks/deploy_docker-prod.sh +21 -0
  37. data/scripts/install/tasks/deploy_github-pages.sh +18 -0
  38. data/scripts/install/tasks/devcontainer.sh +26 -0
  39. data/scripts/install/tasks/docker.sh +29 -0
  40. data/scripts/install/tasks/gemfile.sh +42 -0
  41. data/scripts/install/tasks/gitignore.sh +26 -0
  42. data/scripts/install/tasks/marker.sh +46 -0
  43. data/scripts/install/tasks/nav.sh +18 -0
  44. data/scripts/install/tasks/pages.sh +61 -0
  45. data/scripts/install/tasks/readme.sh +27 -0
  46. data/scripts/install/tasks/scrape.sh +348 -0
  47. data/scripts/install/template.sh +138 -0
  48. data/scripts/install/tui.sh +110 -0
  49. data/scripts/install/upgrade.sh +49 -0
  50. data/scripts/lib/install/template.sh +1 -0
  51. metadata +49 -6
@@ -0,0 +1,168 @@
1
+ #!/bin/bash
2
+ # =============================================================================
3
+ # scripts/install/doctor.sh — Pre-install environment health checks
4
+ # =============================================================================
5
+ # Checks system prerequisites and reports pass/warn/fail for each.
6
+ # Never blocks apply_run by default — warnings only. Fatal conditions
7
+ # (e.g. target dir not writable) return non-zero.
8
+ #
9
+ # Provides:
10
+ # doctor_run [TARGET_DIR] — run all checks; return 0 if clean, 1 if any FAIL
11
+ # doctor_check_ruby — Ruby version check
12
+ # doctor_check_bundler — Bundler present
13
+ # doctor_check_docker — Docker daemon
14
+ # doctor_check_git — git present
15
+ # doctor_check_gh — GitHub CLI present (warn only)
16
+ # doctor_check_writable — target dir writable
17
+ #
18
+ # Bash 3.2 compatible. No set -euo pipefail here.
19
+ # =============================================================================
20
+ [[ -n "${_HAS_DOCTOR_LIB:-}" ]] && return 0
21
+ _HAS_DOCTOR_LIB=1
22
+
23
+ # Internal check helpers
24
+ _doctor_pass() { printf " [PASS] %s\n" "$*" >&2; }
25
+ _doctor_warn() { printf " [WARN] %s\n" "$*" >&2; }
26
+ _doctor_fail() { printf " [FAIL] %s\n" "$*" >&2; }
27
+
28
+ # ---------------------------------------------------------------------------
29
+ # doctor_check_ruby
30
+ # ---------------------------------------------------------------------------
31
+ doctor_check_ruby() {
32
+ local ruby_ver
33
+ ruby_ver=$(ruby --version 2>/dev/null | awk '{print $2}')
34
+ if [[ -z "$ruby_ver" ]]; then
35
+ _doctor_fail "Ruby not found. Install Ruby >= 2.7 (rbenv / brew / asdf recommended)"
36
+ return 1
37
+ fi
38
+
39
+ # Extract major.minor
40
+ local major minor
41
+ major=$(echo "$ruby_ver" | cut -d. -f1)
42
+ minor=$(echo "$ruby_ver" | cut -d. -f2)
43
+
44
+ if [[ "$major" -lt 2 || ( "$major" -eq 2 && "$minor" -lt 5 ) ]]; then
45
+ _doctor_fail "Ruby $ruby_ver is too old (need >= 2.5). Upgrade recommended."
46
+ return 1
47
+ fi
48
+
49
+ if [[ "$major" -eq 2 && "$minor" -lt 7 ]]; then
50
+ _doctor_warn "Ruby $ruby_ver is supported but >= 3.0 is recommended"
51
+ else
52
+ _doctor_pass "Ruby $ruby_ver"
53
+ fi
54
+ return 0
55
+ }
56
+
57
+ # ---------------------------------------------------------------------------
58
+ # doctor_check_bundler
59
+ # ---------------------------------------------------------------------------
60
+ doctor_check_bundler() {
61
+ if ! command -v bundle >/dev/null 2>&1; then
62
+ _doctor_warn "Bundler not found — run: gem install bundler"
63
+ return 0 # warn, not fail
64
+ fi
65
+ local bver
66
+ bver=$(bundle --version 2>/dev/null | awk '{print $NF}')
67
+ _doctor_pass "Bundler $bver"
68
+ }
69
+
70
+ # ---------------------------------------------------------------------------
71
+ # doctor_check_docker
72
+ # ---------------------------------------------------------------------------
73
+ doctor_check_docker() {
74
+ local profile="${SPEC_PROFILE:-default}"
75
+ case "$profile" in
76
+ minimal|github-pages)
77
+ _doctor_pass "Docker: not required for profile '$profile'"
78
+ return 0
79
+ ;;
80
+ esac
81
+
82
+ if ! command -v docker >/dev/null 2>&1; then
83
+ _doctor_warn "Docker not found. Install Docker Desktop for best dev experience."
84
+ return 0
85
+ fi
86
+
87
+ if ! docker info >/dev/null 2>&1; then
88
+ _doctor_warn "Docker daemon not running. Start Docker Desktop."
89
+ return 0
90
+ fi
91
+
92
+ _doctor_pass "Docker (daemon running)"
93
+ }
94
+
95
+ # ---------------------------------------------------------------------------
96
+ # doctor_check_git
97
+ # ---------------------------------------------------------------------------
98
+ doctor_check_git() {
99
+ if ! command -v git >/dev/null 2>&1; then
100
+ _doctor_fail "git not found. Install git before proceeding."
101
+ return 1
102
+ fi
103
+ local gver
104
+ gver=$(git --version | awk '{print $3}')
105
+ _doctor_pass "git $gver"
106
+ }
107
+
108
+ # ---------------------------------------------------------------------------
109
+ # doctor_check_gh
110
+ # ---------------------------------------------------------------------------
111
+ doctor_check_gh() {
112
+ if ! command -v gh >/dev/null 2>&1; then
113
+ _doctor_warn "GitHub CLI (gh) not found. Some features will be limited."
114
+ return 0
115
+ fi
116
+ local ghver
117
+ ghver=$(gh --version 2>/dev/null | head -1 | awk '{print $3}')
118
+ _doctor_pass "GitHub CLI $ghver"
119
+ }
120
+
121
+ # ---------------------------------------------------------------------------
122
+ # doctor_check_writable TARGET_DIR
123
+ # ---------------------------------------------------------------------------
124
+ doctor_check_writable() {
125
+ local target="$1"
126
+ if [[ -z "$target" ]]; then
127
+ _doctor_warn "doctor_check_writable: no target_dir specified"
128
+ return 0
129
+ fi
130
+
131
+ # If directory doesn't exist, check parent
132
+ local check_dir="$target"
133
+ while [[ ! -d "$check_dir" ]]; do
134
+ check_dir=$(dirname "$check_dir")
135
+ done
136
+
137
+ if [[ ! -w "$check_dir" ]]; then
138
+ _doctor_fail "Target directory not writable: $check_dir"
139
+ return 1
140
+ fi
141
+ _doctor_pass "Target directory writable: $check_dir"
142
+ }
143
+
144
+ # ---------------------------------------------------------------------------
145
+ # doctor_run [TARGET_DIR]
146
+ # ---------------------------------------------------------------------------
147
+ doctor_run() {
148
+ local target="${1:-${SPEC_TARGET_DIR:-}}"
149
+ local failures=0
150
+
151
+ printf "\n${_LOG_BOLD:-}Doctor: pre-install checks${_LOG_NC:-}\n" >&2
152
+
153
+ doctor_check_ruby || failures=$(( failures + 1 ))
154
+ doctor_check_bundler
155
+ doctor_check_docker
156
+ doctor_check_git || failures=$(( failures + 1 ))
157
+ doctor_check_gh
158
+ [[ -n "$target" ]] && { doctor_check_writable "$target" || failures=$(( failures + 1 )); }
159
+
160
+ if [[ $failures -eq 0 ]]; then
161
+ printf "\n${_LOG_GREEN:-}Doctor: all checks passed.${_LOG_NC:-}\n\n" >&2
162
+ return 0
163
+ fi
164
+
165
+ printf "\n${_LOG_YELLOW:-}Doctor: %d issue(s) found. See above.${_LOG_NC:-}\n\n" \
166
+ "$failures" >&2
167
+ return 1
168
+ }
@@ -0,0 +1,138 @@
1
+ #!/bin/bash
2
+ # =============================================================================
3
+ # scripts/install/fs.sh — Idempotent filesystem operations
4
+ # =============================================================================
5
+ # ALL file writes in the installer go through this module.
6
+ # Provides:
7
+ # fs_copy_file SRC DEST [--force] [--dry-run]
8
+ # fs_copy_dir SRC DEST [--force] [--dry-run]
9
+ # fs_write_file DEST CONTENT [--force] [--dry-run]
10
+ # fs_ensure_dir PATH [--dry-run]
11
+ # fs_backup_file FILE → creates FILE.backup.YYYYMMDD_HHMMSS
12
+ # fs_file_exists FILE → returns 0 if exists
13
+ #
14
+ # Globals read (set by caller / spec):
15
+ # _FS_DRY_RUN — "1" → never write, only log
16
+ # _FS_FORCE — "1" → overwrite without prompting (still backs up)
17
+ # _FS_BACKUP — "1" (default) → create backups before overwriting
18
+ # _FS_VERBOSE — "1" → extra debug output
19
+ #
20
+ # Bash 3.2 compatible. No set -euo pipefail here.
21
+ # =============================================================================
22
+ [[ -n "${_HAS_FS_LIB:-}" ]] && return 0
23
+ _HAS_FS_LIB=1
24
+
25
+ _FS_DRY_RUN="${_FS_DRY_RUN:-0}"
26
+ _FS_FORCE="${_FS_FORCE:-0}"
27
+ _FS_BACKUP="${_FS_BACKUP:-1}"
28
+ _FS_VERBOSE="${_FS_VERBOSE:-0}"
29
+
30
+ # Create a timestamped backup of FILE; prints backup path.
31
+ fs_backup_file() {
32
+ local file="$1"
33
+ [ -e "$file" ] || return 0
34
+ local bak="${file}.backup.$(date +%Y%m%d_%H%M%S)"
35
+ if [[ "${_FS_DRY_RUN:-0}" == "1" ]]; then
36
+ log_debug "[dry-run] would backup: $file → $bak"
37
+ return 0
38
+ fi
39
+ cp -a "$file" "$bak"
40
+ log_warning "Backed up: $(basename "$file") → $(basename "$bak")"
41
+ echo "$bak"
42
+ }
43
+
44
+ # Copy a single file SRC → DEST. Backs up DEST if it exists (unless --force).
45
+ fs_copy_file() {
46
+ local src="$1"
47
+ local dest="$2"
48
+
49
+ if [[ ! -f "$src" ]]; then
50
+ log_error "fs_copy_file: source not found: $src"
51
+ return 1
52
+ fi
53
+
54
+ if [[ "${_FS_DRY_RUN:-0}" == "1" ]]; then
55
+ log_info "[dry-run] copy: $src → $dest"
56
+ return 0
57
+ fi
58
+
59
+ fs_ensure_dir "$(dirname "$dest")"
60
+
61
+ if [[ -f "$dest" ]]; then
62
+ if [[ "${_FS_BACKUP:-1}" == "1" ]]; then
63
+ fs_backup_file "$dest" > /dev/null
64
+ fi
65
+ fi
66
+
67
+ cp "$src" "$dest"
68
+ log_info "Wrote: $dest"
69
+ }
70
+
71
+ # Recursively copy directory SRC → DEST.
72
+ fs_copy_dir() {
73
+ local src="$1"
74
+ local dest="$2"
75
+
76
+ if [[ ! -d "$src" ]]; then
77
+ log_error "fs_copy_dir: source not found: $src"
78
+ return 1
79
+ fi
80
+
81
+ if [[ "${_FS_DRY_RUN:-0}" == "1" ]]; then
82
+ log_info "[dry-run] copy dir: $src → $dest"
83
+ return 0
84
+ fi
85
+
86
+ if [[ -d "$dest" ]]; then
87
+ if [[ "${_FS_BACKUP:-1}" == "1" ]]; then
88
+ local bak="${dest}.backup.$(date +%Y%m%d_%H%M%S)"
89
+ cp -a "$dest" "$bak"
90
+ log_warning "Backed up dir: $(basename "$dest") → $(basename "$bak")"
91
+ fi
92
+ rm -rf "$dest"
93
+ fi
94
+
95
+ cp -r "$src" "$dest"
96
+ log_info "Copied dir: $dest"
97
+ }
98
+
99
+ # Write CONTENT string to DEST.
100
+ fs_write_file() {
101
+ local dest="$1"
102
+ local content="$2"
103
+
104
+ if [[ "${_FS_DRY_RUN:-0}" == "1" ]]; then
105
+ log_info "[dry-run] write: $dest"
106
+ return 0
107
+ fi
108
+
109
+ fs_ensure_dir "$(dirname "$dest")"
110
+
111
+ if [[ -f "$dest" && "${_FS_FORCE:-0}" != "1" ]]; then
112
+ log_warning "$(basename "$dest") already exists — skipping (use --force to overwrite)"
113
+ return 0
114
+ fi
115
+
116
+ if [[ -f "$dest" && "${_FS_BACKUP:-1}" == "1" ]]; then
117
+ fs_backup_file "$dest" > /dev/null
118
+ fi
119
+
120
+ printf '%s\n' "$content" > "$dest"
121
+ log_info "Wrote: $dest"
122
+ }
123
+
124
+ # Ensure directory exists (including parents).
125
+ fs_ensure_dir() {
126
+ local dir="$1"
127
+ [[ -d "$dir" ]] && return 0
128
+ if [[ "${_FS_DRY_RUN:-0}" == "1" ]]; then
129
+ log_debug "[dry-run] mkdir -p: $dir"
130
+ return 0
131
+ fi
132
+ mkdir -p "$dir"
133
+ log_debug "Created dir: $dir"
134
+ }
135
+
136
+ fs_file_exists() {
137
+ [[ -f "$1" ]]
138
+ }
@@ -0,0 +1,119 @@
1
+ #!/bin/bash
2
+ # =============================================================================
3
+ # scripts/install/log.sh — Logging primitives
4
+ # =============================================================================
5
+ # Provides: log_info, log_success, log_warning, log_error, log_debug,
6
+ # log_json, log_step, log_indent_push, log_indent_pop
7
+ #
8
+ # Output modes:
9
+ # human (default) — coloured, prefixed text to stderr
10
+ # json — machine-readable JSON lines to stdout
11
+ #
12
+ # Callers set _LOG_OUTPUT="json" before sourcing to switch modes.
13
+ # Callers set _LOG_VERBOSE=1 to enable log_debug output.
14
+ #
15
+ # Bash 3.2 compatible. No declare -A. No set -euo pipefail here.
16
+ # =============================================================================
17
+ [[ -n "${_HAS_LOG_LIB:-}" ]] && return 0
18
+ _HAS_LOG_LIB=1
19
+
20
+ # Colour codes — only apply when stderr is a TTY
21
+ _LOG_RED=''
22
+ _LOG_GREEN=''
23
+ _LOG_YELLOW=''
24
+ _LOG_BLUE=''
25
+ _LOG_CYAN=''
26
+ _LOG_BOLD=''
27
+ _LOG_NC=''
28
+ if [[ -t 2 ]]; then
29
+ _LOG_RED='\033[0;31m'
30
+ _LOG_GREEN='\033[0;32m'
31
+ _LOG_YELLOW='\033[1;33m'
32
+ _LOG_BLUE='\033[0;34m'
33
+ _LOG_CYAN='\033[0;36m'
34
+ _LOG_BOLD='\033[1m'
35
+ _LOG_NC='\033[0m'
36
+ fi
37
+
38
+ # Output mode: "human" | "json"
39
+ _LOG_OUTPUT="${_LOG_OUTPUT:-human}"
40
+ # Verbose: 1 = show debug lines
41
+ _LOG_VERBOSE="${_LOG_VERBOSE:-0}"
42
+ # Indent level (incremented by log_step sections)
43
+ _LOG_INDENT=0
44
+
45
+ # Internal: emit a single log line
46
+ _log_emit() {
47
+ local level="$1"
48
+ local msg="$2"
49
+ local prefix color
50
+ local indent=""
51
+ local i=0
52
+ while [[ $i -lt $_LOG_INDENT ]]; do
53
+ indent=" $indent"
54
+ i=$(( i + 1 ))
55
+ done
56
+ case "$level" in
57
+ INFO) color="$_LOG_BLUE"; prefix="[INFO]" ;;
58
+ SUCCESS) color="$_LOG_GREEN"; prefix="[SUCCESS]" ;;
59
+ WARNING) color="$_LOG_YELLOW"; prefix="[WARNING]" ;;
60
+ ERROR) color="$_LOG_RED"; prefix="[ERROR]" ;;
61
+ DEBUG) color="$_LOG_CYAN"; prefix="[DEBUG]" ;;
62
+ STEP) color="$_LOG_BOLD"; prefix="[STEP]" ;;
63
+ *) color="$_LOG_NC"; prefix="[$level]" ;;
64
+ esac
65
+
66
+ if [[ "$_LOG_OUTPUT" == "json" ]]; then
67
+ # Emit JSON line to stdout for machine parsing
68
+ local ts
69
+ ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date +"%Y-%m-%dT%H:%M:%SZ")
70
+ # Escape double quotes in message
71
+ local safe_msg
72
+ safe_msg=$(printf '%s' "$msg" | sed 's/\\/\\\\/g; s/"/\\"/g')
73
+ printf '{"ts":"%s","level":"%s","msg":"%s"}\n' "$ts" "$level" "$safe_msg"
74
+ else
75
+ printf "${color}${prefix}${_LOG_NC} %s%s\n" "$indent" "$msg" >&2
76
+ fi
77
+ }
78
+
79
+ log_info() { _log_emit "INFO" "$1"; }
80
+ log_success() { _log_emit "SUCCESS" "$1"; }
81
+ log_warning() { _log_emit "WARNING" "$1"; }
82
+ log_error() { _log_emit "ERROR" "$1"; }
83
+ log_debug() { [[ "${_LOG_VERBOSE:-0}" == "1" ]] && _log_emit "DEBUG" "$1" || true; }
84
+
85
+ # log_step TITLE — print a section header and increment indent
86
+ log_step() {
87
+ _log_emit "STEP" "── $1"
88
+ _LOG_INDENT=$(( _LOG_INDENT + 1 ))
89
+ }
90
+
91
+ # log_step_done — decrement indent after a section
92
+ log_step_done() {
93
+ [[ $_LOG_INDENT -gt 0 ]] && _LOG_INDENT=$(( _LOG_INDENT - 1 )) || true
94
+ }
95
+
96
+ # log_json KEY VALUE — emit a JSON key/value pair to stdout (for --output json)
97
+ # Use inside sections that need structured output regardless of log mode.
98
+ log_json() {
99
+ local key="$1"
100
+ local val="$2"
101
+ printf '{"key":"%s","value":"%s"}\n' "$key" "$val"
102
+ }
103
+
104
+ # log_banner TEXT — print a visible section banner
105
+ log_banner() {
106
+ local msg="$1"
107
+ local width=70
108
+ local line=""
109
+ local i=0
110
+ while [[ $i -lt $width ]]; do
111
+ line="${line}="
112
+ i=$(( i + 1 ))
113
+ done
114
+ if [[ "$_LOG_OUTPUT" != "json" ]]; then
115
+ printf "${_LOG_BOLD}%s${_LOG_NC}\n" "$line" >&2
116
+ printf "${_LOG_BOLD} %s${_LOG_NC}\n" "$msg" >&2
117
+ printf "${_LOG_BOLD}%s${_LOG_NC}\n" "$line" >&2
118
+ fi
119
+ }