jekyll-theme-zer0 1.26.0 → 1.28.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +558 -5
- data/README.md +10 -27
- data/_data/authors.yml +4 -3
- data/_data/backlog.yml +28 -0
- data/_data/consumers.yml +147 -0
- data/_data/features.yml +142 -23
- data/_data/i18n/fr.yml +98 -0
- data/_data/i18n/languages.yml +36 -0
- data/_data/i18n/manifest.yml +1502 -0
- data/_data/navigation/docs.yml +2 -0
- data/_data/navigation/main.yml +6 -0
- data/_data/series.yml +19 -0
- data/_data/theme-manifest.yml +348 -268
- data/_data/ui-text.yml +36 -246
- data/_includes/README.md +25 -2
- data/_includes/analytics/google-tag-manager-body.html +11 -2
- data/_includes/analytics/google-tag-manager-head.html +16 -6
- data/_includes/analytics/posthog.html +33 -4
- data/_includes/components/abc-letter.html +43 -0
- data/_includes/components/author-avatar-url.html +4 -2
- data/_includes/components/book-card.html +42 -0
- data/_includes/components/book-nav.html +80 -0
- data/_includes/components/book-plate.html +31 -0
- data/_includes/components/book-toc.html +47 -0
- data/_includes/components/bookshelf.html +68 -0
- data/_includes/components/card-grid.html +60 -0
- data/_includes/components/data-card.html +95 -0
- data/_includes/components/env-switcher.html +3 -1
- data/_includes/components/halfmoon.html +5 -1
- data/_includes/components/language-toggle.html +81 -0
- data/_includes/components/page-feedback.html +65 -2
- data/_includes/components/search-modal.html +2 -2
- data/_includes/components/shortcuts-modal.html +1 -1
- data/_includes/components/theme-controls-bar.html +10 -2
- data/_includes/components/theme-customizer.html +8 -2
- data/_includes/components/translation-notice.html +27 -0
- data/_includes/content/intro.html +16 -15
- data/_includes/content/seo.html +9 -3
- data/_includes/core/color-mode-init.html +13 -4
- data/_includes/core/favicon.html +46 -0
- data/_includes/core/footer.html +9 -4
- data/_includes/core/head.html +18 -0
- data/_includes/core/header.html +9 -4
- data/_includes/core/hreflang.html +33 -0
- data/_includes/core/i18n.html +36 -0
- data/_includes/custom/body-end.html +18 -0
- data/_includes/custom/body-start.html +17 -0
- data/_includes/custom/footer.html +18 -0
- data/_includes/custom/head.html +18 -0
- data/_includes/navigation/breadcrumbs.html +1 -1
- data/_includes/navigation/local-graph.html +28 -2
- data/_includes/navigation/nav-tree.html +3 -3
- data/_includes/navigation/navbar.html +5 -4
- data/_includes/navigation/sidebar-config.html +21 -0
- data/_includes/navigation/sidebar-nav.html +4 -0
- data/_includes/navigation/sidebar-pagetree.html +150 -0
- data/_includes/navigation/sidebar-right.html +3 -2
- data/_includes/navigation/unified-drawer.html +9 -3
- data/_includes/obsidian/full-graph.html +165 -136
- data/_layouts/404.html +260 -0
- data/_layouts/README.md +2 -0
- data/_layouts/article.html +7 -1
- data/_layouts/book-abc.html +106 -0
- data/_layouts/book-story.html +91 -0
- data/_layouts/book.html +113 -0
- data/_layouts/collection.html +17 -5
- data/_layouts/default.html +5 -3
- data/_layouts/home.html +5 -2
- data/_layouts/landing.html +9 -0
- data/_layouts/news.html +159 -33
- data/_layouts/root.html +29 -9
- data/_layouts/section.html +61 -17
- data/_sass/components/_book.scss +423 -0
- data/_sass/core/_navbar.scss +13 -0
- data/_sass/core/_obsidian.scss +295 -7
- data/_sass/layouts/_navbar-extras.scss +6 -1
- data/_sass/theme/_backgrounds.scss +21 -8
- data/assets/css/main.scss +1 -0
- data/assets/js/auto-hide-nav.js +5 -1
- data/assets/js/halfmoon.js +26 -0
- data/assets/js/obsidian-graph.js +707 -265
- data/assets/js/obsidian-local-graph.js +161 -54
- data/assets/js/search-modal.js +4 -1
- data/scripts/README.md +20 -26
- data/scripts/bin/audit-consumer +1 -1
- data/scripts/bin/manifest +31 -5
- data/scripts/bin/sync-plugins +0 -1
- data/scripts/bin/validate +5 -1
- data/scripts/dev/rasterize-svg.js +65 -0
- data/scripts/features/generate-preview-images +49 -1390
- data/scripts/features/install-preview-generator +55 -33
- data/scripts/install/README.md +55 -25
- data/scripts/install/ai/client.sh +302 -93
- data/scripts/install/ai/prompts/spec.schema.json +1 -1
- data/scripts/install/ai/prompts/wizard.system.md +8 -17
- data/scripts/install/ai/wizard.sh +10 -5
- data/scripts/install/apply.sh +7 -3
- data/scripts/install/cli.sh +54 -4
- data/scripts/install/config.sh +167 -0
- data/scripts/install/doctor.sh +38 -0
- data/scripts/install/plan.sh +10 -0
- data/scripts/install/spec.sh +15 -7
- data/scripts/install/template.sh +4 -0
- data/scripts/lib/README.md +1 -5
- data/scripts/lib/install/deploy/README.md +3 -9
- data/scripts/lib/preview_generator.py +2261 -1341
- data/scripts/propagate.rb +277 -0
- data/scripts/translate.rb +1204 -0
- metadata +34 -3
- data/_plugins/preview_image_generator.rb +0 -351
data/scripts/install/cli.sh
CHANGED
|
@@ -63,6 +63,7 @@ _cli_load_core() {
|
|
|
63
63
|
_cli_require "fs.sh"
|
|
64
64
|
_cli_require "template.sh"
|
|
65
65
|
_cli_require "prompt.sh"
|
|
66
|
+
_cli_require "config.sh"
|
|
66
67
|
_cli_require "spec.sh"
|
|
67
68
|
_cli_require "plan.sh"
|
|
68
69
|
_cli_require "apply.sh"
|
|
@@ -95,6 +96,9 @@ _cli_parse_flags() {
|
|
|
95
96
|
_FLAG_AGENTS=""
|
|
96
97
|
_FLAG_TASKS=""
|
|
97
98
|
_FLAG_AI=0
|
|
99
|
+
_FLAG_AI_PROVIDER=""
|
|
100
|
+
_FLAG_AI_MODEL=""
|
|
101
|
+
_FLAG_CONFIG=""
|
|
98
102
|
_FLAG_SPEC=""
|
|
99
103
|
_FLAG_SCRAPE_URL=""
|
|
100
104
|
_FLAG_SCRAPE_DEPTH=""
|
|
@@ -111,6 +115,7 @@ _cli_parse_flags() {
|
|
|
111
115
|
_FLAG_SITE_URL _FLAG_SITE_AUTHOR _FLAG_SITE_EMAIL \
|
|
112
116
|
_FLAG_GITHUB_USER _FLAG_GITHUB_REPO _FLAG_THEME_SOURCE \
|
|
113
117
|
_FLAG_DEPLOY _FLAG_AGENTS _FLAG_TASKS _FLAG_AI _FLAG_SPEC \
|
|
118
|
+
_FLAG_AI_PROVIDER _FLAG_AI_MODEL _FLAG_CONFIG \
|
|
114
119
|
_FLAG_SCRAPE_URL _FLAG_SCRAPE_DEPTH _FLAG_SCRAPE_MAX_PAGES
|
|
115
120
|
|
|
116
121
|
while [[ $# -gt 0 ]]; do
|
|
@@ -123,6 +128,14 @@ _cli_parse_flags() {
|
|
|
123
128
|
--skip-doctor) _FLAG_SKIP_DOCTOR=1 ;;
|
|
124
129
|
--verbose|-v) _FLAG_VERBOSE=1 ;;
|
|
125
130
|
--ai) _FLAG_AI=1 ;;
|
|
131
|
+
--yes|-y) _FLAG_AUTO_ACCEPT=1 ;;
|
|
132
|
+
--ai-provider) shift; _FLAG_AI_PROVIDER="${1:-}" ;;
|
|
133
|
+
--ai-provider=*) _FLAG_AI_PROVIDER="${1#--ai-provider=}" ;;
|
|
134
|
+
--ai-model) shift; _FLAG_AI_MODEL="${1:-}" ;;
|
|
135
|
+
--ai-model=*) _FLAG_AI_MODEL="${1#--ai-model=}" ;;
|
|
136
|
+
--config) shift; _FLAG_CONFIG="${1:-}" ;;
|
|
137
|
+
--config=*) _FLAG_CONFIG="${1#--config=}" ;;
|
|
138
|
+
--no-ai) _FLAG_AI_PROVIDER="none" ;;
|
|
126
139
|
--profile) shift; _FLAG_PROFILE="${1:-}" ;;
|
|
127
140
|
--profile=*) _FLAG_PROFILE="${1#--profile=}" ;;
|
|
128
141
|
--output) shift; _FLAG_OUTPUT="${1:-}" ;;
|
|
@@ -241,6 +254,22 @@ _cmd_wizard() {
|
|
|
241
254
|
fi
|
|
242
255
|
}
|
|
243
256
|
|
|
257
|
+
_cmd_suggest() {
|
|
258
|
+
# install suggest [TARGET_DIR] [GOAL...]
|
|
259
|
+
# Recommends a profile + deploy target from an optional goal description.
|
|
260
|
+
# Uses the resolved AI provider; falls back to rule-based defaults.
|
|
261
|
+
local target="${_CLI_POS_0:-$(pwd)}"
|
|
262
|
+
local goal="${_CLI_POS_1:-}"
|
|
263
|
+
local sug="${_CLI_DIR}/ai/suggest.sh"
|
|
264
|
+
if [[ ! -f "$sug" ]]; then
|
|
265
|
+
log_error "suggest: module not available: $sug"
|
|
266
|
+
return 1
|
|
267
|
+
fi
|
|
268
|
+
# shellcheck source=/dev/null
|
|
269
|
+
source "$sug"
|
|
270
|
+
ai_suggest_run "$target" "$goal"
|
|
271
|
+
}
|
|
272
|
+
|
|
244
273
|
_cmd_agents() {
|
|
245
274
|
local target="${_CLI_TARGET:-$(pwd)}"
|
|
246
275
|
local spec_file
|
|
@@ -299,8 +328,12 @@ _cmd_deploy() {
|
|
|
299
328
|
fi
|
|
300
329
|
|
|
301
330
|
# Caller explicitly asked for this deploy target — override spec defaults.
|
|
331
|
+
# Deploy is deploy-only: no base tasks and no agent rewrites (use
|
|
332
|
+
# `install agents` for those). SPEC_TASKS="" now serialises to [] rather
|
|
333
|
+
# than falling back to the full default list (which would clobber content).
|
|
302
334
|
SPEC_DEPLOY="$target_name"
|
|
303
|
-
SPEC_TASKS=""
|
|
335
|
+
SPEC_TASKS=""
|
|
336
|
+
SPEC_AGENTS=""
|
|
304
337
|
|
|
305
338
|
spec_write "$spec_file"
|
|
306
339
|
apply_run "$spec_file"
|
|
@@ -460,9 +493,10 @@ Usage:
|
|
|
460
493
|
Subcommands:
|
|
461
494
|
init Install from flags + profile (default)
|
|
462
495
|
wizard Interactive wizard (--ai for AI-driven spec)
|
|
496
|
+
suggest Recommend a profile + deploy target (AI or rule-based)
|
|
463
497
|
agents Install AI agent files only
|
|
464
498
|
deploy Configure deployment plugin(s)
|
|
465
|
-
doctor Run pre-install health checks
|
|
499
|
+
doctor Run pre-install health checks (incl. AI provider status)
|
|
466
500
|
scrape Crawl an existing site into ./.zer0/scrape (no install)
|
|
467
501
|
diagnose Diagnose an existing install (--ai for suggestions)
|
|
468
502
|
upgrade Re-apply spec to an existing install
|
|
@@ -494,7 +528,12 @@ Global options:
|
|
|
494
528
|
--skip-doctor Skip pre-install health checks
|
|
495
529
|
--verbose, -v Extra debug output
|
|
496
530
|
--output json|human Log format (default: human)
|
|
531
|
+
--yes, -y Auto-confirm all yes/no prompts (alias of --auto-accept)
|
|
532
|
+
--config FILE Load installer settings from a YAML config file
|
|
497
533
|
--ai Use AI assistant for wizard/diagnose
|
|
534
|
+
--ai-provider NAME AI provider: auto|claude-cli|anthropic|openai|none
|
|
535
|
+
--ai-model NAME Override the AI model id for the active provider
|
|
536
|
+
--no-ai Disable AI (equivalent to --ai-provider none)
|
|
498
537
|
--spec FILE Load spec from FILE instead of detecting
|
|
499
538
|
--scrape URL Import content from URL during init (adds 'scrape' task)
|
|
500
539
|
--scrape-depth N Max crawl depth (default: 2)
|
|
@@ -503,9 +542,12 @@ Global options:
|
|
|
503
542
|
Examples:
|
|
504
543
|
install init . --profile blog --site-title "My Blog"
|
|
505
544
|
install init ~/mysite --profile github-pages --github-user bamr87
|
|
506
|
-
install wizard . --ai
|
|
545
|
+
install wizard . --ai # uses Claude Code OAuth if available
|
|
546
|
+
install wizard . --ai --ai-provider anthropic
|
|
547
|
+
install suggest . "a docs site for a Rust CLI"
|
|
548
|
+
install init . --config zer0.install.yml
|
|
507
549
|
install agents . --copilot --claude
|
|
508
|
-
install doctor .
|
|
550
|
+
install doctor . # includes AI provider status
|
|
509
551
|
install diff .
|
|
510
552
|
install upgrade . --force
|
|
511
553
|
install plan . --profile docs
|
|
@@ -537,9 +579,17 @@ cli_main() {
|
|
|
537
579
|
[[ "$_FLAG_VERBOSE" == "1" ]] && _LOG_VERBOSE=1 && export _LOG_VERBOSE
|
|
538
580
|
[[ -n "$_FLAG_OUTPUT" ]] && _LOG_OUTPUT="$_FLAG_OUTPUT" && export _LOG_OUTPUT
|
|
539
581
|
|
|
582
|
+
# Propagate AI selection + config path to the environment so provider
|
|
583
|
+
# resolution and config discovery see them regardless of the spec flow
|
|
584
|
+
# (e.g. `doctor`, `suggest`, `wizard --ai`).
|
|
585
|
+
[[ -n "$_FLAG_AI_PROVIDER" ]] && export ZER0_AI_PROVIDER="$_FLAG_AI_PROVIDER"
|
|
586
|
+
[[ -n "$_FLAG_AI_MODEL" ]] && export ZER0_AI_MODEL="$_FLAG_AI_MODEL"
|
|
587
|
+
[[ -n "$_FLAG_CONFIG" ]] && export ZER0_CONFIG="$_FLAG_CONFIG"
|
|
588
|
+
|
|
540
589
|
case "$subcommand" in
|
|
541
590
|
init) _cmd_init ;;
|
|
542
591
|
wizard) _cmd_wizard ;;
|
|
592
|
+
suggest) _cmd_suggest ;;
|
|
543
593
|
agents) _cmd_agents ;;
|
|
544
594
|
deploy) _cmd_deploy ;;
|
|
545
595
|
doctor) _cmd_doctor ;;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# scripts/install/config.sh — User config-file layer
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Lets a site persist its installer choices in a small YAML file instead of
|
|
6
|
+
# re-typing flags every run. Discovered files are merged low→high precedence:
|
|
7
|
+
#
|
|
8
|
+
# 1. ~/.config/zer0/install.yml (user-global defaults)
|
|
9
|
+
# 2. <target>/zer0.install.yml (project-local)
|
|
10
|
+
# 3. <target>/.zer0/config.yml (project-local, hidden)
|
|
11
|
+
# 4. $ZER0_CONFIG / --config FILE (explicit — wins over the rest)
|
|
12
|
+
#
|
|
13
|
+
# In the plan pipeline this sits between profile defaults and env/flag
|
|
14
|
+
# overrides: defaults < profile < CONFIG FILE < env vars < CLI flags.
|
|
15
|
+
#
|
|
16
|
+
# Recognised keys (nested or flat/dotted both work):
|
|
17
|
+
# profile
|
|
18
|
+
# site.title|description|author|email|url|timezone|locale
|
|
19
|
+
# github.user|repo|pages_branch|enable_pages
|
|
20
|
+
# theme.source|version
|
|
21
|
+
# deploy (scalar or list → space-joined)
|
|
22
|
+
# agents (scalar or list → space-joined)
|
|
23
|
+
# tasks (scalar or list → space-joined)
|
|
24
|
+
# ai.provider (auto|claude-cli|anthropic|openai|none)
|
|
25
|
+
# ai.model
|
|
26
|
+
#
|
|
27
|
+
# API keys are deliberately NOT read from config files — they stay in the
|
|
28
|
+
# environment only (see ai/client.sh).
|
|
29
|
+
#
|
|
30
|
+
# Bash 3.2 compatible. No `declare -A`, no `set -euo pipefail` here.
|
|
31
|
+
# =============================================================================
|
|
32
|
+
[[ -n "${_HAS_CONFIG_LIB:-}" ]] && return 0
|
|
33
|
+
_HAS_CONFIG_LIB=1
|
|
34
|
+
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
# config_discover TARGET_DIR → print existing config files, low→high priority.
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
config_discover() {
|
|
39
|
+
local target="${1:-$(pwd)}"
|
|
40
|
+
local f
|
|
41
|
+
for f in \
|
|
42
|
+
"${HOME:-/nonexistent}/.config/zer0/install.yml" \
|
|
43
|
+
"${target}/zer0.install.yml" \
|
|
44
|
+
"${target}/zer0.install.yaml" \
|
|
45
|
+
"${target}/.zer0/config.yml" \
|
|
46
|
+
"${_FLAG_CONFIG:-}" \
|
|
47
|
+
"${ZER0_CONFIG:-}" ; do
|
|
48
|
+
[[ -n "$f" && -f "$f" ]] && printf '%s\n' "$f"
|
|
49
|
+
done
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# ---------------------------------------------------------------------------
|
|
53
|
+
# _config_flatten FILE → emit `key=value` for a small YAML subset:
|
|
54
|
+
# - top-level scalars foo: bar → foo=bar
|
|
55
|
+
# - one level of nested maps site:\n title: x → site.title=x
|
|
56
|
+
# - block lists deploy:\n - a → deploy=a b
|
|
57
|
+
# - inline flow lists deploy: [a, b] → deploy=a b
|
|
58
|
+
# Quotes and inline comments are stripped. Not a full YAML parser — just
|
|
59
|
+
# enough for installer config, mirroring plan.sh's awk profile reader.
|
|
60
|
+
# ---------------------------------------------------------------------------
|
|
61
|
+
_config_flatten() {
|
|
62
|
+
local file="$1"
|
|
63
|
+
[[ -f "$file" ]] || return 0
|
|
64
|
+
awk '
|
|
65
|
+
function strip(s) {
|
|
66
|
+
sub(/[[:space:]]*#.*$/, "", s) # trailing comment
|
|
67
|
+
sub(/^[[:space:]]+/, "", s); sub(/[[:space:]]+$/, "", s)
|
|
68
|
+
gsub(/^["'"'"']|["'"'"']$/, "", s) # surrounding quotes
|
|
69
|
+
return s
|
|
70
|
+
}
|
|
71
|
+
# blank / comment lines
|
|
72
|
+
/^[[:space:]]*$/ { next }
|
|
73
|
+
/^[[:space:]]*#/ { next }
|
|
74
|
+
{
|
|
75
|
+
indent = 0
|
|
76
|
+
tmp = $0
|
|
77
|
+
while (substr(tmp,1,1) == " ") { indent++; tmp = substr(tmp,2) }
|
|
78
|
+
}
|
|
79
|
+
# list item under the current parent key
|
|
80
|
+
indent > 0 && tmp ~ /^-[[:space:]]+/ {
|
|
81
|
+
if (listkey != "") {
|
|
82
|
+
item = tmp; sub(/^-[[:space:]]+/, "", item); item = strip(item)
|
|
83
|
+
vals[listkey] = (vals[listkey] == "" ? item : vals[listkey] " " item)
|
|
84
|
+
}
|
|
85
|
+
next
|
|
86
|
+
}
|
|
87
|
+
# key: value (any depth we care about is 0 or 1)
|
|
88
|
+
match(tmp, /^[A-Za-z0-9_.-]+[[:space:]]*:/) {
|
|
89
|
+
key = tmp; sub(/[[:space:]]*:.*$/, "", key)
|
|
90
|
+
val = tmp; sub(/^[A-Za-z0-9_.-]+[[:space:]]*:/, "", val); val = strip(val)
|
|
91
|
+
|
|
92
|
+
if (indent == 0) {
|
|
93
|
+
parent = key
|
|
94
|
+
listkey = ""
|
|
95
|
+
if (val == "") { listkey = key; next } # map/list header
|
|
96
|
+
# inline flow list? key: [a, b, c]
|
|
97
|
+
if (val ~ /^\[.*\]$/) {
|
|
98
|
+
gsub(/^\[|\]$/, "", val); gsub(/[[:space:],]+/, " ", val)
|
|
99
|
+
val = strip(val)
|
|
100
|
+
}
|
|
101
|
+
print key "=" val
|
|
102
|
+
} else {
|
|
103
|
+
# nested child of the last top-level parent
|
|
104
|
+
if (parent != "") print parent "." key "=" val
|
|
105
|
+
}
|
|
106
|
+
next
|
|
107
|
+
}
|
|
108
|
+
END {
|
|
109
|
+
for (k in vals) print k "=" vals[k]
|
|
110
|
+
}
|
|
111
|
+
' "$file"
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
# ---------------------------------------------------------------------------
|
|
115
|
+
# config_apply_file FILE → map recognised keys onto SPEC_* globals + AI env.
|
|
116
|
+
# ---------------------------------------------------------------------------
|
|
117
|
+
config_apply_file() {
|
|
118
|
+
local file="$1"
|
|
119
|
+
[[ -f "$file" ]] || return 0
|
|
120
|
+
log_debug "config: loading ${file}"
|
|
121
|
+
|
|
122
|
+
local line key val
|
|
123
|
+
while IFS= read -r line; do
|
|
124
|
+
key="${line%%=*}"
|
|
125
|
+
val="${line#*=}"
|
|
126
|
+
[[ -z "$key" || "$key" == "$line" ]] && continue
|
|
127
|
+
case "$key" in
|
|
128
|
+
profile) SPEC_PROFILE="$val" ;;
|
|
129
|
+
site.title|site_title) SPEC_SITE_TITLE="$val" ;;
|
|
130
|
+
site.description|site_description) SPEC_SITE_DESCRIPTION="$val" ;;
|
|
131
|
+
site.author|site_author) SPEC_SITE_AUTHOR="$val" ;;
|
|
132
|
+
site.email|site_email) SPEC_SITE_EMAIL="$val" ;;
|
|
133
|
+
site.url|site_url) SPEC_SITE_URL="$val" ;;
|
|
134
|
+
site.timezone|site_timezone) SPEC_SITE_TIMEZONE="$val" ;;
|
|
135
|
+
site.locale|site_locale) SPEC_SITE_LOCALE="$val" ;;
|
|
136
|
+
github.user|github_user) SPEC_GITHUB_USER="$val" ;;
|
|
137
|
+
github.repo|github_repo) SPEC_GITHUB_REPO="$val" ;;
|
|
138
|
+
github.pages_branch) SPEC_GITHUB_PAGES_BRANCH="$val" ;;
|
|
139
|
+
github.enable_pages) SPEC_GITHUB_ENABLE_PAGES="$val" ;;
|
|
140
|
+
theme.source|theme_source) SPEC_THEME_SOURCE="$val" ;;
|
|
141
|
+
theme.version|theme_version) SPEC_THEME_VERSION="$val" ;;
|
|
142
|
+
deploy|deploy_targets) SPEC_DEPLOY="$val" ;;
|
|
143
|
+
agents|agent_files) SPEC_AGENTS="$val" ;;
|
|
144
|
+
tasks) SPEC_TASKS="$val" ;;
|
|
145
|
+
ai.provider|ai_provider)
|
|
146
|
+
SPEC_AI_PROVIDER="$val"
|
|
147
|
+
export ZER0_AI_PROVIDER="${ZER0_AI_PROVIDER:-$val}" ;;
|
|
148
|
+
ai.model|ai_model)
|
|
149
|
+
SPEC_AI_MODEL="$val"
|
|
150
|
+
export ZER0_AI_MODEL="${ZER0_AI_MODEL:-$val}" ;;
|
|
151
|
+
*) log_debug "config: ignoring unknown key '${key}'" ;;
|
|
152
|
+
esac
|
|
153
|
+
done < <(_config_flatten "$file")
|
|
154
|
+
return 0
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
# ---------------------------------------------------------------------------
|
|
158
|
+
# config_load TARGET_DIR → apply every discovered config file in order.
|
|
159
|
+
# ---------------------------------------------------------------------------
|
|
160
|
+
config_load() {
|
|
161
|
+
local target="${1:-$(pwd)}"
|
|
162
|
+
local f
|
|
163
|
+
while IFS= read -r f; do
|
|
164
|
+
[[ -n "$f" ]] && config_apply_file "$f"
|
|
165
|
+
done < <(config_discover "$target")
|
|
166
|
+
return 0
|
|
167
|
+
}
|
data/scripts/install/doctor.sh
CHANGED
|
@@ -118,6 +118,43 @@ doctor_check_gh() {
|
|
|
118
118
|
_doctor_pass "GitHub CLI $ghver"
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
# ---------------------------------------------------------------------------
|
|
122
|
+
# doctor_check_ai — report which AI provider (if any) will serve wizard/
|
|
123
|
+
# suggest/diagnose. Warn-only: AI is always opt-in and never blocks install.
|
|
124
|
+
# ---------------------------------------------------------------------------
|
|
125
|
+
doctor_check_ai() {
|
|
126
|
+
if [[ "$(type -t ai_client_provider)" != "function" ]]; then
|
|
127
|
+
local _d
|
|
128
|
+
_d="${_CLI_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" 2>/dev/null && pwd)}"
|
|
129
|
+
[[ -f "${_d}/ai/client.sh" ]] && source "${_d}/ai/client.sh"
|
|
130
|
+
fi
|
|
131
|
+
if [[ "$(type -t ai_client_provider)" != "function" ]]; then
|
|
132
|
+
_doctor_warn "AI: client module unavailable (wizard/suggest/diagnose disabled)"
|
|
133
|
+
return 0
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
if [[ "${ZER0_NO_AI:-0}" == "1" ]]; then
|
|
137
|
+
_doctor_pass "AI: disabled via ZER0_NO_AI (rule-based fallbacks only)"
|
|
138
|
+
return 0
|
|
139
|
+
fi
|
|
140
|
+
case "${ZER0_AI_PROVIDER:-}" in
|
|
141
|
+
none|off|disabled)
|
|
142
|
+
_doctor_pass "AI: disabled by choice (rule-based fallbacks only)"
|
|
143
|
+
return 0 ;;
|
|
144
|
+
esac
|
|
145
|
+
|
|
146
|
+
local provider
|
|
147
|
+
provider="$(ai_client_provider)"
|
|
148
|
+
if [[ "$provider" == "none" ]] || ! ai_client_available; then
|
|
149
|
+
_doctor_warn "AI: no provider available — install the 'claude' CLI (Claude Code OAuth), or set CLAUDE_CODE_OAUTH_TOKEN / ANTHROPIC_API_KEY / OPENAI_API_KEY"
|
|
150
|
+
return 0
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
local model
|
|
154
|
+
model="$(ai_client_model "$provider")"
|
|
155
|
+
_doctor_pass "AI: ${provider} via $(ai_client_auth_source)${model:+ (model ${model})}"
|
|
156
|
+
}
|
|
157
|
+
|
|
121
158
|
# ---------------------------------------------------------------------------
|
|
122
159
|
# doctor_check_writable TARGET_DIR
|
|
123
160
|
# ---------------------------------------------------------------------------
|
|
@@ -155,6 +192,7 @@ doctor_run() {
|
|
|
155
192
|
doctor_check_docker
|
|
156
193
|
doctor_check_git || failures=$(( failures + 1 ))
|
|
157
194
|
doctor_check_gh
|
|
195
|
+
doctor_check_ai
|
|
158
196
|
[[ -n "$target" ]] && { doctor_check_writable "$target" || failures=$(( failures + 1 )); }
|
|
159
197
|
|
|
160
198
|
if [[ $failures -eq 0 ]]; then
|
data/scripts/install/plan.sh
CHANGED
|
@@ -144,6 +144,8 @@ plan_apply_flags() {
|
|
|
144
144
|
[[ -n "${_FLAG_DEPLOY:-}" ]] && SPEC_DEPLOY="$_FLAG_DEPLOY"
|
|
145
145
|
[[ -n "${_FLAG_AGENTS:-}" ]] && SPEC_AGENTS="$_FLAG_AGENTS"
|
|
146
146
|
[[ -n "${_FLAG_TASKS:-}" ]] && SPEC_TASKS="$_FLAG_TASKS"
|
|
147
|
+
[[ -n "${_FLAG_AI_PROVIDER:-}" ]] && SPEC_AI_PROVIDER="$_FLAG_AI_PROVIDER"
|
|
148
|
+
[[ -n "${_FLAG_AI_MODEL:-}" ]] && SPEC_AI_MODEL="$_FLAG_AI_MODEL"
|
|
147
149
|
|
|
148
150
|
# Booleans — flags are set to "1" when present
|
|
149
151
|
[[ "${_FLAG_DRY_RUN:-0}" == "1" ]] && SPEC_OPT_DRY_RUN=true
|
|
@@ -231,12 +233,19 @@ plan_build() {
|
|
|
231
233
|
# Layer 1: Profile defaults
|
|
232
234
|
[[ -n "$profile_file" ]] && plan_load_profile "$profile_file"
|
|
233
235
|
|
|
236
|
+
# Layer 1.5: User config file(s) — override profile, sit below env/flags.
|
|
237
|
+
if [[ "$(type -t config_load)" == "function" ]]; then
|
|
238
|
+
config_load "$target_dir"
|
|
239
|
+
fi
|
|
240
|
+
|
|
234
241
|
# Layer 2: Environment variable overrides (ZER0_SITE_* etc.)
|
|
235
242
|
[[ -n "${ZER0_SITE_TITLE:-}" ]] && SPEC_SITE_TITLE="$ZER0_SITE_TITLE"
|
|
236
243
|
[[ -n "${ZER0_SITE_AUTHOR:-}" ]] && SPEC_SITE_AUTHOR="$ZER0_SITE_AUTHOR"
|
|
237
244
|
[[ -n "${ZER0_SITE_EMAIL:-}" ]] && SPEC_SITE_EMAIL="$ZER0_SITE_EMAIL"
|
|
238
245
|
[[ -n "${ZER0_GITHUB_USER:-}" ]] && SPEC_GITHUB_USER="$ZER0_GITHUB_USER"
|
|
239
246
|
[[ -n "${ZER0_GITHUB_REPO:-}" ]] && SPEC_GITHUB_REPO="$ZER0_GITHUB_REPO"
|
|
247
|
+
[[ -n "${ZER0_AI_PROVIDER:-}" ]] && SPEC_AI_PROVIDER="$ZER0_AI_PROVIDER"
|
|
248
|
+
[[ -n "${ZER0_AI_MODEL:-}" ]] && SPEC_AI_MODEL="$ZER0_AI_MODEL"
|
|
240
249
|
|
|
241
250
|
# Layer 3: CLI flags (highest priority)
|
|
242
251
|
plan_apply_flags
|
|
@@ -271,6 +280,7 @@ plan_build() {
|
|
|
271
280
|
SPEC_OPT_DRY_RUN SPEC_OPT_FORCE SPEC_OPT_BACKUP \
|
|
272
281
|
SPEC_OPT_NON_INTERACTIVE SPEC_OPT_OUTPUT SPEC_OPT_AUTO_ACCEPT \
|
|
273
282
|
SPEC_OPT_SKIP_DOCTOR SPEC_OPT_VERBOSE \
|
|
283
|
+
SPEC_AI_PROVIDER SPEC_AI_MODEL \
|
|
274
284
|
SITE_TITLE SITE_DESCRIPTION SITE_AUTHOR SITE_EMAIL SITE_URL \
|
|
275
285
|
SITE_TIMEZONE SITE_LOCALE GITHUB_USER GITHUB_REPO \
|
|
276
286
|
GITHUB_PAGES_BRANCH THEME_SOURCE REPOSITORY_NAME INSTALL_PROFILE
|
data/scripts/install/spec.sh
CHANGED
|
@@ -85,7 +85,7 @@ spec_default() {
|
|
|
85
85
|
},
|
|
86
86
|
"ai": {
|
|
87
87
|
"used": false,
|
|
88
|
-
"provider": "
|
|
88
|
+
"provider": "auto",
|
|
89
89
|
"model": "",
|
|
90
90
|
"tokens_estimated": 0,
|
|
91
91
|
"spec_hash": ""
|
|
@@ -108,10 +108,18 @@ spec_write() {
|
|
|
108
108
|
local out_file="$1"
|
|
109
109
|
local dry_run="${_FS_DRY_RUN:-0}"
|
|
110
110
|
|
|
111
|
-
# Build tasks array from space-separated SPEC_TASKS
|
|
111
|
+
# Build tasks array from space-separated SPEC_TASKS.
|
|
112
|
+
# Distinguish UNSET (→ default task list) from EMPTY (→ [], e.g. a
|
|
113
|
+
# deploy-only run). Using `${SPEC_TASKS:-default}` here would turn an
|
|
114
|
+
# intentional empty list back into the full default and clobber content.
|
|
112
115
|
local tasks_json=""
|
|
113
|
-
local t
|
|
114
|
-
|
|
116
|
+
local t _tasks_src
|
|
117
|
+
if [[ -z "${SPEC_TASKS+set}" ]]; then
|
|
118
|
+
_tasks_src="config gemfile docker pages nav data gitignore readme marker"
|
|
119
|
+
else
|
|
120
|
+
_tasks_src="${SPEC_TASKS}"
|
|
121
|
+
fi
|
|
122
|
+
for t in ${_tasks_src}; do
|
|
115
123
|
tasks_json="${tasks_json}\"${t}\","
|
|
116
124
|
done
|
|
117
125
|
tasks_json="[${tasks_json%,}]"
|
|
@@ -168,7 +176,7 @@ spec_write() {
|
|
|
168
176
|
},
|
|
169
177
|
"ai": {
|
|
170
178
|
"used": ${SPEC_AI_USED:-false},
|
|
171
|
-
"provider": "${SPEC_AI_PROVIDER:-
|
|
179
|
+
"provider": "${SPEC_AI_PROVIDER:-auto}",
|
|
172
180
|
"model": "${SPEC_AI_MODEL:-}",
|
|
173
181
|
"tokens_estimated": ${SPEC_AI_TOKENS:-0},
|
|
174
182
|
"spec_hash": ""
|
|
@@ -248,7 +256,7 @@ _spec_read_jq() {
|
|
|
248
256
|
SPEC_OPT_SKIP_DOCTOR=$(jq -r '.options.skip_doctor // false' "$f")
|
|
249
257
|
SPEC_OPT_VERBOSE=$(jq -r '.options.verbose // false' "$f")
|
|
250
258
|
SPEC_AI_USED=$(jq -r '.ai.used // false' "$f")
|
|
251
|
-
SPEC_AI_PROVIDER=$(jq -r '.ai.provider // "
|
|
259
|
+
SPEC_AI_PROVIDER=$(jq -r '.ai.provider // "auto"' "$f")
|
|
252
260
|
SPEC_AI_MODEL=$(jq -r '.ai.model // ""' "$f")
|
|
253
261
|
SPEC_AI_TOKENS=$(jq -r '.ai.tokens_estimated // 0' "$f")
|
|
254
262
|
SPEC_SCRAPE_SOURCE_URL=$(jq -r '.scrape.source_url // ""' "$f")
|
|
@@ -369,7 +377,7 @@ _spec_read_awk() {
|
|
|
369
377
|
SPEC_OPT_SKIP_DOCTOR="${SPEC_OPT_SKIP_DOCTOR:-false}"
|
|
370
378
|
SPEC_OPT_VERBOSE="${SPEC_OPT_VERBOSE:-false}"
|
|
371
379
|
SPEC_AI_USED="${SPEC_AI_USED:-false}"
|
|
372
|
-
SPEC_AI_PROVIDER="${SPEC_AI_PROVIDER:-
|
|
380
|
+
SPEC_AI_PROVIDER="${SPEC_AI_PROVIDER:-auto}"
|
|
373
381
|
SPEC_AI_MODEL="${SPEC_AI_MODEL:-}"
|
|
374
382
|
SPEC_AI_TOKENS="${SPEC_AI_TOKENS:-0}"
|
|
375
383
|
SPEC_SCRAPE_SOURCE_URL="${SPEC_SCRAPE_SOURCE_URL:-$(awk '/"source_url"/ { gsub(/.*"source_url"[[:space:]]*:[[:space:]]*"/, ""); gsub(/".*/, ""); print; exit }' "$f" 2>/dev/null)}"
|
data/scripts/install/template.sh
CHANGED
|
@@ -61,6 +61,7 @@ tmpl_render() {
|
|
|
61
61
|
-e "s|{{THEME_DISPLAY_NAME}}|${THEME_DISPLAY_NAME:-Zer0-Mistakes}|g" \
|
|
62
62
|
-e "s|{{THEME_VERSION}}|${THEME_VERSION:-}|g" \
|
|
63
63
|
-e "s|{{THEME_SOURCE}}|${THEME_SOURCE:-gem}|g" \
|
|
64
|
+
-e "s|{{THEME_REMOTE}}|${THEME_REMOTE:-bamr87/zer0-mistakes}|g" \
|
|
64
65
|
-e "s|{{GITHUB_USER}}|${GITHUB_USER:-}|g" \
|
|
65
66
|
-e "s|{{FORK_GITHUB_USER}}|${GITHUB_USER:-}|g" \
|
|
66
67
|
-e "s|{{GITHUB_REPO}}|${GITHUB_REPO:-}|g" \
|
|
@@ -72,6 +73,7 @@ tmpl_render() {
|
|
|
72
73
|
-e "s|{{REMOTE_BRANCH}}|${REMOTE_BRANCH:-${GITHUB_PAGES_BRANCH:-gh-pages}}|g" \
|
|
73
74
|
-e "s|{{REPOSITORY_NAME}}|${REPOSITORY_NAME:-${GITHUB_REPO:-my-site}}|g" \
|
|
74
75
|
-e "s|{{SITE_TITLE}}|${SITE_TITLE:-My Jekyll Site}|g" \
|
|
76
|
+
-e "s|{{SITE_NAME}}|${SITE_TITLE:-My Jekyll Site}|g" \
|
|
75
77
|
-e "s|{{SITE_DESCRIPTION}}|${SITE_DESCRIPTION:-A Jekyll site built with zer0-mistakes}|g" \
|
|
76
78
|
-e "s|{{SITE_AUTHOR}}|${SITE_AUTHOR:-Site Author}|g" \
|
|
77
79
|
-e "s|{{SITE_EMAIL}}|${SITE_EMAIL:-}|g" \
|
|
@@ -87,6 +89,8 @@ tmpl_render() {
|
|
|
87
89
|
-e "s|{{COMMONMARKER_MACOS_VERSION}}|${COMMONMARKER_MACOS_VERSION:-~> 0.23}|g" \
|
|
88
90
|
-e "s|{{GITHUB_PAGES_MAX_VERSION}}|${GITHUB_PAGES_MAX_VERSION:-232}|g" \
|
|
89
91
|
-e "s|{{RUBY_MIN_VERSION_MACOS}}|${RUBY_MIN_VERSION_MACOS:-2.6.0}|g" \
|
|
92
|
+
-e "s|{{DEFAULT_BRANCH}}|${DEFAULT_BRANCH:-main}|g" \
|
|
93
|
+
-e "s|{{RUBY_VERSION}}|${RUBY_VERSION:-3.3}|g" \
|
|
90
94
|
-e "s|{{INSTALL_PROFILE}}|${INSTALL_PROFILE:-default}|g" \
|
|
91
95
|
-e "s|{{INSTALL_MODE}}|${INSTALL_MODE:-full}|g" \
|
|
92
96
|
-e "s|{{CURRENT_DATE}}|$(date +%Y-%m-%d)|g" \
|
data/scripts/lib/README.md
CHANGED
|
@@ -49,11 +49,7 @@ validate_environment false false # skip_publish=false, require_gh=false
|
|
|
49
49
|
|
|
50
50
|
### ✅ `scripts/bin/validate` - Preflight Validation
|
|
51
51
|
|
|
52
|
-
Canonical command for local and CI preflight checks. It composes the release
|
|
53
|
-
validation helpers with project-specific checks for version consistency, YAML
|
|
54
|
-
configuration/data files, active configuration contracts, config-file
|
|
55
|
-
classification, navigation data shape, Jekyll build/doctor, compiled assets,
|
|
56
|
-
and optional test suites.
|
|
52
|
+
Canonical command for local and CI preflight checks. It composes the release validation helpers with project-specific checks for version consistency, YAML configuration/data files, active configuration contracts, config-file classification, navigation data shape, Jekyll build/doctor, compiled assets, and optional test suites.
|
|
57
53
|
|
|
58
54
|
**Usage:**
|
|
59
55
|
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# scripts/lib/install/deploy/
|
|
2
2
|
|
|
3
|
-
Pluggable deploy-target modules consumed by `scripts/bin/install deploy`
|
|
4
|
-
(Phase 4 of the installer refactor). Each module configures one target;
|
|
5
|
-
the registry coordinates discovery, dispatch, and verification.
|
|
3
|
+
Pluggable deploy-target modules consumed by `scripts/bin/install deploy` (Phase 4 of the installer refactor). Each module configures one target; the registry coordinates discovery, dispatch, and verification.
|
|
6
4
|
|
|
7
5
|
## Files
|
|
8
6
|
|
|
@@ -26,16 +24,12 @@ Every module must define:
|
|
|
26
24
|
| `deploy_<slug>_verify <dir>` | Confirm expected files exist + look correct. |
|
|
27
25
|
| `deploy_<slug>_doc_url` | Print the canonical upstream documentation URL. |
|
|
28
26
|
|
|
29
|
-
Modules use the lightweight `deploy_render` placeholder set
|
|
30
|
-
(`{{RUBY_VERSION}}`, `{{DEFAULT_BRANCH}}`, `{{GITHUB_USER}}`,
|
|
31
|
-
`{{SITE_NAME}}`) so they can run without the full install.sh global
|
|
32
|
-
environment.
|
|
27
|
+
Modules use the lightweight `deploy_render` placeholder set (`{{RUBY_VERSION}}`, `{{DEFAULT_BRANCH}}`, `{{GITHUB_USER}}`, `{{SITE_NAME}}`) so they can run without the full install.sh global environment.
|
|
33
28
|
|
|
34
29
|
## Adding a target
|
|
35
30
|
|
|
36
31
|
1. Add `templates/deploy/<slug>/` with the assets (workflow YAML,
|
|
37
|
-
|
|
38
|
-
variable substitution.
|
|
32
|
+
Dockerfile, README, etc.). Use `*.template` for files that need variable substitution.
|
|
39
33
|
2. Create `scripts/lib/install/deploy/<slug>.sh` exporting the four
|
|
40
34
|
hooks above.
|
|
41
35
|
3. Add `<slug>` to `DEPLOY_TARGETS_LIST` in `registry.sh` (alphabetical).
|