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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -3
- data/README.md +98 -7
- data/_data/content_statistics.yml +253 -251
- data/_includes/components/nav-export.html +61 -0
- data/_includes/components/nav-overview.html +54 -0
- data/scripts/bin/install +52 -705
- data/scripts/github-setup.sh +0 -0
- data/scripts/install/README.md +162 -0
- data/scripts/install/ai/client.sh +164 -0
- data/scripts/install/ai/diagnose.sh +81 -0
- data/scripts/install/ai/prompts/diagnose.system.md +42 -0
- data/scripts/install/ai/prompts/spec.schema.json +129 -0
- data/scripts/install/ai/prompts/suggest.system.md +43 -0
- data/scripts/install/ai/prompts/wizard.system.md +142 -0
- data/scripts/install/ai/suggest.sh +57 -0
- data/scripts/install/ai/wizard.sh +150 -0
- data/scripts/install/apply.sh +156 -0
- data/scripts/install/cli.sh +561 -0
- data/scripts/install/diff.sh +128 -0
- data/scripts/install/doctor.sh +168 -0
- data/scripts/install/fs.sh +138 -0
- data/scripts/install/log.sh +119 -0
- data/scripts/install/plan.sh +299 -0
- data/scripts/install/platform.sh +122 -0
- data/scripts/install/prompt.sh +124 -0
- data/scripts/install/repair.sh +45 -0
- data/scripts/install/scrape.sh +535 -0
- data/scripts/install/scrape_html.py +764 -0
- data/scripts/install/spec.sh +486 -0
- data/scripts/install/tasks/_registry.sh +65 -0
- data/scripts/install/tasks/agents.sh +60 -0
- data/scripts/install/tasks/config.sh +37 -0
- data/scripts/install/tasks/data.sh +18 -0
- data/scripts/install/tasks/deploy_azure-swa.sh +17 -0
- data/scripts/install/tasks/deploy_docker-prod.sh +21 -0
- data/scripts/install/tasks/deploy_github-pages.sh +18 -0
- data/scripts/install/tasks/devcontainer.sh +26 -0
- data/scripts/install/tasks/docker.sh +29 -0
- data/scripts/install/tasks/gemfile.sh +42 -0
- data/scripts/install/tasks/gitignore.sh +26 -0
- data/scripts/install/tasks/marker.sh +46 -0
- data/scripts/install/tasks/nav.sh +18 -0
- data/scripts/install/tasks/pages.sh +61 -0
- data/scripts/install/tasks/readme.sh +27 -0
- data/scripts/install/tasks/scrape.sh +348 -0
- data/scripts/install/template.sh +138 -0
- data/scripts/install/tui.sh +110 -0
- data/scripts/install/upgrade.sh +49 -0
- data/scripts/lib/install/template.sh +1 -0
- metadata +49 -6
data/scripts/github-setup.sh
CHANGED
|
File without changes
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# scripts/install/ — Modular Installer v2
|
|
2
|
+
|
|
3
|
+
Spec-driven, AI-integrated Jekyll theme installer for zer0-mistakes.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
scripts/install/
|
|
9
|
+
├── cli.sh Subcommand dispatcher — main entry point
|
|
10
|
+
├── plan.sh Spec builder (flags + profile + env + platform)
|
|
11
|
+
├── apply.sh Executor — reads spec, runs tasks in order
|
|
12
|
+
├── diff.sh Spec-vs-disk diff renderer (preview before apply)
|
|
13
|
+
├── spec.sh JSON spec I/O (jq optional; awk fallback)
|
|
14
|
+
├── log.sh Color-coded logging (human + JSON-line modes)
|
|
15
|
+
├── platform.sh OS / Ruby / Docker / git / gh detection
|
|
16
|
+
├── fs.sh THE ONLY filesystem writer (dry-run, backup, force)
|
|
17
|
+
├── template.sh {{VAR}} template renderer (reads from templates/)
|
|
18
|
+
├── prompt.sh TTY-aware interactive prompts
|
|
19
|
+
├── doctor.sh Pre-install health checks
|
|
20
|
+
├── tui.sh Non-AI interactive wizard → spec
|
|
21
|
+
├── upgrade.sh Re-apply spec to existing install
|
|
22
|
+
├── repair.sh Fix drift: re-apply only changed tasks
|
|
23
|
+
├── tasks/
|
|
24
|
+
│ ├── _registry.sh Task metadata + dependency graph
|
|
25
|
+
│ ├── config.sh _config.yml + _config_dev.yml
|
|
26
|
+
│ ├── gemfile.sh Gemfile (variant per profile/platform)
|
|
27
|
+
│ ├── docker.sh docker-compose.yml + docker/Dockerfile
|
|
28
|
+
│ ├── theme.sh Copy _layouts _includes _sass assets
|
|
29
|
+
│ ├── pages.sh Starter pages from templates/pages/
|
|
30
|
+
│ ├── nav.sh _data/navigation/main.yml
|
|
31
|
+
│ ├── data.sh _data/authors.yml + seed data
|
|
32
|
+
│ ├── devcontainer.sh .devcontainer/devcontainer.json
|
|
33
|
+
│ ├── agents.sh AI agent files (AGENTS.md, copilot, claude, cursor, aider)
|
|
34
|
+
│ ├── gitignore.sh .gitignore
|
|
35
|
+
│ ├── readme.sh INSTALLATION.md + README seed
|
|
36
|
+
│ └── marker.sh .zer0-installed + spec persistence
|
|
37
|
+
└── ai/
|
|
38
|
+
├── client.sh HTTP wrapper (OpenAI-compatible, 30s timeout)
|
|
39
|
+
├── wizard.sh LLM → spec (AI-driven setup)
|
|
40
|
+
├── diagnose.sh Jekyll build errors → fix suggestions
|
|
41
|
+
├── suggest.sh Goal text → profile + deploy recommendation
|
|
42
|
+
└── prompts/
|
|
43
|
+
├── spec.schema.json JSON Schema (AI contract)
|
|
44
|
+
├── wizard.system.md AI wizard system prompt
|
|
45
|
+
├── diagnose.system.md AI diagnose system prompt
|
|
46
|
+
└── suggest.system.md AI suggest system prompt
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Core concepts
|
|
50
|
+
|
|
51
|
+
### The Spec
|
|
52
|
+
|
|
53
|
+
A single JSON document (`.zer0/install.spec.json`) is the universal contract
|
|
54
|
+
between all front-ends and the executor:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
CLI flags → plan.sh → spec.json → apply.sh → tasks → files on disk
|
|
58
|
+
AI wizard → spec.json → apply.sh → tasks → files on disk
|
|
59
|
+
TUI wizard → spec.json → apply.sh → tasks → files on disk
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The spec schema is defined in `ai/prompts/spec.schema.json`. The AI is
|
|
63
|
+
constrained to emit only valid spec JSON — never raw file content.
|
|
64
|
+
|
|
65
|
+
### Write contract
|
|
66
|
+
|
|
67
|
+
**ALL filesystem writes go through `fs.sh`**. No raw `>`, `cp`, or `echo >`
|
|
68
|
+
outside of `fs.sh` functions. This enforces:
|
|
69
|
+
- `--dry-run`: zero mutations
|
|
70
|
+
- `--backup`: auto-backup before overwrite
|
|
71
|
+
- `--force`: overwrite without prompting
|
|
72
|
+
|
|
73
|
+
### Template contract
|
|
74
|
+
|
|
75
|
+
**ALL generated file content comes from `templates/`**. No heredocs in shell
|
|
76
|
+
code. Templates use `{{VARIABLE}}` substitution via `template.sh::tmpl_apply`.
|
|
77
|
+
|
|
78
|
+
### Bash 3.2 compatibility
|
|
79
|
+
|
|
80
|
+
This installer runs on macOS's `/bin/bash` (3.2). Restrictions:
|
|
81
|
+
- No `declare -A` (associative arrays)
|
|
82
|
+
- No `=~` capture groups
|
|
83
|
+
- No `mapfile`/`readarray`
|
|
84
|
+
- Source guards (`[[ -n "${_HAS_FOO:-}" ]] && return 0`) on every module
|
|
85
|
+
- Modules do not `set -euo pipefail` or call `exit` (caller does)
|
|
86
|
+
|
|
87
|
+
## Usage
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Local development
|
|
91
|
+
./scripts/bin/install help
|
|
92
|
+
./scripts/bin/install init . --profile blog --site-title "My Blog"
|
|
93
|
+
./scripts/bin/install wizard . --ai
|
|
94
|
+
./scripts/bin/install doctor .
|
|
95
|
+
./scripts/bin/install diff .
|
|
96
|
+
./scripts/bin/install plan . --profile docs
|
|
97
|
+
|
|
98
|
+
# Remote (curl|bash)
|
|
99
|
+
curl -fsSL https://raw.githubusercontent.com/bamr87/zer0-mistakes/main/scripts/bin/install | bash -s -- init . --profile github-pages
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Adding a new task
|
|
103
|
+
|
|
104
|
+
1. Create `tasks/mytask.sh` with:
|
|
105
|
+
```bash
|
|
106
|
+
[[ -n "${_HAS_TASK_MYTASK:-}" ]] && return 0
|
|
107
|
+
_HAS_TASK_MYTASK=1
|
|
108
|
+
task_mytask_run() {
|
|
109
|
+
local target="$1"
|
|
110
|
+
tmpl_apply "config/mytask.template" "${target}/output-file"
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
2. Add to `tasks/_registry.sh` (`_TASK_ALL`, `_task_desc_mytask`, `_task_deps_mytask`)
|
|
114
|
+
3. Add template to `templates/config/` or `templates/pages/`
|
|
115
|
+
4. List in the appropriate profile YAML under `templates/profiles/`
|
|
116
|
+
|
|
117
|
+
## AI integration
|
|
118
|
+
|
|
119
|
+
The AI path is a first-class citizen but never mandatory:
|
|
120
|
+
|
|
121
|
+
- `ai_wizard_run` → interactive LLM spec generation
|
|
122
|
+
- `ai_diagnose_run` → post-build error analysis
|
|
123
|
+
- `ai_suggest_run` → profile + deploy recommendation
|
|
124
|
+
|
|
125
|
+
All three are guarded by `ZER0_NO_AI=1` kill-switch and degrade gracefully
|
|
126
|
+
to defaults or rule-based logic when AI is unavailable.
|
|
127
|
+
|
|
128
|
+
To enable: set `OPENAI_API_KEY` (or `OPENAI_BASE_URL` for Azure/Ollama).
|
|
129
|
+
|
|
130
|
+
## Deploy plugins
|
|
131
|
+
|
|
132
|
+
Deploy targets listed in the spec (`SPEC_DEPLOY`) are dispatched as
|
|
133
|
+
`tasks/deploy_<target>.sh` modules that render reusable templates from
|
|
134
|
+
`templates/deploy/<target>/`. Built-in plugins:
|
|
135
|
+
|
|
136
|
+
| Target | Writes |
|
|
137
|
+
|-------------------|---------------------------------------------------------------------|
|
|
138
|
+
| `github-pages` | `.github/workflows/jekyll-gh-pages.yml` |
|
|
139
|
+
| `azure-swa` | `.github/workflows/azure-static-web-apps.yml`, `staticwebapp.config.json` |
|
|
140
|
+
| `docker-prod` | `Dockerfile.prod`, `docker-compose.prod.yml`, `nginx.conf`, `.dockerignore` |
|
|
141
|
+
|
|
142
|
+
Add a new plugin by dropping `tasks/deploy_<target>.sh` with a
|
|
143
|
+
`task_deploy_<target>_run` function plus a `templates/deploy/<target>/`
|
|
144
|
+
template directory. The dispatcher is generic — no registry changes needed.
|
|
145
|
+
|
|
146
|
+
## Testing
|
|
147
|
+
|
|
148
|
+
A regression harness lives at [`test/test_installer.sh`](../../test/test_installer.sh)
|
|
149
|
+
and is wired into the main runner as the `installer` suite:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# Standalone (auto-enables AI tier when OPENAI_API_KEY is set)
|
|
153
|
+
./test/test_installer.sh
|
|
154
|
+
./test/test_installer.sh --ai # force AI tier on
|
|
155
|
+
./test/test_installer.sh --no-ai # skip AI tier
|
|
156
|
+
|
|
157
|
+
# Via the unified runner (included in --suites all and --suites full)
|
|
158
|
+
./test/test_runner.sh --suites installer
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The harness covers: module syntax, all 6 profile inits, all 3 deploy plugins,
|
|
162
|
+
all 5 agent flavours, and (when keyed) the full AI wizard → apply pipeline.
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# scripts/install/ai/client.sh — HTTP client for AI providers
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Thin curl wrapper that:
|
|
6
|
+
# - Targets OpenAI-compatible APIs (OpenAI, Azure OpenAI, Ollama)
|
|
7
|
+
# - Enforces 30s timeout
|
|
8
|
+
# - Redacts Authorization header from logs
|
|
9
|
+
# - Returns raw JSON response body to stdout
|
|
10
|
+
#
|
|
11
|
+
# Provides:
|
|
12
|
+
# ai_client_chat SYSTEM_PROMPT USER_PROMPT [JSON_SCHEMA]
|
|
13
|
+
# → print raw API JSON response to stdout
|
|
14
|
+
# → return 0 on success, 1 on error
|
|
15
|
+
#
|
|
16
|
+
# ai_client_extract_text RESPONSE_JSON
|
|
17
|
+
# → print the assistant's text content from a chat response
|
|
18
|
+
#
|
|
19
|
+
# ai_client_available
|
|
20
|
+
# → return 0 if OPENAI_API_KEY or OPENAI_BASE_URL is set, 1 otherwise
|
|
21
|
+
#
|
|
22
|
+
# Environment:
|
|
23
|
+
# OPENAI_API_KEY Required for openai.com
|
|
24
|
+
# OPENAI_BASE_URL Override base URL (Azure OpenAI / Ollama compatible)
|
|
25
|
+
# Default: https://api.openai.com/v1
|
|
26
|
+
# OPENAI_MODEL Model name (default: gpt-4o-mini)
|
|
27
|
+
# ZER0_NO_AI Set to 1 to disable all AI calls (kill-switch)
|
|
28
|
+
#
|
|
29
|
+
# Bash 3.2 compatible. No set -euo pipefail here.
|
|
30
|
+
# =============================================================================
|
|
31
|
+
[[ -n "${_HAS_AI_CLIENT:-}" ]] && return 0
|
|
32
|
+
_HAS_AI_CLIENT=1
|
|
33
|
+
|
|
34
|
+
_AI_BASE_URL="${OPENAI_BASE_URL:-https://api.openai.com/v1}"
|
|
35
|
+
_AI_MODEL="${OPENAI_MODEL:-gpt-4o-mini}"
|
|
36
|
+
_AI_TIMEOUT=30
|
|
37
|
+
|
|
38
|
+
# ---------------------------------------------------------------------------
|
|
39
|
+
# ai_client_available
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
ai_client_available() {
|
|
42
|
+
[[ "${ZER0_NO_AI:-0}" == "1" ]] && return 1
|
|
43
|
+
[[ -n "${OPENAI_API_KEY:-}" ]] && return 0
|
|
44
|
+
[[ -n "${OPENAI_BASE_URL:-}" ]] && return 0
|
|
45
|
+
return 1
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
# ai_client_chat SYSTEM_PROMPT USER_PROMPT [JSON_SCHEMA]
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
ai_client_chat() {
|
|
52
|
+
local system_prompt="$1"
|
|
53
|
+
local user_prompt="$2"
|
|
54
|
+
local json_schema="${3:-}"
|
|
55
|
+
|
|
56
|
+
if ! ai_client_available; then
|
|
57
|
+
log_error "ai_client_chat: AI not available (set OPENAI_API_KEY or OPENAI_BASE_URL)"
|
|
58
|
+
return 1
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
local api_key="${OPENAI_API_KEY:-}"
|
|
62
|
+
local endpoint="${_AI_BASE_URL}/chat/completions"
|
|
63
|
+
|
|
64
|
+
# Build request body — escape for JSON
|
|
65
|
+
local sys_escaped user_escaped
|
|
66
|
+
sys_escaped=$(printf '%s' "$system_prompt" | python3 -c "import json,sys; print(json.dumps(sys.stdin.read()))" 2>/dev/null \
|
|
67
|
+
|| printf '%s' "$system_prompt" | sed 's/\\/\\\\/g; s/"/\\"/g; s/$/\\n/g' | tr -d '\n' | sed 's/\\n$//')
|
|
68
|
+
user_escaped=$(printf '%s' "$user_prompt" | python3 -c "import json,sys; print(json.dumps(sys.stdin.read()))" 2>/dev/null \
|
|
69
|
+
|| printf '%s' "$user_prompt" | sed 's/\\/\\\\/g; s/"/\\"/g')
|
|
70
|
+
|
|
71
|
+
local body
|
|
72
|
+
if [[ -n "$json_schema" ]]; then
|
|
73
|
+
# Structured output (function calling)
|
|
74
|
+
body=$(cat <<JSON
|
|
75
|
+
{
|
|
76
|
+
"model": "${_AI_MODEL}",
|
|
77
|
+
"response_format": {
|
|
78
|
+
"type": "json_schema",
|
|
79
|
+
"json_schema": ${json_schema}
|
|
80
|
+
},
|
|
81
|
+
"messages": [
|
|
82
|
+
{"role": "system", "content": ${sys_escaped}},
|
|
83
|
+
{"role": "user", "content": ${user_escaped}}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
JSON
|
|
87
|
+
)
|
|
88
|
+
else
|
|
89
|
+
body=$(cat <<JSON
|
|
90
|
+
{
|
|
91
|
+
"model": "${_AI_MODEL}",
|
|
92
|
+
"messages": [
|
|
93
|
+
{"role": "system", "content": ${sys_escaped}},
|
|
94
|
+
{"role": "user", "content": ${user_escaped}}
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
JSON
|
|
98
|
+
)
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
log_debug "ai_client_chat: POST ${endpoint} model=${_AI_MODEL}"
|
|
102
|
+
|
|
103
|
+
local resp
|
|
104
|
+
resp=$(curl -fsSL \
|
|
105
|
+
--max-time "$_AI_TIMEOUT" \
|
|
106
|
+
-H "Content-Type: application/json" \
|
|
107
|
+
${api_key:+-H "Authorization: Bearer ${api_key}"} \
|
|
108
|
+
-d "$body" \
|
|
109
|
+
"$endpoint" 2>&1)
|
|
110
|
+
|
|
111
|
+
local ret=$?
|
|
112
|
+
if [[ $ret -ne 0 ]]; then
|
|
113
|
+
log_error "ai_client_chat: HTTP request failed (exit $ret)"
|
|
114
|
+
return 1
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
printf '%s\n' "$resp"
|
|
118
|
+
return 0
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# ---------------------------------------------------------------------------
|
|
122
|
+
# ai_client_extract_text RESPONSE_JSON
|
|
123
|
+
# Extracts assistant text and strips markdown code fences if present.
|
|
124
|
+
# ---------------------------------------------------------------------------
|
|
125
|
+
ai_client_extract_text() {
|
|
126
|
+
local resp="$1"
|
|
127
|
+
local text
|
|
128
|
+
if command -v jq >/dev/null 2>&1; then
|
|
129
|
+
text=$(printf '%s' "$resp" | jq -r '.choices[0].message.content // ""')
|
|
130
|
+
else
|
|
131
|
+
# Minimal awk extraction
|
|
132
|
+
text=$(printf '%s' "$resp" | awk '
|
|
133
|
+
/"content"[[:space:]]*:/ {
|
|
134
|
+
line = $0
|
|
135
|
+
sub(/.*"content"[[:space:]]*:[[:space:]]*"/, "", line)
|
|
136
|
+
sub(/"[^"]*$/, "", line)
|
|
137
|
+
gsub(/\\n/, "\n", line)
|
|
138
|
+
print line
|
|
139
|
+
exit
|
|
140
|
+
}
|
|
141
|
+
')
|
|
142
|
+
fi
|
|
143
|
+
# Strip markdown code fences (```json, ```, etc.) — remove any line starting with ```
|
|
144
|
+
printf '%s\n' "$text" | sed '/^[[:space:]]*```/d'
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# ---------------------------------------------------------------------------
|
|
148
|
+
# _ai_json_object_body SYSTEM_PROMPT USER_PROMPT
|
|
149
|
+
# Build a request body that forces json_object output (no schema needed)
|
|
150
|
+
# ---------------------------------------------------------------------------
|
|
151
|
+
_ai_json_object_body() {
|
|
152
|
+
local sys_escaped="$1"
|
|
153
|
+
local user_escaped="$2"
|
|
154
|
+
cat <<JSON
|
|
155
|
+
{
|
|
156
|
+
"model": "${_AI_MODEL}",
|
|
157
|
+
"response_format": {"type": "json_object"},
|
|
158
|
+
"messages": [
|
|
159
|
+
{"role": "system", "content": ${sys_escaped}},
|
|
160
|
+
{"role": "user", "content": ${user_escaped}}
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
JSON
|
|
164
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# scripts/install/ai/diagnose.sh — AI-assisted site diagnosis
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Sends sanitized Jekyll build output + config to LLM for fix suggestions.
|
|
6
|
+
#
|
|
7
|
+
# Provides:
|
|
8
|
+
# ai_diagnose_run TARGET_DIR [--log FILE]
|
|
9
|
+
#
|
|
10
|
+
# Bash 3.2 compatible. No set -euo pipefail here.
|
|
11
|
+
# =============================================================================
|
|
12
|
+
[[ -n "${_HAS_AI_DIAGNOSE:-}" ]] && return 0
|
|
13
|
+
_HAS_AI_DIAGNOSE=1
|
|
14
|
+
|
|
15
|
+
_AI_DIAGNOSE_DIR="${_AI_DIAGNOSE_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" 2>/dev/null && pwd)}"
|
|
16
|
+
|
|
17
|
+
ai_diagnose_run() {
|
|
18
|
+
local target="${1:-$(pwd)}"
|
|
19
|
+
local log_file=""
|
|
20
|
+
|
|
21
|
+
# Parse optional --log
|
|
22
|
+
shift || true
|
|
23
|
+
while [[ $# -gt 0 ]]; do
|
|
24
|
+
case "$1" in
|
|
25
|
+
--log) shift; log_file="${1:-}" ;;
|
|
26
|
+
esac
|
|
27
|
+
shift
|
|
28
|
+
done
|
|
29
|
+
|
|
30
|
+
if [[ "$(type -t ai_client_available)" != "function" ]]; then
|
|
31
|
+
source "${_AI_DIAGNOSE_DIR}/client.sh" || return 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
if ! ai_client_available; then
|
|
35
|
+
log_error "ai_diagnose_run: AI not available"
|
|
36
|
+
return 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# Collect context
|
|
40
|
+
local build_log=""
|
|
41
|
+
if [[ -n "$log_file" && -f "$log_file" ]]; then
|
|
42
|
+
build_log=$(tail -100 "$log_file")
|
|
43
|
+
else
|
|
44
|
+
# Run jekyll doctor for quick diagnostics
|
|
45
|
+
if command -v bundle >/dev/null 2>&1 && [[ -f "${target}/Gemfile" ]]; then
|
|
46
|
+
build_log=$(cd "$target" && bundle exec jekyll doctor 2>&1 | tail -50) || true
|
|
47
|
+
fi
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
local config_excerpt=""
|
|
51
|
+
[[ -f "${target}/_config.yml" ]] && \
|
|
52
|
+
config_excerpt=$(head -30 "${target}/_config.yml")
|
|
53
|
+
|
|
54
|
+
local sys_prompt_file="${_AI_DIAGNOSE_DIR}/prompts/diagnose.system.md"
|
|
55
|
+
local sys_prompt
|
|
56
|
+
if [[ -f "$sys_prompt_file" ]]; then
|
|
57
|
+
sys_prompt=$(cat "$sys_prompt_file")
|
|
58
|
+
else
|
|
59
|
+
sys_prompt="You are a Jekyll expert. Analyze the build output and config provided and suggest specific fixes. Be concise. Return a JSON object with keys: summary (string), fixes (array of {issue, fix, severity})."
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
local user_prompt
|
|
63
|
+
user_prompt=$(cat <<PROMPT
|
|
64
|
+
Target: $target
|
|
65
|
+
|
|
66
|
+
_config.yml (first 30 lines):
|
|
67
|
+
${config_excerpt:-[not found]}
|
|
68
|
+
|
|
69
|
+
Build output / doctor:
|
|
70
|
+
${build_log:-[no output captured]}
|
|
71
|
+
PROMPT
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
log_info "Analyzing with AI..."
|
|
75
|
+
local resp
|
|
76
|
+
resp=$(ai_client_chat "$sys_prompt" "$user_prompt" '{"name":"diagnose","schema":{"type":"object","properties":{"summary":{"type":"string"},"fixes":{"type":"array","items":{"type":"object","properties":{"issue":{"type":"string"},"cause":{"type":"string"},"fix":{"type":"string"},"severity":{"type":"string"}},"required":["issue","fix","severity"]}}},"required":["summary","fixes"]}}') || { log_error "Diagnosis API call failed"; return 1; }
|
|
77
|
+
|
|
78
|
+
local result
|
|
79
|
+
result=$(ai_client_extract_text "$resp")
|
|
80
|
+
printf '\n%s\n\n' "$result"
|
|
81
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# zer0-mistakes Site Diagnosis
|
|
2
|
+
|
|
3
|
+
You are a Jekyll expert analyzing a zer0-mistakes site for problems.
|
|
4
|
+
|
|
5
|
+
## Your task
|
|
6
|
+
|
|
7
|
+
Given the build output, Jekyll doctor output, or _config.yml content:
|
|
8
|
+
|
|
9
|
+
1. Identify the specific error or warning.
|
|
10
|
+
2. Explain the root cause in one sentence.
|
|
11
|
+
3. Provide a concrete fix command or file change.
|
|
12
|
+
4. Rate severity: critical | high | medium | low.
|
|
13
|
+
|
|
14
|
+
## Output format
|
|
15
|
+
|
|
16
|
+
Return a JSON object with this structure:
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"summary": "One-sentence overall assessment",
|
|
20
|
+
"fixes": [
|
|
21
|
+
{
|
|
22
|
+
"issue": "Error description",
|
|
23
|
+
"cause": "Root cause",
|
|
24
|
+
"fix": "Exact command or change needed",
|
|
25
|
+
"severity": "critical|high|medium|low"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
No prose before or after the JSON.
|
|
32
|
+
|
|
33
|
+
## Common Jekyll issues to recognize
|
|
34
|
+
|
|
35
|
+
- Missing `bundler` or wrong Ruby version
|
|
36
|
+
- `github-pages` gem conflicts
|
|
37
|
+
- Missing `_config.yml` fields (url, baseurl)
|
|
38
|
+
- Liquid syntax errors
|
|
39
|
+
- Asset path issues with baseurl
|
|
40
|
+
- Missing `jekyll-feed` / `jekyll-seo-tag` plugins
|
|
41
|
+
- Docker container networking issues
|
|
42
|
+
- Permission errors on _site/
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://github.com/bamr87/zer0-mistakes/installer/spec/v1",
|
|
4
|
+
"title": "zer0-mistakes Install Spec",
|
|
5
|
+
"description": "Declarative specification consumed by scripts/install/apply.sh. Produced by plan.sh, tui.sh, or ai/wizard.sh. This schema is the AI contract: the wizard must emit only valid spec JSON.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schema_version", "target_dir", "profile", "tasks", "options"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema_version": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"const": "1",
|
|
13
|
+
"description": "Schema version. Increment with breaking changes."
|
|
14
|
+
},
|
|
15
|
+
"target_dir": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"minLength": 1,
|
|
18
|
+
"description": "Absolute path to the directory where the site will be installed."
|
|
19
|
+
},
|
|
20
|
+
"profile": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"enum": ["default", "minimal", "blog", "docs", "portfolio", "github-pages", "fork"],
|
|
23
|
+
"description": "Named preset that provides default values for all other fields."
|
|
24
|
+
},
|
|
25
|
+
"site": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"additionalProperties": false,
|
|
28
|
+
"properties": {
|
|
29
|
+
"title": { "type": "string", "default": "My Jekyll Site" },
|
|
30
|
+
"description": { "type": "string", "default": "A Jekyll site built with zer0-mistakes" },
|
|
31
|
+
"url": { "type": "string", "default": "" },
|
|
32
|
+
"author": { "type": "string", "default": "Site Author" },
|
|
33
|
+
"email": { "type": "string", "format": "email", "default": "" },
|
|
34
|
+
"timezone": { "type": "string", "default": "UTC" },
|
|
35
|
+
"locale": { "type": "string", "default": "en" }
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"github": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"additionalProperties": false,
|
|
41
|
+
"properties": {
|
|
42
|
+
"user": { "type": "string", "default": "" },
|
|
43
|
+
"repo": { "type": "string", "default": "" },
|
|
44
|
+
"pages_branch": { "type": "string", "default": "gh-pages" },
|
|
45
|
+
"enable_pages": { "type": "boolean", "default": false }
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"theme": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"additionalProperties": false,
|
|
51
|
+
"required": ["source"],
|
|
52
|
+
"properties": {
|
|
53
|
+
"source": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"enum": ["gem", "remote", "vendored"],
|
|
56
|
+
"description": "gem = install jekyll-theme-zer0 from RubyGems; remote = remote_theme on GitHub Pages; vendored = copy theme files locally."
|
|
57
|
+
},
|
|
58
|
+
"version": { "type": "string", "default": "" }
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"tasks": {
|
|
62
|
+
"type": "array",
|
|
63
|
+
"minItems": 1,
|
|
64
|
+
"items": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"enum": [
|
|
67
|
+
"config",
|
|
68
|
+
"gemfile",
|
|
69
|
+
"docker",
|
|
70
|
+
"theme",
|
|
71
|
+
"pages",
|
|
72
|
+
"nav",
|
|
73
|
+
"data",
|
|
74
|
+
"devcontainer",
|
|
75
|
+
"agents",
|
|
76
|
+
"gitignore",
|
|
77
|
+
"readme",
|
|
78
|
+
"marker"
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
"description": "Ordered list of tasks apply.sh will execute. 'marker' should always be last."
|
|
82
|
+
},
|
|
83
|
+
"deploy": {
|
|
84
|
+
"type": "array",
|
|
85
|
+
"items": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"enum": ["github-pages", "azure-swa", "docker-prod", "vercel", "netlify", "cloudflare-pages"]
|
|
88
|
+
},
|
|
89
|
+
"default": [],
|
|
90
|
+
"description": "Deploy targets to pre-configure alongside the install."
|
|
91
|
+
},
|
|
92
|
+
"agents": {
|
|
93
|
+
"type": "array",
|
|
94
|
+
"items": {
|
|
95
|
+
"type": "string",
|
|
96
|
+
"enum": ["claude", "cursor", "aider", "copilot", "generic"]
|
|
97
|
+
},
|
|
98
|
+
"default": [],
|
|
99
|
+
"description": "AI agent config files to generate (AGENTS.md, .cursor/, CLAUDE.md, .aider.conf.yml, etc.)."
|
|
100
|
+
},
|
|
101
|
+
"options": {
|
|
102
|
+
"type": "object",
|
|
103
|
+
"additionalProperties": false,
|
|
104
|
+
"required": ["dry_run", "force", "backup", "non_interactive", "output"],
|
|
105
|
+
"properties": {
|
|
106
|
+
"dry_run": { "type": "boolean", "default": false, "description": "Print what would happen without writing any files." },
|
|
107
|
+
"force": { "type": "boolean", "default": false, "description": "Overwrite existing files without prompting (still backs up)." },
|
|
108
|
+
"backup": { "type": "boolean", "default": true, "description": "Create timestamped backups before overwriting." },
|
|
109
|
+
"non_interactive": { "type": "boolean", "default": false, "description": "Never prompt for user input. Errors out on missing required values." },
|
|
110
|
+
"output": { "type": "string", "default": "human", "enum": ["human", "json"], "description": "Output mode. 'json' is machine-readable." },
|
|
111
|
+
"auto_accept": { "type": "boolean", "default": false, "description": "Skip all y/N confirmation prompts. Implies --non-interactive for confirmations." },
|
|
112
|
+
"skip_doctor": { "type": "boolean", "default": false, "description": "Skip the pre-install environment health check." },
|
|
113
|
+
"verbose": { "type": "boolean", "default": false, "description": "Print debug-level log lines." }
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"ai": {
|
|
117
|
+
"type": "object",
|
|
118
|
+
"additionalProperties": false,
|
|
119
|
+
"description": "Metadata about AI involvement. Written by ai/wizard.sh; informational only.",
|
|
120
|
+
"properties": {
|
|
121
|
+
"used": { "type": "boolean", "default": false },
|
|
122
|
+
"provider": { "type": "string", "default": "openai" },
|
|
123
|
+
"model": { "type": "string", "default": "" },
|
|
124
|
+
"tokens_estimated": { "type": "integer", "minimum": 0, "default": 0 },
|
|
125
|
+
"spec_hash": { "type": "string", "default": "", "description": "sha256 of the spec JSON (without the ai.spec_hash field itself)." }
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# zer0-mistakes Profile & Deploy Advisor
|
|
2
|
+
|
|
3
|
+
You are a Jekyll setup advisor for the zer0-mistakes theme.
|
|
4
|
+
|
|
5
|
+
## Your task
|
|
6
|
+
|
|
7
|
+
Given the user's goal and context, recommend:
|
|
8
|
+
1. The best installation profile
|
|
9
|
+
2. The best deploy target(s)
|
|
10
|
+
|
|
11
|
+
## Available profiles
|
|
12
|
+
|
|
13
|
+
| Profile | Best for |
|
|
14
|
+
|---------------|-----------------------------------------------|
|
|
15
|
+
| default | General-purpose Jekyll site |
|
|
16
|
+
| minimal | Bare bones, no extras |
|
|
17
|
+
| blog | Blog-focused with posts/categories/tags |
|
|
18
|
+
| docs | Technical documentation |
|
|
19
|
+
| portfolio | Personal portfolio / resume site |
|
|
20
|
+
| github-pages | Direct GitHub Pages deployment |
|
|
21
|
+
| fork | Forking the theme to customize it |
|
|
22
|
+
|
|
23
|
+
## Available deploy targets
|
|
24
|
+
|
|
25
|
+
| Target | Description |
|
|
26
|
+
|------------------|--------------------------------------------------|
|
|
27
|
+
| github-pages | GitHub Pages (Actions-driven) |
|
|
28
|
+
| azure-swa | Azure Static Web Apps |
|
|
29
|
+
| docker-prod | Self-hosted Docker + nginx |
|
|
30
|
+
| vercel | Vercel (static) |
|
|
31
|
+
| netlify | Netlify |
|
|
32
|
+
| cloudflare-pages | Cloudflare Pages |
|
|
33
|
+
|
|
34
|
+
## Output format
|
|
35
|
+
|
|
36
|
+
Return only this JSON object — no prose:
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"profile": "profile-name",
|
|
40
|
+
"deploy": ["target1"],
|
|
41
|
+
"rationale": "One sentence explaining the choice"
|
|
42
|
+
}
|
|
43
|
+
```
|