Última atividade 1 week ago

Cross-platform Zsh setup scripts and managed dotfiles with Oh My Zsh framework, Starship prompt, aliases, functions, and path configuration.

Revisão 50fa76235582ca9790d16b429621f0bb891e5540

README.md Bruto

Bash Script Installer for Zsh

The "Bash Script Installer" simplifies the setup of Zsh.

Zsh

Ubuntu

bash -c "$(curl -fsSL https://tinyurl.com/y6fhb594)"

WSL

bash -c "$(curl -fsSL https://tinyurl.com/ycyct6wp)"

MacOS

bash -c "$(curl -fsSL https://tinyurl.com/yptybtpa)"

Configuration

bash -c "$(curl -fsSL https://tinyurl.com/y8vf753j)"

Shell framework and prompt

The installers use Oh My Zsh for framework features and plugins, with the OMZ theme disabled:

ZSH_THEME=""
source "$ZSH/oh-my-zsh.sh"
eval "$(starship init zsh)"

Starship owns the prompt and loads after Oh My Zsh. The managed Starship config is installed at ~/.config/starship.toml. A Nerd Font is required for the Powerline symbols.

Plan-build workflow

plan_build launches Claude Code as the implementation orchestrator, with Codex and CodeRabbit as independent reviewers. The configuration installer also installs its workflow at ~/.claude/skills/plan-build/SKILL.md.

plan_build                                      # Standard plan and build
plan_build --prompt                             # Enhance the payload with Auggie first
plan_build --writing-plan                       # Superpowers implementation plan, Codex review, then build
plan_build --prompt --brainstorm                # Auggie, Superpowers design and plan, Codex review, then build
plan_build --yolo --writing-plan                # Pass --yolo through to Claude Code
plan_build --v2 --grill                         # Run the standalone v2 script (adds --grill and --distribute)
plan_build --v2 --distribute                    # v2 distributed mode: Codex implements BE, Kimi Code implements FE

--brainstorm and --writing-plan are mutually exclusive. They require the enabled superpowers@claude-plugins-official Claude Code plugin; plan_build reports the install or enable command and exits before Auggie when the requirement is not met.

--v2 downloads and runs the standalone plan_build v2 script on demand, forwarding every other argument to it. v2 adds the --grill planning mode (mattpocock-skills:grilling) and --distribute (backend items implemented by Codex, frontend items by Kimi Code, sequentially, after the FE/BE split is confirmed). --distribute additionally requires the kimi CLI. Without --v2, plan_build runs the built-in v1 function unchanged.

alias Bruto
1# =============================================================================
2# OS-SPECIFIC ALIASES
3# =============================================================================
4if [[ "$OSTYPE" == "linux-gnu"* ]] && command -v apt-get >/dev/null 2>&1; then
5 # Linux Only System update & cleanup
6 alias uu='sudo apt-get update && \
7 sudo apt-get upgrade -y && \
8 sudo apt-get full-upgrade -y && \
9 sudo apt-get autoremove -y && \
10 sudo apt-get autoclean -y && \
11 sudo apt-get clean'
12elif [[ "$OSTYPE" == "darwin"* ]]; then
13 # macOS Only
14 alias flushdns='sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder'
15fi
16
17# =============================================================================
18# UNIVERSAL GIT ALIASES (Fixed: Removed broken "$@" from aliases)
19# =============================================================================
20alias gph='git push'
21alias gco='git checkout'
22alias gbh='git branch'
23alias gmt='git commit'
24alias gpl='git pull'
25alias grb='git rebase'
26alias grt='git reset'
27alias gst='git status'
28alias grmrf='git checkout -- . && git clean -fd'
config.sh Bruto
1#!/bin/bash
2set -euo pipefail
3
4# Configuration
5GIST_RAW_BASE="https://opengist.resetrix.work/weehong/f0d940c3c1214bf5b7996195199fdc09/raw/HEAD"
6CONFIG_FILES=(
7 ".alias"
8 ".func"
9 ".pathrc"
10 ".sourcerc"
11 ".vimrc"
12 ".zshrc"
13 ".config/starship.toml"
14)
15PLAN_BUILD_SKILL_TARGET="$HOME/.claude/skills/plan-build/SKILL.md"
16
17echo "Starting configuration download..."
18download_failed=0
19
20for f in "${CONFIG_FILES[@]}"; do
21 # Remove the leading dot for the URL path
22 remote_name="${f#.}"
23 [[ "$f" == ".config/starship.toml" ]] && remote_name="starship.toml"
24 url="$GIST_RAW_BASE/$remote_name"
25 target="$HOME/$f"
26 tmp="${target}.tmp.$$"
27
28 echo "Downloading $f..."
29 mkdir -p "$(dirname "$target")"
30
31 # Use -f to fail silently on server errors, -s for silent, -L to follow redirects
32 if curl -fsSL "$url" -o "$tmp" && mv "$tmp" "$target"; then
33 echo "Successfully updated $target"
34 else
35 rm -f "$tmp"
36 echo "Error: Failed to download $f from $url" >&2
37 download_failed=1
38 fi
39done
40
41echo "Downloading Claude Code plan-build workflow..."
42mkdir -p "$(dirname "$PLAN_BUILD_SKILL_TARGET")"
43skill_tmp="${PLAN_BUILD_SKILL_TARGET}.tmp.$$"
44if curl -fsSL "$GIST_RAW_BASE/orchestrate-loop.md" -o "$skill_tmp" && mv "$skill_tmp" "$PLAN_BUILD_SKILL_TARGET"; then
45 echo "Successfully updated $PLAN_BUILD_SKILL_TARGET"
46else
47 rm -f "$skill_tmp"
48 echo "Error: Failed to download the plan-build workflow" >&2
49 download_failed=1
50fi
51
52if [ "$download_failed" -ne 0 ]; then
53 echo "Configuration download completed with errors." >&2
54 exit 1
55fi
56
57echo "Done! All configuration files have been replaced."
58
func Bruto
1# =============================================================================
2# CUSTOM FUNCTIONS
3# =============================================================================
4
5# -----------------------------------------------------------------------------
6# Function: funcs (List available .func functions)
7# -----------------------------------------------------------------------------
8funcs() {
9 local func_file="${1:-$HOME/.func}"
10
11 if [ ! -f "$func_file" ]; then
12 printf "Error: Function file '%s' not found.\n" "$func_file" >&2
13 return 1
14 fi
15
16 printf "Available functions in %s:\n" "$func_file"
17 awk '
18 /^# Function: / {
19 line = $0
20 sub(/^# Function: /, "", line)
21
22 name = line
23 sub(/ .*/, "", name)
24
25 desc = ""
26 if (line ~ / \(.+\)$/) {
27 desc = line
28 sub(/^[^ ]+ \(/, "", desc)
29 sub(/\)$/, "", desc)
30 }
31
32 if (desc != "") {
33 printf " %-22s %s\n", name, desc
34 } else {
35 printf " %s\n", name
36 }
37 }
38 ' "$func_file"
39}
40
41# -----------------------------------------------------------------------------
42# Function: clip (Cross-platform clipboard)
43# -----------------------------------------------------------------------------
44clip() {
45 local cmd
46 local args=()
47
48 if command -v pbcopy >/dev/null 2>&1; then
49 cmd="pbcopy" # macOS
50 elif grep -qi "microsoft" /proc/version 2>/dev/null && command -v clip.exe >/dev/null 2>&1; then
51 cmd="clip.exe" # WSL
52 elif [ "$XDG_SESSION_TYPE" = "wayland" ] && command -v wl-copy >/dev/null 2>&1; then
53 cmd="wl-copy" # Linux Wayland
54 elif command -v xclip >/dev/null 2>&1; then
55 cmd="xclip" # Linux X11 (Fallback 1)
56 args=("-selection" "clipboard")
57 elif command -v xsel >/dev/null 2>&1; then
58 cmd="xsel" # Linux X11 (Fallback 2)
59 args=("--clipboard" "--input")
60 else
61 printf "Error: No supported clipboard utility found.\n" >&2
62 return 1
63 fi
64
65 if [ $# -gt 0 ]; then
66 if [ -f "$1" ]; then
67 "$cmd" "${args[@]}" < "$1"
68 echo "Copied contents of '$1' to clipboard."
69 else
70 printf "Error: File '%s' not found.\n" "$1" >&2
71 return 1
72 fi
73 else
74 "$cmd" "${args[@]}"
75 fi
76}
77
78# -----------------------------------------------------------------------------
79# Function: open_file (Cross-platform file/directory opener)
80# -----------------------------------------------------------------------------
81open_file() {
82 local target="${1:-.}"
83
84 if [[ "$OSTYPE" == "darwin"* ]]; then
85 command open "$target"
86 elif grep -qi "microsoft" /proc/version 2>/dev/null; then
87 if command -v wslpath >/dev/null 2>&1 && command -v explorer.exe >/dev/null 2>&1; then
88 explorer.exe "$(wslpath -w "$target")"
89 else
90 printf "Error: 'wslpath' or 'explorer.exe' not found.\n" >&2
91 return 1
92 fi
93 elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
94 if command -v xdg-open >/dev/null 2>&1; then
95 xdg-open "$target"
96 else
97 printf "Error: 'xdg-open' not found.\n" >&2
98 return 1
99 fi
100 fi
101}
102alias open='open_file'
103
104# -----------------------------------------------------------------------------
105# Function: clear_history (Supports shell, Claude, Codex, and OpenCode)
106# -----------------------------------------------------------------------------
107clear_history() {
108 case "${1:-}" in
109 claude)
110 if [ -d "$HOME/.claude/projects" ]; then
111 # Use 'yes' to skip prompts and -f to ignore non-existent files
112 yes | rm -rf "$HOME/.claude/projects"/*
113 echo "Claude project history/cache cleared."
114 fi
115 ;;
116
117 codex)
118 local codex_home="${CODEX_HOME:-$HOME/.codex}"
119 local codex_session_file
120 local codex_session_id
121
122 mkdir -p "$codex_home"
123 : > "$codex_home/history.jsonl"
124
125 if command -v codex >/dev/null 2>&1; then
126 find "$codex_home/sessions" "$codex_home/archived_sessions" -type f -name '*.jsonl' -print 2>/dev/null | while IFS= read -r codex_session_file; do
127 codex_session_id="$(sed -n 's/.*"session_id":"\([^"]*\)".*/\1/p; q' "$codex_session_file")"
128 if [ -n "$codex_session_id" ]; then
129 codex delete --force "$codex_session_id" >/dev/null 2>&1 || true
130 fi
131 done
132 fi
133
134 rm -rf "$codex_home/sessions" "$codex_home/archived_sessions" "$codex_home/shell_snapshots"
135 mkdir -p "$codex_home/sessions" "$codex_home/archived_sessions" "$codex_home/shell_snapshots"
136
137 echo "Codex history cleared."
138 ;;
139
140 opencode)
141 local opencode_state_home="${XDG_STATE_HOME:-$HOME/.local/state}/opencode"
142 local opencode_data_home="${XDG_DATA_HOME:-$HOME/.local/share}/opencode"
143
144 mkdir -p "$opencode_state_home" "$opencode_data_home"
145 : > "$opencode_state_home/prompt-history.jsonl"
146 rm -f \
147 "$opencode_data_home/opencode.db" \
148 "$opencode_data_home/opencode.db-shm" \
149 "$opencode_data_home/opencode.db-wal"
150 rm -rf "$opencode_data_home/repos" "$opencode_data_home/log"
151 mkdir -p "$opencode_data_home/repos" "$opencode_data_home/log"
152
153 echo "OpenCode history cleared."
154 ;;
155
156 *)
157 # 1. Truncate the file
158 : > "$HISTFILE"
159
160 # 2. Clear RAM by briefly setting history size to 0
161 local old_histsize=$HISTSIZE
162 HISTSIZE=0
163 HISTSIZE=$old_histsize
164
165 echo "Shell history cleared."
166 ;;
167 esac
168}
169
170# -----------------------------------------------------------------------------
171# Function: require_cli (Check external CLI availability)
172# -----------------------------------------------------------------------------
173require_cli() {
174 local binary="$1"
175 local label="$2"
176 local resolved
177
178 if command -v whence >/dev/null 2>&1; then
179 if whence -p "$binary" >/dev/null 2>&1; then
180 return 0
181 fi
182 else
183 resolved="$(command -v "$binary" 2>/dev/null)" || resolved=""
184 if [ -n "$resolved" ] && [ -x "$resolved" ]; then
185 return 0
186 fi
187 fi
188
189 printf "Error: %s ('%s') is not installed or not in your PATH.\n" "$label" "$binary" >&2
190 return 1
191}
192
193# -----------------------------------------------------------------------------
194# Function: claude (Includes --yolo and clear shortcuts)
195# -----------------------------------------------------------------------------
196claude() {
197 # Shortcut for clearing cache
198 if [[ "$1" == "clear" ]]; then
199 clear_history claude
200 return 0
201 fi
202
203 # Check if binary exists
204 require_cli claude "Claude Code CLI" || return 1
205
206 # Handle --yolo mode
207 if [[ "$1" == "--yolo" ]]; then
208 shift
209 command claude --dangerously-skip-permissions "$@"
210 return $?
211 fi
212
213 command claude "$@"
214}
215
216# -----------------------------------------------------------------------------
217# Internal: _prompt_confirm_indexing (Confirm project indexing via the terminal)
218# -----------------------------------------------------------------------------
219_prompt_confirm_indexing() {
220 local project_root="$1"
221 local reply
222
223 if ! (: </dev/tty) 2>/dev/null; then
224 printf "Notice: No interactive terminal available; enhancing without project indexing.\n" >&2
225 return 1
226 fi
227
228 printf "Allow Auggie to index and use project context from '%s'? (y/N) " "$project_root" >/dev/tty
229 if ! IFS= read -r reply </dev/tty; then
230 printf "\nNotice: Unable to read confirmation; enhancing without project indexing.\n" >&2
231 return 1
232 fi
233
234 case "$reply" in
235 y|Y|yes|YES|Yes)
236 return 0
237 ;;
238 *)
239 return 1
240 ;;
241 esac
242}
243
244# -----------------------------------------------------------------------------
245# Internal: _enhance_prompt (Run Auggie and write only the enhanced prompt)
246# -----------------------------------------------------------------------------
247_enhance_prompt() {
248 local prompt="$1"
249 local output_file="$2"
250 local run_dir
251 local prompt_file
252 local workspace
253 local cache_dir
254 local auth_file
255 local log_file
256 local parsed_file
257 local project_root
258 local use_project_context
259 local script_pid
260 local waited
261 local timeout_seconds
262 local monitor_status
263
264 if [ -z "$prompt" ]; then
265 printf "Error: Prompt was empty.\n" >&2
266 return 1
267 fi
268
269 if [ ! -s "$HOME/.augment/session.json" ]; then
270 printf "Error: Auggie session file not found. Run 'auggie login' first.\n" >&2
271 return 1
272 fi
273
274 run_dir="$(mktemp -d "${TMPDIR:-/tmp}/auggie-enhance.XXXXXX")" || return 1
275 prompt_file="$run_dir/prompt.txt"
276 workspace="$run_dir/workspace"
277 cache_dir="$run_dir/cache"
278 auth_file="$HOME/.augment/session.json"
279 log_file="$run_dir/auggie.log"
280 parsed_file="$run_dir/enhanced.txt"
281 mkdir -p "$workspace" "$cache_dir"
282 printf "%s\n" "$prompt" > "$prompt_file"
283
284 project_root="$(git rev-parse --show-toplevel 2>/dev/null)" || project_root="$PWD"
285 use_project_context=0
286 timeout_seconds=90
287
288 if _prompt_confirm_indexing "$project_root"; then
289 use_project_context=1
290 timeout_seconds=300
291 workspace="$project_root"
292 cache_dir="$HOME/.augment"
293 printf "Indexing approved; enhancing with project context from '%s'.\n" "$project_root" >&2
294 else
295 printf "Enhancing without project indexing.\n" >&2
296 fi
297
298 (
299 if [ "$use_project_context" -eq 1 ]; then
300 AUGGIE_PROMPT_FILE="$prompt_file" \
301 AUGGIE_WORKSPACE="$workspace" \
302 AUGGIE_CACHE_DIR="$cache_dir" \
303 AUGGIE_AUTH_FILE="$auth_file" \
304 script -q -f -O "$log_file" -c 'auggie --print --enhance-prompt --workspace-root "$AUGGIE_WORKSPACE" --augment-cache-dir "$AUGGIE_CACHE_DIR" --augment-session-json "$AUGGIE_AUTH_FILE" --allow-indexing --wait-for-indexing --dont-save-session --instruction-file "$AUGGIE_PROMPT_FILE"' </dev/null >/dev/null 2>&1 &
305 else
306 AUGGIE_PROMPT_FILE="$prompt_file" \
307 AUGGIE_WORKSPACE="$workspace" \
308 AUGGIE_CACHE_DIR="$cache_dir" \
309 AUGGIE_AUTH_FILE="$auth_file" \
310 script -q -f -O "$log_file" -c 'auggie --print --enhance-prompt --no-discover-workspaces --workspace-root "$AUGGIE_WORKSPACE" --augment-cache-dir "$AUGGIE_CACHE_DIR" --augment-session-json "$AUGGIE_AUTH_FILE" --dont-save-session --instruction-file "$AUGGIE_PROMPT_FILE"' </dev/null >/dev/null 2>&1 &
311 fi
312
313 script_pid=$!
314 waited=0
315
316 while kill -0 "$script_pid" >/dev/null 2>&1; do
317 if grep -aq "🤖" "$log_file" 2>/dev/null || grep -aq "Tool call:" "$log_file" 2>/dev/null; then
318 kill "$script_pid" >/dev/null 2>&1 || true
319 wait "$script_pid" >/dev/null 2>&1 || true
320 exit 0
321 fi
322
323 if [ "$waited" -ge "$timeout_seconds" ]; then
324 kill "$script_pid" >/dev/null 2>&1 || true
325 wait "$script_pid" >/dev/null 2>&1 || true
326 exit 124
327 fi
328
329 sleep 1
330 waited=$((waited + 1))
331 done
332
333 wait "$script_pid" >/dev/null 2>&1 || true
334 exit 0
335 )
336 monitor_status=$?
337
338 if [ "$monitor_status" -eq 124 ]; then
339 printf "Error: Timed out waiting for Auggie to enhance the prompt.\n" >&2
340 rm -rf "$run_dir"
341 return 124
342 fi
343
344 perl -ne '
345 # Auggie redraws streamed terminal lines with carriage returns. Keep only
346 # the final rendered segment so stale text cannot corrupt the prompt.
347 s/\r$//;
348 s/.*\r//;
349 1 while s/[^\x08]\x08//g;
350 s/\x08//g;
351 s/\e\][^\a]*(?:\a|\e\\)//g;
352 s/\e\[[0-?]*[ -\/]*[@-~]//g;
353 next if /Script started on/ || /Script done on/;
354 if (/^(?:✨\s*)?Enhanced prompt:\s*(.*)$/) {
355 $capturing = 1;
356 $output .= "$1\n" if length $1;
357 next;
358 }
359 next unless $capturing;
360 exit if /^🤖/ || /Tool call:/ || /Session terminated/;
361 $output .= $_;
362 END {
363 $output =~ s/^\s*\n//;
364 $output =~ s/\s+\z//;
365 print "$output\n" if length $output;
366 }
367 ' "$log_file" > "$parsed_file"
368
369 if [ ! -s "$parsed_file" ]; then
370 printf "Error: Auggie did not return an enhanced prompt.\n" >&2
371 rm -rf "$run_dir"
372 return 1
373 fi
374
375 if ! command cp "$parsed_file" "$output_file"; then
376 printf "Error: Unable to save the enhanced prompt.\n" >&2
377 rm -rf "$run_dir"
378 return 1
379 fi
380 rm -rf "$run_dir"
381}
382
383# -----------------------------------------------------------------------------
384# Function: prompt (Enhance a prompt with Auggie and optional project context)
385# -----------------------------------------------------------------------------
386prompt() {
387 local prompt
388 local line
389 local output_file
390
391 require_cli auggie "Auggie CLI" || return 1
392 require_cli script "script utility" || return 1
393
394 if [ $# -gt 0 ]; then
395 prompt="$*"
396 elif [ ! -t 0 ]; then
397 prompt="$(cat)"
398 else
399 echo "Reading prompt... Type 'EOF' on a new line and press Enter when finished."
400 prompt=""
401
402 while IFS= read -r line; do
403 [ "$line" = "EOF" ] && break
404 if [ -z "$prompt" ]; then
405 prompt="$line"
406 else
407 prompt="${prompt}"$'\n'"${line}"
408 fi
409 done
410 fi
411
412 if [ -z "$prompt" ]; then
413 printf "Error: Prompt was empty.\n" >&2
414 return 1
415 fi
416
417 output_file="$(mktemp "${TMPDIR:-/tmp}/auggie-enhanced-prompt.XXXXXX")" || return 1
418 if ! _enhance_prompt "$prompt" "$output_file"; then
419 rm -f "$output_file"
420 return 1
421 fi
422
423 printf "Enhanced prompt:\n"
424 command cat "$output_file"
425 rm -f "$output_file"
426
427 command auggie account status
428}
429
430# -----------------------------------------------------------------------------
431# Function: plan_build (Claude Code multi-agent workflow orchestrator)
432# -----------------------------------------------------------------------------
433_plan_build_superpowers_state() {
434 awk '
435 BEGIN {
436 RS = "}"
437 state = "missing"
438 printed = 0
439 }
440 /"id"[[:space:]]*:[[:space:]]*"superpowers@claude-plugins-official"/ {
441 state = "installed"
442 if ($0 ~ /"enabled"[[:space:]]*:[[:space:]]*true/) {
443 state = "enabled"
444 } else if ($0 ~ /"enabled"[[:space:]]*:[[:space:]]*false/) {
445 state = "disabled"
446 }
447 print state
448 printed = 1
449 exit
450 }
451 END {
452 if (!printed) {
453 print state
454 }
455 }
456 '
457}
458
459_plan_build_superpowers_preflight() {
460 local plugin_json
461 local plugin_state
462
463 case "${CLAUDE_CODE_SAFE_MODE:-}" in
464 1|true|TRUE|yes|YES|on|ON)
465 echo "❌ Error: Claude Code safe mode disables Superpowers."
466 echo "Unset CLAUDE_CODE_SAFE_MODE before using --brainstorm or --writing-plan."
467 return 1
468 ;;
469 esac
470
471 if ! plugin_json="$(command claude plugin list --json 2>/dev/null)"; then
472 echo "❌ Error: Unable to inspect Claude Code plugins."
473 echo "Run 'claude plugin list' to diagnose the problem."
474 return 1
475 fi
476
477 plugin_state="$(printf '%s\n' "$plugin_json" | _plan_build_superpowers_state)"
478
479 case "$plugin_state" in
480 enabled)
481 return 0
482 ;;
483 disabled)
484 echo "❌ Error: Claude Code Superpowers is installed but disabled."
485 echo "Enable it with: claude plugin enable superpowers@claude-plugins-official"
486 ;;
487 missing)
488 echo "❌ Error: Claude Code Superpowers is required for --brainstorm and --writing-plan."
489 echo "Install it in Claude Code with: /plugin install superpowers@claude-plugins-official"
490 ;;
491 *)
492 echo "❌ Error: Unable to determine Claude Code Superpowers status."
493 echo "Run 'claude plugin list' to diagnose the problem."
494 ;;
495 esac
496
497 return 1
498}
499
500_plan_build_v2() {
501 # v2 is the standalone plan_build script (adds --grill and --distribute),
502 # fetched on demand from its own gist so this function never goes stale.
503 local v2_url="https://opengist.resetrix.work/weehong/plan-build/raw/HEAD/plan_build.sh"
504 local v2_file
505 local v2_status
506
507 require_cli curl "curl" || return 1
508
509 v2_file="$(mktemp "${TMPDIR:-/tmp}/plan-build-v2.XXXXXX")" || return 1
510
511 if ! curl -fsSL "$v2_url" -o "$v2_file"; then
512 rm -f "$v2_file"
513 echo "❌ Error: Unable to download plan_build v2 from $v2_url"
514 return 1
515 fi
516
517 bash "$v2_file" "$@"
518 v2_status=$?
519 rm -f "$v2_file"
520 return $v2_status
521}
522
523plan_build() {
524 local claude_args=()
525 local use_yolo=0
526 local enhance_payload=0
527 local planning_mode="standard"
528 local usage="Usage: plan_build [--v2] [--yolo] [--prompt] [--brainstorm | --writing-plan]
529 --v2 forwards all other arguments to the standalone v2 script (adds --grill and --distribute)."
530
531 local use_v2=0
532 local arg
533 local passthrough=()
534 for arg in "$@"; do
535 if [ "$arg" = "--v2" ]; then
536 use_v2=1
537 else
538 passthrough+=("$arg")
539 fi
540 done
541
542 if [ "$use_v2" -eq 1 ]; then
543 _plan_build_v2 "${passthrough[@]}"
544 return $?
545 fi
546
547 while [ $# -gt 0 ]; do
548 case "$1" in
549 --yolo)
550 if [ "$use_yolo" -eq 1 ]; then
551 echo "Error: Duplicate argument: --yolo"
552 echo "$usage"
553 return 1
554 fi
555 use_yolo=1
556 claude_args=(--yolo)
557 ;;
558 --prompt)
559 if [ "$enhance_payload" -eq 1 ]; then
560 echo "Error: Duplicate argument: --prompt"
561 echo "$usage"
562 return 1
563 fi
564 enhance_payload=1
565 ;;
566 --brainstorm)
567 if [ "$planning_mode" = "brainstorm" ]; then
568 echo "Error: Duplicate argument: --brainstorm"
569 echo "$usage"
570 return 1
571 fi
572 if [ "$planning_mode" != "standard" ]; then
573 echo "Error: --brainstorm and --writing-plan are mutually exclusive."
574 echo "$usage"
575 return 1
576 fi
577 planning_mode="brainstorm"
578 ;;
579 --writing-plan)
580 if [ "$planning_mode" = "writing-plan" ]; then
581 echo "Error: Duplicate argument: --writing-plan"
582 echo "$usage"
583 return 1
584 fi
585 if [ "$planning_mode" != "standard" ]; then
586 echo "Error: --brainstorm and --writing-plan are mutually exclusive."
587 echo "$usage"
588 return 1
589 fi
590 planning_mode="writing-plan"
591 ;;
592 *)
593 echo "Error: Unknown argument: $1"
594 echo "$usage"
595 return 1
596 ;;
597 esac
598 shift
599 done
600
601 require_cli claude "Claude Code CLI" || return 1
602 if [ "$planning_mode" != "standard" ]; then
603 _plan_build_superpowers_preflight || return 1
604 fi
605 require_cli codex "Codex CLI" || return 1
606 require_cli coderabbit "CodeRabbit CLI" || return 1
607 if [ "$enhance_payload" -eq 1 ]; then
608 require_cli auggie "Auggie CLI" || return 1
609 require_cli script "script utility" || return 1
610 fi
611
612 echo "📥 Reading payload... Type 'EOF' on a new line and press Enter when finished."
613
614 local payload
615 local line
616 payload=""
617
618 while IFS= read -r line; do
619 [ "$line" = "EOF" ] && break
620 if [ -z "$payload" ]; then
621 payload="$line"
622 else
623 payload="${payload}"$'\n'"${line}"
624 fi
625 done
626
627 if [ -z "$payload" ]; then
628 echo "❌ Error: Payload was empty."
629 return 1
630 fi
631
632 if [ "$enhance_payload" -eq 1 ]; then
633 local enhanced_file
634 local review_reply
635 enhanced_file="$(mktemp "${TMPDIR:-/tmp}/plan-build-enhanced.XXXXXX")" || return 1
636
637 echo "✨ Enhancing payload with Auggie..."
638 if ! _enhance_prompt "$payload" "$enhanced_file"; then
639 rm -f "$enhanced_file"
640 echo "❌ Error: Prompt enhancement failed; Claude was not launched."
641 return 1
642 fi
643
644 payload="$(cat "$enhanced_file")"
645 rm -f "$enhanced_file"
646
647 if [ -z "$payload" ]; then
648 echo "❌ Error: Enhanced payload was empty; Claude was not launched."
649 return 1
650 fi
651
652 printf '\n%s\n' "━━━━━━━━━━━━━━━━ Auggie enhanced prompt ━━━━━━━━━━━━━━━━"
653 printf '%s\n' "$payload"
654 printf '%s\n\n' "━━━━━━━━━━━━━━━━ End enhanced prompt ━━━━━━━━━━━━━━━━━"
655
656 if ! (: </dev/tty) 2>/dev/null; then
657 echo "❌ Error: Cannot review the enhanced prompt without an interactive terminal; Claude was not launched."
658 return 1
659 fi
660
661 printf "Proceed with this enhanced prompt? (y/N) " >/dev/tty
662 if ! IFS= read -r review_reply </dev/tty; then
663 printf '\n' >/dev/tty
664 echo "🛑 Review cancelled; Claude was not launched."
665 return 1
666 fi
667
668 case "$review_reply" in
669 y|Y|yes|YES|Yes)
670 echo "✅ Enhanced prompt approved."
671 ;;
672 *)
673 echo "🛑 Enhanced prompt not approved; Claude was not launched."
674 return 0
675 ;;
676 esac
677 fi
678
679 local planning_instruction
680 case "$planning_mode" in
681 brainstorm)
682 planning_instruction="Planning mode: brainstorm. Use the superpowers:brainstorming skill, including its approval gates and transition to superpowers:writing-plans. After the implementation plan is saved, return to the plan-build workflow for Codex plan review before implementation."
683 ;;
684 writing-plan)
685 planning_instruction="Planning mode: writing-plan. Use the superpowers:writing-plans skill with the payload as the requirements. After the implementation plan is saved, return to the plan-build workflow for Codex plan review before implementation."
686 ;;
687 *)
688 planning_instruction="Planning mode: standard. Create the workflow's normal short implementation plan."
689 ;;
690 esac
691
692 echo "🚀 Launching Claude Code with your multi-agent workflow ($planning_mode planning)..."
693
694 claude "${claude_args[@]}" "Please read \`~/.claude/skills/plan-build/SKILL.md\` and strictly follow the 8-step multi-agent workflow to implement the following task.
695
696$planning_instruction
697
698$payload"
699}
700
701# -----------------------------------------------------------------------------
702# Function: aicommit (Automated Conventional Commit Engine Wrapper)
703# -----------------------------------------------------------------------------
704aicommit() {
705 local diff engine prompt
706
707 # 1. Get diff safely: exclude lock files/minified files and cap at ~100k characters to prevent CLI crashes
708 diff=$(git diff --cached -- . ":(exclude)*.lock" ":(exclude)*-lock.json" ":(exclude)*.min.js" | head -c 100000)
709
710 if [ -z "$diff" ]; then
711 printf "❌ Error: Nothing staged to commit.\n" >&2
712 return 1
713 fi
714
715 if [ ! -f "$HOME/.config/ai-commit-prompt.txt" ]; then
716 printf "❌ Error: Configuration file not found at ~/.config/ai-commit-prompt.txt\n" >&2
717 printf "Please create it and paste your system prompt inside.\n" >&2
718 return 1
719 fi
720
721 engine=${1:-claude}
722 prompt=$(cat "$HOME/.config/ai-commit-prompt.txt")
723
724 case "$engine" in
725 claude)
726 echo "🤖 Claude is analyzing staged changes..."
727 cat "$HOME/.config/ai-commit-prompt.txt" <(echo -e "\n\nHere is the git diff:\n$diff") \
728 | command claude --allowedTools 'Bash(git commit *)' --permission-mode dontAsk -p \
729 "Read the provided instructions and diff, then generate and execute the commit."
730 ;;
731
732 codex)
733 echo "🤖 Codex is analyzing staged changes..."
734 cat "$HOME/.config/ai-commit-prompt.txt" <(echo -e "\n\nHere is the git diff:\n$diff") \
735 | command codex --ask-for-approval never exec --sandbox workspace-write --ephemeral \
736 "Read the provided instructions and diff, then generate and execute the commit."
737 ;;
738
739 copilot)
740 echo "🤖 Copilot is analyzing staged changes..."
741 copilot -p "$prompt
742
743Here is the git diff:
744$diff" -s --no-ask-user --allow-tool='shell(git:*)'
745 ;;
746
747 opencode)
748 echo "🤖 OpenCode is analyzing staged changes..."
749 cat "$HOME/.config/ai-commit-prompt.txt" <(echo -e "\n\nHere is the git diff:\n$diff") | opencode run
750 ;;
751
752 *)
753 printf "❌ Error: Invalid engine '%s'\n" "$engine" >&2
754 printf "Usage: aicommit [claude | codex | copilot | opencode]\n" >&2
755 return 1
756 ;;
757 esac
758}
759
760# -----------------------------------------------------------------------------
761# Function: create (Efficiently create file + parent dirs)
762# -----------------------------------------------------------------------------
763create() {
764 if ! command -v install >/dev/null 2>&1; then
765 printf "Error: 'install' coreutil is not available.\n" >&2
766 return 1
767 fi
768
769 if [ $# -eq 0 ]; then
770 printf "Usage: create <file> [file ...]\n" >&2
771 return 2
772 fi
773
774 for p in "$@"; do
775 # Expand tilde manually if shell doesn't
776 [[ "$p" == "~/"* ]] && p="${HOME}/${p#\~/}"
777
778 if [ -z "$p" ] || [ "$p" = "/" ]; then
779 printf "create: refusing to operate on '%s'\n" "$p" >&2
780 continue
781 fi
782
783 if [ -e "$p" ]; then
784 printf "create: '%s' already exists. Skipping.\n" "$p" >&2
785 continue
786 fi
787
788 if install -D /dev/null "$p"; then
789 echo "Created '$p'"
790 fi
791 done
792}
793
794# -----------------------------------------------------------------------------
795# Function: kill_port (Kill or check the process using a port)
796# -----------------------------------------------------------------------------
797kill_port() {
798 local action="kill"
799 local signal="TERM"
800 local port
801 local pids
802 local pid
803
804 case "${1:-}" in
805 -c|--check)
806 action="check"
807 shift
808 ;;
809 -k|--kill)
810 action="kill"
811 shift
812 ;;
813 -f|--force)
814 action="kill"
815 signal="KILL"
816 shift
817 ;;
818 -h|--help|"")
819 printf "Usage: kill_port [--check|-c|--kill|-k|--force|-f] <port_number>\n" >&2
820 return 1
821 ;;
822 esac
823
824 port="${1:-}"
825 if [[ ! "$port" =~ ^[0-9]+$ ]] || [ "$port" -lt 1 ] || [ "$port" -gt 65535 ]; then
826 printf "Error: port must be a number between 1 and 65535.\n" >&2
827 printf "Usage: kill_port [--check|-c|--kill|-k|--force|-f] <port_number>\n" >&2
828 return 1
829 fi
830
831 if [[ "$OSTYPE" == "darwin"* ]]; then
832 pids="$(
833 {
834 lsof -nP -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null
835 lsof -nP -tiUDP:"$port" 2>/dev/null
836 } | sort -u
837 )"
838
839 if [ -z "$pids" ]; then
840 printf "Port %s is FREE\n" "$port"
841 return 0
842 fi
843
844 printf "Port %s is in use:\n" "$port"
845 lsof -nP -iTCP:"$port" -sTCP:LISTEN -iUDP:"$port" 2>/dev/null
846 else
847 local port_hex
848 local inodes
849
850 port_hex="$(printf "%04X" "$port")"
851 inodes="$(
852 awk -v port="$port_hex" '
853 NR > 1 {
854 split($2, local_addr, ":")
855 if (toupper(local_addr[length(local_addr)]) == port && ($4 == "0A" || FILENAME ~ /udp/)) {
856 print $10
857 }
858 }
859 ' /proc/net/tcp /proc/net/tcp6 /proc/net/udp /proc/net/udp6 2>/dev/null | sort -u
860 )"
861
862 if [ -z "$inodes" ]; then
863 printf "Port %s is FREE\n" "$port"
864 return 0
865 fi
866
867 pids=""
868 pids="$(
869 find /proc/[0-9]*/fd -maxdepth 1 -type l -printf "%p %l\n" 2>/dev/null |
870 awk -v inodes="$inodes" '
871 BEGIN {
872 split(inodes, inode_list, " ")
873 for (i in inode_list) {
874 wanted["socket:[" inode_list[i] "]"] = 1
875 }
876 }
877 $2 in wanted {
878 split($1, path, "/")
879 print path[3]
880 }
881 ' | sort -u
882 )"
883
884 if [ -z "$pids" ]; then
885 printf "Port %s is in use, but no readable process owner was found.\n" "$port" >&2
886 return 1
887 fi
888
889 printf "Port %s is in use:\n" "$port"
890 for pid in $pids; do
891 if [ -r "/proc/$pid/cmdline" ]; then
892 printf " PID %-8s %s\n" "$pid" "$(tr '\0' ' ' < "/proc/$pid/cmdline")"
893 else
894 printf " PID %s\n" "$pid"
895 fi
896 done
897 fi
898
899 if [ "$action" = "kill" ]; then
900 for pid in $pids; do
901 if kill "-$signal" "$pid" 2>/dev/null; then
902 printf "Killed PID %s with SIG%s.\n" "$pid" "$signal"
903 else
904 printf "Error: failed to kill PID %s. Try with sudo or --force.\n" "$pid" >&2
905 return 1
906 fi
907 done
908 fi
909}
910
911# -----------------------------------------------------------------------------
912# Function: append_path (Adds to session PATH if exists)
913# -----------------------------------------------------------------------------
914append_path() {
915 local dir="$1"
916 if [ -d "$dir" ]; then
917 case ":$PATH:" in
918 *":$dir:"*) ;;
919 *) PATH="$PATH:$dir" ;;
920 esac
921 elif [ "${SUPPRESS_WARNINGS:-0}" -ne 1 ]; then
922 echo "Warning: $dir does not exist." >&2
923 fi
924}
925
926# -----------------------------------------------------------------------------
927# Function: add_path_to_config (Fixed awk/sed injection)
928# -----------------------------------------------------------------------------
929add_path_to_config() {
930 local new_path="$1"
931 local config_file="$HOME/.bashrc"
932 [[ "$SHELL" == *"zsh"* ]] && config_file="$HOME/.zshrc"
933
934 if [ -z "$new_path" ]; then
935 printf "Usage: add_path_to_config <directory>\n" >&2
936 return 1
937 fi
938
939 new_path="${new_path/#\~/$HOME}"
940
941 if [ ! -d "$new_path" ]; then
942 printf "Warning: '%s' does not exist. Add anyway? (y/N) " "$new_path"
943 read -r REPLY
944 [[ ! $REPLY =~ ^[Yy]$ ]] && return 1
945 fi
946
947 # Check if already exists in file
948 if grep -Fq "append_path \"$new_path\"" "$config_file"; then
949 printf "Notice: Already in %s.\n" "$config_file"
950 return 0
951 fi
952
953 # Robust Injection: Use sed to insert before export PATH or just append
954 if grep -q "^export PATH" "$config_file"; then
955 sed -i.bak "/^export PATH/i append_path \"$new_path\"" "$config_file"
956 else
957 echo "append_path \"$new_path\"" >> "$config_file"
958 echo "export PATH" >> "$config_file"
959 fi
960
961 printf "✅ Success: Added to %s\n" "$config_file"
962 append_path "$new_path" && export PATH
963}
964
orchestrate-loop.md Bruto

Plan-Build Orchestrate Loop

Use this workflow when plan_build hands Claude Code an implementation task. The goal is to keep Claude as the orchestrator while using Codex and CodeRabbit as independent review and validation agents. When plan_build supplies "Distribution mode: on", Codex and Kimi Code also become the implementers (backend and frontend respectively) via the Distributed Implementation variant of step 5.

Operating Rules

  • Run from the project root. Treat the current working directory as the project to modify.
  • Preserve user work. Check git status before edits and do not revert unrelated changes.
  • Keep implementation scoped to the payload unless repository context proves a wider change is required.
  • Prefer existing project conventions, scripts, test commands, and dependency managers.
  • Do not call the task complete until validation has run or the reason it cannot run is documented.
  • If any agent reports a plausible correctness, security, data-loss, migration, or test risk, resolve it or explicitly document why it is not applicable.

The 8-Step Workflow

1. Intake

Read the user payload fully. Identify:

  • Objective and expected user-visible behavior.
  • Files, modules, commands, and frameworks likely involved.
  • Constraints from repository docs, package scripts, CI config, and existing patterns.
  • Any ambiguity that blocks safe execution.

Only ask the user a question when no reasonable project-local assumption is safe.

2. Baseline

Inspect the repository before changing files:

git status --short
rg --files

Then read the smallest useful set of files. Prefer rg, package manifests, tests, routing files, and nearby implementations over broad file dumps.

3. Plan

Follow the planning mode supplied by plan_build:

  • standard: Create a short implementation plan with concrete steps and validation commands.
  • brainstorm: Invoke superpowers:brainstorming, honor its design and written-spec approval gates, and let it transition to superpowers:writing-plans after approval.
  • writing-plan: Invoke superpowers:writing-plans directly, treating the payload as the requirements or specification.
  • grill: Invoke mattpocock-skills:grilling to relentlessly stress-test the payload and your intended approach with the user. After grilling concludes, write the workflow's normal short implementation plan incorporating what survived.

For either Superpowers mode, save the artifacts at the paths selected by the skills. In grill mode, the short implementation plan is the planning artifact. When writing-plans reaches its execution handoff, return to this workflow instead of starting implementation: Codex must review the plan first. If the user rejects or cancels a required approval, stop cleanly without modifying implementation files.

If the task touches behavior, data, auth, payments, destructive actions, or shared infrastructure, include a rollback or compatibility note.

4. Codex Plan Review

Run this step only for brainstorm, writing-plan, and grill modes. Ask Codex for an independent, read-only review of the approved spec, when present, and the implementation plan before touching implementation files. Provide the original payload and artifact paths. Ask it to focus on requirement coverage, incorrect assumptions, unsafe migrations, missing edge cases, inadequate tests, and steps that are too vague to execute.

Recommended prompt shape:

Review these planning artifacts before implementation. Check requirement coverage, technical correctness, repository fit, edge cases, migration or rollback risk, test coverage, and whether every step is executable. Report concrete findings only; do not modify files.

Task:
<payload>

Spec:
<spec path, if present>

Implementation plan:
<plan path>

Use a read-only, ephemeral Codex invocation. Resolve every valid finding in the artifacts and repeat the review if revisions are substantial. If an artifact is missing or empty, or Codex cannot complete the review, stop before implementation and report the failure.

In standard mode, skip this step and continue directly to implementation.

5. Implement

If plan_build supplied "Distribution mode: on", skip this step and step 6 and follow the Distributed Implementation variant (steps 5a–5c) below instead, then continue at step 7.

Make the change in small, reviewable edits:

  • Follow existing style and abstractions.
  • Add or update tests when behavior changes.
  • Update docs only when user-facing usage changes.
  • Avoid unrelated refactors and formatting churn.

After each meaningful edit group, re-check the diff for accidental changes.

Distributed Implementation (distribution mode: on)

In this variant you orchestrate and review only — you never implement, not even leftovers. Codex implements backend items; Kimi Code implements frontend items.

5a. Split And Confirm

Tag every work item in the plan as BE, FE, or unclear. Present the full table to the user (AskUserQuestion) and have them confirm or reassign each item; unclear items must be assigned by the user to BE or FE. Dispatch nothing until every item is confirmed.

5b. Backend Phase (Codex)

Check git status first so pre-existing work is never mixed in or reverted. Compose a self-contained brief: the task, constraints, the exact confirmed BE item list with file paths, project conventions, and validation commands. Dispatch:

codex exec --full-auto "<brief>"

Then review the phase diff (git diff) yourself for correctness, scope, and convention fit. Send findings back to Codex as a new brief (findings plus the original context) — do not fix them yourself. Maximum 2 retry rounds; if valid findings remain after that, stop and report with the partial diff intact.

An empty diff from a delegate counts as a failure: retry once with a sharpened brief, within the same 2-round bound. If the delegate CLI crashes or hangs, stop and report, preserving the partial diff.

5c. Frontend Phase (Kimi Code)

Check git status again, then compose the FE brief the same way, and additionally include the now-implemented backend API surface (routes, types, request/response contracts) so the frontend builds against the real backend. Dispatch:

kimi -p "<brief>"

(-p runs one prompt non-interactively and manages permissions itself; it cannot be combined with --auto or --yolo.)

Review the phase diff and route findings back to Kimi under the same 2-round retry bound and failure rules as the backend phase.

After both phases pass review, skip step 6 (the per-phase reviews above replace it — Codex cannot impartially review the half it wrote) and continue at step 7 with the combined diff.

6. Codex Code Review Pass

Skip this step in distribution mode; the per-phase reviews in steps 5b–5c replace it.

Ask Codex for an independent review of the local diff before finalizing. Provide the task, constraints, and current diff. Ask it to focus on bugs, edge cases, missing tests, regressions, and simpler project-native alternatives.

Recommended prompt shape:

Review this change for correctness and risk. Prioritize bugs, regressions, missing tests, and mismatches with existing project patterns. Do not rewrite the whole solution unless a specific issue requires it.

Task:
<payload>

Diff:
<git diff>

Apply fixes for valid findings, then repeat this review pass if the fixes are non-trivial.

7. CodeRabbit Review Pass

Run CodeRabbit on the branch or diff when available. Treat its output as advisory but investigate every concrete finding.

If CodeRabbit cannot run locally, record the command attempted and the failure. Continue with manual validation rather than blocking indefinitely.

8. Validate And Close

Run the planned validation commands, such as:

npm test
npm run lint
pytest
cargo test
go test ./...

Use the commands that actually exist in the project. If validation fails, fix the issue and rerun the relevant command. If a failure is unrelated or environmental, capture the evidence.

Before final response:

  • Confirm git diff contains only intended changes.
  • Summarize what changed.
  • Report validation run and result.
  • Note any remaining risks or commands that could not run.
pathrc Bruto
1# =============================================================================
2# ENVIRONMENT & PATH CONFIGURATION
3# =============================================================================
4
5# 1. Source functions first
6if [[ -f "$HOME/.func" && -r "$HOME/.func" ]]; then
7 source "$HOME/.func"
8fi
9
10# 2. Define Root Variables
11export DOTNET_ROOT="$HOME/.dotnet"
12
13# 3. PATH INITIALIZATION
14# -----------------------------------------------------------------------------
15append_path "$HOME/.local/bin"
16append_path "$DOTNET_ROOT"
17append_path "$DOTNET_ROOT/tools"
18append_path "$HOME/.opencode/bin"
19append_path "$HOME/Flutter/bin"
20
21# macOS specific paths
22if [[ "$OSTYPE" == "darwin"* ]]; then
23 append_path "/Applications/Espanso.app/Contents/MacOS"
24fi
25
26# Finalize PATH
27export PATH
sourcerc Bruto
1# =============================================================================
2# FILE: ~/.sourcerc
3# Description: Initializes third-party package managers and external tools.
4# =============================================================================
5
6# =============================================================================
7# GENERAL ENV
8# =============================================================================
9if [[ -f "$HOME/.local/bin/env" && -r "$HOME/.local/bin/env" ]]; then
10 source "$HOME/.local/bin/env"
11fi
12
13# =============================================================================
14# CLAUDE CODE / AI GATEWAY
15# =============================================================================
16export ANTHROPIC_BASE_URL="https://gateway.ai.cloudflare.com/v1/9a71825e3842e918e0dff9ad84f50484/claude-code-gateway/anthropic"
17
18# =============================================================================
19# SDKMAN (Java/Kotlin/Scala Version Manager)
20# =============================================================================
21export SDKMAN_DIR="$HOME/.sdkman"
22if [[ -f "$SDKMAN_DIR/bin/sdkman-init.sh" && -r "$SDKMAN_DIR/bin/sdkman-init.sh" ]]; then
23 source "$SDKMAN_DIR/bin/sdkman-init.sh"
24fi
25
26# =============================================================================
27# NVM (Node Version Manager)
28# =============================================================================
29export NVM_DIR="$HOME/.nvm"
30if [[ -f "$NVM_DIR/nvm.sh" && -r "$NVM_DIR/nvm.sh" ]]; then
31 source "$NVM_DIR/nvm.sh"
32fi
33if [[ -f "$NVM_DIR/bash_completion" && -r "$NVM_DIR/bash_completion" ]]; then
34 source "$NVM_DIR/bash_completion"
35fi
36
37# =============================================================================
38# GOOGLE CLOUD SDK
39# =============================================================================
40if [[ -f "/opt/google-cloud-sdk/path.zsh.inc" ]]; then
41 source "/opt/google-cloud-sdk/path.zsh.inc"
42fi
43
44if [[ -f "/opt/google-cloud-sdk/completion.zsh.inc" ]]; then
45 source "/opt/google-cloud-sdk/completion.zsh.inc"
46fi
47
starship.toml Bruto
1"$schema" = 'https://starship.rs/config-schema.json'
2
3format = """
4[](red)\
5$os\
6$username\
7[](bg:peach fg:red)\
8$directory\
9[](bg:yellow fg:peach)\
10$git_branch\
11$git_status\
12[](fg:yellow bg:green)\
13$c\
14$rust\
15$golang\
16$nodejs\
17$bun\
18$php\
19$java\
20$kotlin\
21$haskell\
22$python\
23[](fg:green bg:sapphire)\
24$conda\
25[](fg:sapphire bg:lavender)\
26$time\
27[ ](fg:lavender)\
28$cmd_duration\
29$line_break\
30$character"""
31
32palette = 'catppuccin_mocha'
33
34[os]
35disabled = false
36style = "bg:red fg:crust"
37
38[os.symbols]
39Windows = ""
40Ubuntu = "󰕈"
41SUSE = ""
42Raspbian = "󰐿"
43Mint = "󰣭"
44Macos = "󰀵"
45Manjaro = ""
46Linux = "󰌽"
47Gentoo = "󰣨"
48Fedora = "󰣛"
49Alpine = ""
50Amazon = ""
51Android = ""
52AOSC = ""
53Arch = "󰣇"
54Artix = "󰣇"
55CentOS = ""
56Debian = "󰣚"
57Redhat = "󱄛"
58RedHatEnterprise = "󱄛"
59
60[username]
61show_always = true
62style_user = "bg:red fg:crust"
63style_root = "bg:red fg:crust"
64format = '[ $user]($style)'
65
66[directory]
67style = "bg:peach fg:crust"
68format = "[ $path ]($style)"
69truncation_length = 3
70truncation_symbol = "…/"
71
72[directory.substitutions]
73"Documents" = "󰈙 "
74"Downloads" = " "
75"Music" = "󰝚 "
76"Pictures" = " "
77"Developer" = "󰲋 "
78
79[git_branch]
80symbol = ""
81style = "bg:yellow"
82format = '[[ $symbol $branch ](fg:crust bg:yellow)]($style)'
83
84[git_status]
85style = "bg:yellow"
86format = '[[($all_status$ahead_behind )](fg:crust bg:yellow)]($style)'
87
88[nodejs]
89symbol = ""
90style = "bg:green"
91format = '[[ $symbol( $version) ](fg:crust bg:green)]($style)'
92
93[bun]
94symbol = ""
95style = "bg:green"
96format = '[[ $symbol( $version) ](fg:crust bg:green)]($style)'
97
98[c]
99symbol = " "
100style = "bg:green"
101format = '[[ $symbol( $version) ](fg:crust bg:green)]($style)'
102
103[rust]
104symbol = ""
105style = "bg:green"
106format = '[[ $symbol( $version) ](fg:crust bg:green)]($style)'
107
108[golang]
109symbol = ""
110style = "bg:green"
111format = '[[ $symbol( $version) ](fg:crust bg:green)]($style)'
112
113[php]
114symbol = ""
115style = "bg:green"
116format = '[[ $symbol( $version) ](fg:crust bg:green)]($style)'
117
118[java]
119symbol = " "
120style = "bg:green"
121format = '[[ $symbol( $version) ](fg:crust bg:green)]($style)'
122
123[kotlin]
124symbol = ""
125style = "bg:green"
126format = '[[ $symbol( $version) ](fg:crust bg:green)]($style)'
127
128[haskell]
129symbol = ""
130style = "bg:green"
131format = '[[ $symbol( $version) ](fg:crust bg:green)]($style)'
132
133[python]
134symbol = ""
135style = "bg:green"
136format = '[[ $symbol( $version)(\(#$virtualenv\)) ](fg:crust bg:green)]($style)'
137
138[docker_context]
139symbol = ""
140style = "bg:sapphire"
141format = '[[ $symbol( $context) ](fg:crust bg:sapphire)]($style)'
142
143[conda]
144symbol = "  "
145style = "fg:crust bg:sapphire"
146format = '[$symbol$environment ]($style)'
147ignore_base = false
148
149[time]
150disabled = false
151time_format = "%R"
152style = "bg:lavender"
153format = '[[  $time ](fg:crust bg:lavender)]($style)'
154
155[line_break]
156disabled = false
157
158[character]
159disabled = false
160success_symbol = '[❯](bold fg:green)'
161error_symbol = '[❯](bold fg:red)'
162vimcmd_symbol = '[❮](bold fg:green)'
163vimcmd_replace_one_symbol = '[❮](bold fg:lavender)'
164vimcmd_replace_symbol = '[❮](bold fg:lavender)'
165vimcmd_visual_symbol = '[❮](bold fg:yellow)'
166
167[cmd_duration]
168show_milliseconds = true
169format = " in $duration "
170style = "bg:lavender"
171disabled = false
172show_notifications = true
173min_time_to_notify = 45000
174
175[palettes.catppuccin_mocha]
176rosewater = "#f5e0dc"
177flamingo = "#f2cdcd"
178pink = "#f5c2e7"
179mauve = "#cba6f7"
180red = "#f38ba8"
181maroon = "#eba0ac"
182peach = "#fab387"
183yellow = "#f9e2af"
184green = "#a6e3a1"
185teal = "#94e2d5"
186sky = "#89dceb"
187sapphire = "#74c7ec"
188blue = "#89b4fa"
189lavender = "#b4befe"
190text = "#cdd6f4"
191subtext1 = "#bac2de"
192subtext0 = "#a6adc8"
193overlay2 = "#9399b2"
194overlay1 = "#7f849c"
195overlay0 = "#6c7086"
196surface2 = "#585b70"
197surface1 = "#45475a"
198surface0 = "#313244"
199base = "#1e1e2e"
200mantle = "#181825"
201crust = "#11111b"
202
203[palettes.catppuccin_frappe]
204rosewater = "#f2d5cf"
205flamingo = "#eebebe"
206pink = "#f4b8e4"
207mauve = "#ca9ee6"
208red = "#e78284"
209maroon = "#ea999c"
210peach = "#ef9f76"
211yellow = "#e5c890"
212green = "#a6d189"
213teal = "#81c8be"
214sky = "#99d1db"
215sapphire = "#85c1dc"
216blue = "#8caaee"
217lavender = "#babbf1"
218text = "#c6d0f5"
219subtext1 = "#b5bfe2"
220subtext0 = "#a5adce"
221overlay2 = "#949cbb"
222overlay1 = "#838ba7"
223overlay0 = "#737994"
224surface2 = "#626880"
225surface1 = "#51576d"
226surface0 = "#414559"
227base = "#303446"
228mantle = "#292c3c"
229crust = "#232634"
230
231[palettes.catppuccin_latte]
232rosewater = "#dc8a78"
233flamingo = "#dd7878"
234pink = "#ea76cb"
235mauve = "#8839ef"
236red = "#d20f39"
237maroon = "#e64553"
238peach = "#fe640b"
239yellow = "#df8e1d"
240green = "#40a02b"
241teal = "#179299"
242sky = "#04a5e5"
243sapphire = "#209fb5"
244blue = "#1e66f5"
245lavender = "#7287fd"
246text = "#4c4f69"
247subtext1 = "#5c5f77"
248subtext0 = "#6c6f85"
249overlay2 = "#7c7f93"
250overlay1 = "#8c8fa1"
251overlay0 = "#9ca0b0"
252surface2 = "#acb0be"
253surface1 = "#bcc0cc"
254surface0 = "#ccd0da"
255base = "#eff1f5"
256mantle = "#e6e9ef"
257crust = "#dce0e8"
258
259[palettes.catppuccin_macchiato]
260rosewater = "#f4dbd6"
261flamingo = "#f0c6c6"
262pink = "#f5bde6"
263mauve = "#c6a0f6"
264red = "#ed8796"
265maroon = "#ee99a0"
266peach = "#f5a97f"
267yellow = "#eed49f"
268green = "#a6da95"
269teal = "#8bd5ca"
270sky = "#91d7e3"
271sapphire = "#7dc4e4"
272blue = "#8aadf4"
273lavender = "#b7bdf8"
274text = "#cad3f5"
275subtext1 = "#b8c0e0"
276subtext0 = "#a5adcb"
277overlay2 = "#939ab7"
278overlay1 = "#8087a2"
279overlay0 = "#6e738d"
280surface2 = "#5b6078"
281surface1 = "#494d64"
282surface0 = "#363a4f"
283base = "#24273a"
284mantle = "#1e2030"
285crust = "#181926"
286
test_plan_build.zsh Bruto
1#!/usr/bin/env zsh
2
3set -eu
4
5repo_dir="${0:A:h}"
6source "$repo_dir/func"
7
8failures=0
9
10assert_equal() {
11 local expected="$1"
12 local actual="$2"
13 local label="$3"
14
15 if [ "$actual" != "$expected" ]; then
16 print -u2 -- "FAIL: $label (expected '$expected', got '$actual')"
17 failures=$((failures + 1))
18 fi
19}
20
21assert_contains() {
22 local output="$1"
23 local expected="$2"
24 local label="$3"
25
26 if [[ "$output" != *"$expected"* ]]; then
27 print -u2 -- "FAIL: $label (missing '$expected')"
28 failures=$((failures + 1))
29 fi
30}
31
32plugin_state() {
33 printf '%s\n' "$1" | _plan_build_superpowers_state
34}
35
36assert_equal "enabled" "$(plugin_state '[{"id":"superpowers@claude-plugins-official","enabled":true}]')" "compact enabled JSON"
37assert_equal "disabled" "$(plugin_state $'[\n {\n "enabled": false,\n "id": "superpowers@claude-plugins-official"\n }\n]')" "reordered disabled JSON"
38assert_equal "enabled" "$(plugin_state $'[\n {"enabled": false, "id": "other@market"},\n {"enabled": true, "id": "superpowers@claude-plugins-official"}\n]')" "multiple plugin JSON"
39assert_equal "missing" "$(plugin_state '[{"id":"coderabbit@claude-plugins-official","enabled":true}]')" "missing plugin JSON"
40assert_equal "installed" "$(plugin_state '[{"id":"superpowers@claude-plugins-official"}]')" "malformed plugin JSON"
41
42output="$(plan_build --brainstorm --writing-plan 2>&1)" && rc=0 || rc=$?
43assert_equal "1" "$rc" "mutually exclusive flags status"
44assert_contains "$output" "mutually exclusive" "mutually exclusive flags message"
45
46output="$(plan_build --brainstorm --brainstorm 2>&1)" && rc=0 || rc=$?
47assert_equal "1" "$rc" "duplicate brainstorm status"
48assert_contains "$output" "Duplicate argument: --brainstorm" "duplicate brainstorm message"
49
50output="$(plan_build --writing-plan --writing-plan 2>&1)" && rc=0 || rc=$?
51assert_equal "1" "$rc" "duplicate writing-plan status"
52assert_contains "$output" "Duplicate argument: --writing-plan" "duplicate writing-plan message"
53
54export CLAUDE_CODE_SAFE_MODE=1
55output="$(_plan_build_superpowers_preflight 2>&1)" && rc=0 || rc=$?
56unset CLAUDE_CODE_SAFE_MODE
57assert_equal "1" "$rc" "safe mode status"
58assert_contains "$output" "safe mode disables Superpowers" "safe mode message"
59
60require_cli() { return 0; }
61_plan_build_superpowers_preflight() { return 0; }
62claude() {
63 printf 'CLAUDE_ARGS:'
64 printf ' <%s>' "$@"
65 printf '\n'
66}
67
68output="$(printf 'build feature\nEOF\n' | plan_build --writing-plan --yolo 2>&1)" && rc=0 || rc=$?
69assert_equal "0" "$rc" "writing-plan launch status"
70assert_contains "$output" "Planning mode: writing-plan" "writing-plan prompt"
71assert_contains "$output" "<--yolo>" "yolo forwarding"
72
73output="$(printf 'build feature\nEOF\n' | plan_build --brainstorm 2>&1)" && rc=0 || rc=$?
74assert_equal "0" "$rc" "brainstorm launch status"
75assert_contains "$output" "Planning mode: brainstorm" "brainstorm prompt"
76
77output="$(printf 'build feature\nEOF\n' | plan_build 2>&1)" && rc=0 || rc=$?
78assert_equal "0" "$rc" "standard launch status"
79assert_contains "$output" "Planning mode: standard" "standard prompt"
80
81if [ "$failures" -ne 0 ]; then
82 exit 1
83fi
84
85print -- "PASS: plan_build tests"
86
vimrc Bruto
1" Enable line numbers
2set number
3
4" Enable relative line numbers
5set relativenumber
6
7" Enable syntax highlighting
8syntax on
9
10" Set colorscheme
11colorscheme slate
12
13" Enable file type detection and plugins
14filetype plugin indent on
15
16" Set the tab width to 4 spaces
17set tabstop=4
18set shiftwidth=4
19set expandtab
20
21" Enable auto-indentation
22set autoindent
23set smartindent
24
25" Highlight current line
26set cursorline
27
28" Show matching parentheses
29set showmatch
30
31" Enable line wrapping
32set wrap
33
34" Enable mouse support
35set mouse=a
36
37" Enable clipboard access
38set clipboard=unnamedplus
39
40" Disable swap file
41set noswapfile
42
43" Enable incremental search
44set incsearch
45
46" Ignore case in search
47set ignorecase
48
49" Override ignorecase if search contains capital letters
50set smartcase
51
52" Display line and column number of the cursor position
53set ruler
54
55" Set the status line at the bottom
56set laststatus=2
57
58" Show command in bottom bar
59set showcmd
60
61" Set command height
62set cmdheight=2
63
64" Set history lines
65set history=1000
66
67" Disable backup file
68set nobackup
69
70" Enable persistent undo
71set undofile
72
73" Set maximum number of undo levels
74set undolevels=1000
75
76" Set undo directory
77if has("persistent_undo")
78 silent !mkdir ~/.vim/undodir > /dev/null 2>&1
79 set undodir=~/.vim/undodir
80endif
81
82" Set search highlighting
83set hlsearch
84
85" Enable visual bell
86set visualbell
87
88" Set default file encoding
89set encoding=utf-8
90
91" Set the leader key to space
92let mapleader = " "
93
94" Map <Leader>w to save the file
95nnoremap <Leader>w :w<CR>
96
97" Map <Leader>q to quit
98nnoremap <Leader>q :q<CR>
99
100" Map <Leader>x to save and quit
101nnoremap <Leader>x :wq<CR>
102
103" Enable folding
104set foldmethod=syntax
105set foldlevelstart=99
106
107" Enable line wrapping at 80 characters
108set textwidth=80
109set colorcolumn=80
110
111" Add some basic key mappings
112" Map jj to escape insert mode
113inoremap jj <Esc>
114
115" Map <Leader>n to toggle line numbers
116nnoremap <Leader>n :set number!<CR>
117
118" Map <Leader>r to toggle relative line numbers
119nnoremap <Leader>r :set relativenumber!<CR>
120
121" Configure plugins (if you use a plugin manager like vim-plug)
122" Example with vim-plug:
123" call plug#begin('~/.vim/plugged')
124" Plug 'tpope/vim-sensible'
125" Plug 'preservim/nerdtree'
126" Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
127" Plug 'airblade/vim-gitgutter'
128" call plug#end()
129
130" NERDTree key mappings
131" nnoremap <C-n> :NERDTreeToggle<CR>
132
133" Enable automatic hard wrapping at textwidth (80 chars)
134set formatoptions+=t " Auto-wrap text using textwidth
135set formatoptions+=c " Auto-wrap comments using textwidth
136set formatoptions+=r " Continue comments when pressing Enter
137set formatoptions+=o " Continue comments when using 'o' or 'O'
138set formatoptions+=q " Allow formatting of comments with 'gq'
139set formatoptions+=n " Recognize numbered lists
140set formatoptions+=l " Don't break lines that were already long
141
142" Enable syntax highlighting
143syntax on
144
145" Increase memory limit for complex syntax parsing (Prevents E363)
146set maxmempattern=20000
zsh_macos.sh Bruto
1#!/bin/bash
2set -euo pipefail
3
4# =============================
5# COLORS & LOGGING
6# =============================
7RED='\033[0;31m'
8GREEN='\033[0;32m'
9YELLOW='\033[1;33m'
10BLUE='\033[0;34m'
11NC='\033[0m'
12
13log() { echo -e "${BLUE}[INFO]${NC} $*"; }
14ok() { echo -e "${GREEN}[OK]${NC} $*"; }
15warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
16err() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
17
18# =============================
19# CONFIG
20# =============================
21GIST_RAW_BASE="https://opengist.resetrix.work/weehong/f0d940c3c1214bf5b7996195199fdc09/raw/HEAD"
22
23CONFIG_FILES=(
24 ".alias"
25 ".func"
26 ".pathrc"
27 ".sourcerc"
28 ".vimrc"
29 ".zshrc"
30 ".config/starship.toml"
31)
32
33# =============================
34# REQUIREMENTS
35# =============================
36check_requirements() {
37 if [[ $EUID -eq 0 ]]; then
38 err "Do not run as root on macOS"
39 exit 1
40 fi
41}
42
43# =============================
44# INSTALLATION FUNCTIONS
45# =============================
46install_oh_my_zsh() {
47 log "Installing Oh My Zsh and plugins..."
48 export RUNZSH=no
49 export CHSH=no
50 export KEEP_ZSHRC=yes
51
52 if [[ ! -d "$HOME/.oh-my-zsh" ]]; then
53 sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" --unattended
54 else
55 ok "Oh My Zsh already installed"
56 fi
57
58 local custom="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}"
59 local plugin_dir="$custom/plugins"
60 mkdir -p "$plugin_dir"
61
62 [[ -d "$plugin_dir/zsh-autosuggestions" ]] || \
63 git clone https://github.com/zsh-users/zsh-autosuggestions "$plugin_dir/zsh-autosuggestions"
64
65 [[ -d "$plugin_dir/zsh-syntax-highlighting" ]] || \
66 git clone https://github.com/zsh-users/zsh-syntax-highlighting "$plugin_dir/zsh-syntax-highlighting"
67
68 ok "Oh My Zsh plugins installed to $plugin_dir"
69}
70
71install_starship() {
72 if command -v starship >/dev/null 2>&1 || [[ -f "$HOME/.local/bin/starship" ]]; then
73 ok "Starship already installed"
74 elif command -v brew >/dev/null 2>&1; then
75 log "Installing Starship with Homebrew..."
76 brew install starship
77 ok "Starship installed"
78 else
79 log "Installing Starship..."
80 mkdir -p "$HOME/.local/bin"
81 curl -fsSL https://starship.rs/install.sh | sh -s -- -y -b "$HOME/.local/bin"
82 ok "Starship installed"
83 fi
84}
85
86download_configs() {
87 log "Downloading custom config files..."
88 local backup="$HOME/.config_backup_$(date +%Y%m%d_%H%M%S)"
89 mkdir -p "$backup"
90
91 for f in "${CONFIG_FILES[@]}"; do
92 # Strip the leading dot for the download URL
93 local remote_file="${f#.}"
94 [[ "$f" == ".config/starship.toml" ]] && remote_file="starship.toml"
95 local url="$GIST_RAW_BASE/$remote_file"
96 local target="$HOME/$f"
97 local tmp="${target}.tmp.$$"
98
99 mkdir -p "$(dirname "$target")"
100
101 if [[ -f "$target" ]]; then
102 cp "$target" "$backup/"
103 fi
104
105 log "Fetching $remote_file -> $f ..."
106 if curl -fsSL "$url" -o "$tmp"; then
107 mv "$tmp" "$target"
108 else
109 rm -f "$tmp"
110 warn "Failed to download $remote_file"
111 fi
112 done
113
114 local skill_target="$HOME/.claude/skills/plan-build/SKILL.md"
115 local skill_tmp="${skill_target}.tmp.$$"
116 mkdir -p "$(dirname "$skill_target")"
117 log "Fetching orchestrate-loop.md -> .claude/skills/plan-build/SKILL.md ..."
118 if curl -fsSL "$GIST_RAW_BASE/orchestrate-loop.md" -o "$skill_tmp"; then
119 mv "$skill_tmp" "$skill_target"
120 else
121 rm -f "$skill_tmp"
122 warn "Failed to download orchestrate-loop.md"
123 fi
124
125 ok "Configs downloaded (Backup at $backup)"
126}
127
128configure_zshrc() {
129 local zshrc="$HOME/.zshrc"
130 log "Configuring .zshrc for Oh My Zsh and Starship..."
131
132 touch "$zshrc"
133 cp "$zshrc" "$HOME/.zshrc.backup_$(date +%Y%m%d_%H%M%S)"
134
135 cat > "$zshrc" <<'EOF'
136# =============================================================================
137# 1. HELPER FUNCTIONS & PATH
138# =============================================================================
139source_if_readable() {
140 local file="$1"
141 if [[ -f "$file" && -r "$file" ]]; then
142 source "$file"
143 fi
144}
145
146export PATH="$HOME/.local/bin:$PATH"
147
148# =============================================================================
149# 2. OH MY ZSH FRAMEWORK
150# =============================================================================
151export ZSH="${ZSH:-$HOME/.oh-my-zsh}"
152ZSH_THEME=""
153
154plugins=(
155 git
156 zsh-autosuggestions
157 zsh-syntax-highlighting
158)
159
160source_if_readable "$ZSH/oh-my-zsh.sh"
161
162# =============================================================================
163# 3. THIRD-PARTY INITIALIZATION & CUSTOM CONFIGS
164# =============================================================================
165source_if_readable "$HOME/.sourcerc"
166source_if_readable "$HOME/.func"
167source_if_readable "$HOME/.pathrc"
168source_if_readable "$HOME/.alias"
169
170# =============================================================================
171# 4. STARSHIP PROMPT
172# =============================================================================
173if [[ -z "${STARSHIP_CONFIG:-}" && -f "$HOME/.config/starship.toml" ]]; then
174 export STARSHIP_CONFIG="$HOME/.config/starship.toml"
175fi
176
177if command -v starship >/dev/null 2>&1; then
178 eval "$(starship init zsh)"
179elif [[ -x "$HOME/.local/bin/starship" ]]; then
180 eval "$("$HOME/.local/bin/starship" init zsh)"
181fi
182EOF
183
184 ok ".zshrc configured for Oh My Zsh framework with Starship prompt"
185}
186
187switch_shell() {
188 log "Starting Zsh session..."
189 echo -e "${YELLOW}Type 'exit' to return to this installer menu.${NC}"
190 echo "----------------------------------------"
191 zsh -l
192 echo "----------------------------------------"
193 ok "Returned from Zsh session"
194}
195
196# =============================
197# INTERACTIVE MENU
198# =============================
199show_menu() {
200 echo "==========================================="
201 echo "macOS Minimal Zsh Setup - Choose what to do"
202 echo "==========================================="
203 echo " 0) Run ALL steps (1-5)"
204 echo " 1) Install Oh My Zsh + plugins"
205 echo " 2) Install Starship prompt"
206 echo " 3) Download custom configs (~/.alias, .func, .vimrc, etc.)"
207 echo " 4) Configure ~/.zshrc (Oh My Zsh + Starship)"
208 echo " 5) Switch to Zsh (Temporary Sub-shell)"
209 echo " 6) Quit"
210 echo "==========================================="
211}
212
213run_choices() {
214 local input
215 read -p "Select: " input
216 input="${input//,/ }"
217
218 local -a to_run=()
219 local -a to_exclude=()
220
221 for item in $input; do
222 if [[ "$item" == !* ]]; then
223 to_exclude+=("${item:1}")
224 elif [[ "$item" == "0" ]]; then
225 to_run+=(1 2 3 4 5)
226 else
227 to_run+=("$item")
228 fi
229 done
230
231 if [[ ${#to_run[@]} -gt 0 ]]; then
232 for choice in "${to_run[@]}"; do
233 local skip=false
234
235 if [[ ${#to_exclude[@]} -gt 0 ]]; then
236 for ex in "${to_exclude[@]}"; do
237 if [[ "$choice" == "$ex" ]]; then
238 skip=true
239 break
240 fi
241 done
242 fi
243
244 $skip && continue
245
246 case "$choice" in
247 1) install_oh_my_zsh ;;
248 2) install_starship ;;
249 3) download_configs ;;
250 4) configure_zshrc ;;
251 5) switch_shell ;;
252 6) exit 0 ;;
253 *) warn "Skipping invalid option: $choice" ;;
254 esac
255 echo
256 done
257 fi
258}
259
260# =============================
261# MAIN
262# =============================
263main() {
264 check_requirements
265 while true; do
266 show_menu
267 run_choices
268 read -p "Do you want to run more options? (y/n): " again
269 [[ "$again" =~ ^[Yy]$ ]] || break
270 done
271 ok "macOS minimal Zsh configuration complete!"
272}
273
274main "$@"
275
zsh_ubuntu.sh Bruto
1#!/bin/bash
2set -euo pipefail
3
4# =============================
5# COLORS & LOGGING
6# =============================
7RED='\033[0;31m'
8GREEN='\033[0;32m'
9YELLOW='\033[1;33m'
10BLUE='\033[0;34m'
11NC='\033[0m'
12
13log() { echo -e "${BLUE}[INFO]${NC} $*"; }
14ok() { echo -e "${GREEN}[OK]${NC} $*"; }
15warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
16err() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
17
18# =============================
19# FLAGS & CONFIG
20# =============================
21SKIP_PACKAGES=false
22SKIP_SHELL_CHANGE=false
23INSTALL_HOMEBREW=false
24
25GIST_RAW_BASE="https://opengist.resetrix.work/weehong/f0d940c3c1214bf5b7996195199fdc09/raw/HEAD"
26CONFIG_FILES=(
27 ".alias"
28 ".func"
29 ".pathrc"
30 ".sourcerc"
31 ".vimrc"
32 ".zshrc"
33 ".config/starship.toml"
34)
35
36# =============================
37# OS DETECTION
38# =============================
39detect_os() {
40 if [[ "$OSTYPE" == "darwin"* ]]; then
41 echo "macos"
42 elif [ -f /etc/os-release ]; then
43 . /etc/os-release
44 echo "$ID"
45 else
46 echo "unknown"
47 fi
48}
49
50# =============================
51# REQUIREMENTS
52# =============================
53check_requirements() {
54 if [[ $EUID -eq 0 ]]; then
55 err "Do not run as root. The script will request sudo when necessary."
56 exit 1
57 fi
58 if [[ "$(detect_os)" != "macos" ]] && ! command -v sudo >/dev/null 2>&1; then
59 err "sudo required on Linux"
60 exit 1
61 fi
62}
63
64# =============================
65# INSTALLATION FUNCTIONS
66# =============================
67update_system() {
68 $SKIP_PACKAGES && return
69 log "Updating system..."
70 local os=$(detect_os)
71
72 case "$os" in
73 macos)
74 brew update || warn "Homebrew update failed; continuing installer"
75 ;;
76 ubuntu|debian)
77 sudo apt-get update -y
78 if ! sudo apt-get upgrade -y; then
79 warn "System upgrade did not complete. This can happen when apt wants to downgrade a package."
80 warn "Continuing because the Zsh setup does not require OS package upgrades to finish."
81 fi
82 ;;
83 fedora)
84 sudo dnf upgrade -y || warn "System upgrade failed; continuing installer"
85 ;;
86 arch)
87 sudo pacman -Syu --noconfirm || warn "System upgrade failed; continuing installer"
88 ;;
89 *) warn "Auto-update not supported for OS: $os" ;;
90 esac
91 ok "System update step finished"
92}
93
94install_packages() {
95 $SKIP_PACKAGES && return
96 log "Installing core packages..."
97 local os=$(detect_os)
98
99 case "$os" in
100 macos) brew install zsh git vim curl wget unzip xz ;;
101 ubuntu|debian) sudo apt-get install -y zsh git vim curl wget unzip zip build-essential xz-utils ;;
102 fedora) sudo dnf install -y zsh git vim curl wget unzip zip @development-tools xz ;;
103 arch) sudo pacman -S --noconfirm zsh git vim curl wget unzip zip base-devel xz ;;
104 *) warn "Auto-install not supported for OS: $os. Please install zsh, git, vim, curl manually." ;;
105 esac
106 ok "Packages installed"
107}
108
109set_timezone() {
110 log "Setting timezone to Asia/Singapore..."
111 local os=$(detect_os)
112 if [[ "$os" == "macos" ]]; then
113 sudo systemsetup -settimezone Asia/Singapore >/dev/null
114 else
115 sudo timedatectl set-timezone Asia/Singapore
116 fi
117 ok "Timezone set to Asia/Singapore"
118}
119
120install_homebrew() {
121 ! $INSTALL_HOMEBREW && return
122 command -v brew >/dev/null 2>&1 && ok "Homebrew already installed" && return
123 log "Installing Homebrew..."
124 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
125 ok "Homebrew installed"
126}
127
128configure_shell() {
129 $SKIP_SHELL_CHANGE && return
130 log "Changing default shell to zsh..."
131 local zsh_path
132 zsh_path="$(command -v zsh)"
133
134 if ! grep -qx "$zsh_path" /etc/shells; then
135 echo "$zsh_path" | sudo tee -a /etc/shells >/dev/null
136 fi
137 chsh -s "$zsh_path"
138 ok "Shell changed (requires logout/login to take effect)"
139}
140
141install_oh_my_zsh() {
142 log "Installing Oh My Zsh and plugins..."
143 export RUNZSH=no
144 export CHSH=no
145 export KEEP_ZSHRC=yes
146
147 if [[ ! -d "$HOME/.oh-my-zsh" ]]; then
148 sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" --unattended
149 else
150 ok "Oh My Zsh already installed"
151 fi
152
153 local custom="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}"
154 local plugin_dir="$custom/plugins"
155 mkdir -p "$plugin_dir"
156
157 [[ -d "$plugin_dir/zsh-autosuggestions" ]] || git clone https://github.com/zsh-users/zsh-autosuggestions "$plugin_dir/zsh-autosuggestions"
158 [[ -d "$plugin_dir/zsh-syntax-highlighting" ]] || git clone https://github.com/zsh-users/zsh-syntax-highlighting "$plugin_dir/zsh-syntax-highlighting"
159
160 ok "Oh My Zsh plugins installed to $plugin_dir"
161}
162
163install_starship() {
164 if command -v starship >/dev/null 2>&1 || [[ -f "$HOME/.local/bin/starship" ]]; then
165 ok "Starship already installed"
166 else
167 log "Installing Starship prompt..."
168 if command -v brew >/dev/null 2>&1; then
169 brew install starship
170 else
171 mkdir -p "$HOME/.local/bin"
172 curl -fsSL https://starship.rs/install.sh | sh -s -- -y -b "$HOME/.local/bin"
173 fi
174 ok "Starship installed"
175 fi
176}
177
178download_configs() {
179 log "Downloading custom config files from OpenGist..."
180 local backup="$HOME/.config_backup_$(date +%Y%m%d_%H%M%S)"
181 mkdir -p "$backup"
182
183 for f in "${CONFIG_FILES[@]}"; do
184 local remote_file="${f#.}"
185 [[ "$f" == ".config/starship.toml" ]] && remote_file="starship.toml"
186 local url="$GIST_RAW_BASE/$remote_file"
187 local target="$HOME/$f"
188 local tmp="${target}.tmp.$$"
189
190 mkdir -p "$(dirname "$target")"
191
192 if [[ -f "$target" ]]; then
193 cp "$target" "$backup/"
194 fi
195
196 log "Fetching $remote_file -> $f ..."
197 if curl -fsSL "$url" -o "$tmp"; then
198 mv "$tmp" "$target"
199 else
200 rm -f "$tmp"
201 warn "Failed to download $remote_file"
202 fi
203 done
204
205 local skill_target="$HOME/.claude/skills/plan-build/SKILL.md"
206 local skill_tmp="${skill_target}.tmp.$$"
207 mkdir -p "$(dirname "$skill_target")"
208 log "Fetching orchestrate-loop.md -> .claude/skills/plan-build/SKILL.md ..."
209 if curl -fsSL "$GIST_RAW_BASE/orchestrate-loop.md" -o "$skill_tmp"; then
210 mv "$skill_tmp" "$skill_target"
211 else
212 rm -f "$skill_tmp"
213 warn "Failed to download orchestrate-loop.md"
214 fi
215
216 ok "Configs downloaded (Backup at $backup)"
217}
218
219configure_zshrc() {
220 local zshrc="$HOME/.zshrc"
221 log "Configuring .zshrc for Oh My Zsh and Starship..."
222
223 touch "$zshrc"
224 cp "$zshrc" "$HOME/.zshrc.backup_$(date +%Y%m%d_%H%M%S)"
225
226 cat > "$zshrc" <<'EOF'
227# =============================================================================
228# 1. HELPER FUNCTIONS & PATH
229# =============================================================================
230source_if_readable() {
231 local file="$1"
232 if [[ -f "$file" && -r "$file" ]]; then
233 source "$file"
234 fi
235}
236
237export PATH="$HOME/.local/bin:$PATH"
238
239# =============================================================================
240# 2. OH MY ZSH FRAMEWORK
241# =============================================================================
242export ZSH="${ZSH:-$HOME/.oh-my-zsh}"
243ZSH_THEME=""
244
245plugins=(
246 git
247 zsh-autosuggestions
248 zsh-syntax-highlighting
249)
250
251source_if_readable "$ZSH/oh-my-zsh.sh"
252
253# =============================================================================
254# 3. THIRD-PARTY INITIALIZATION & CUSTOM CONFIGS
255# =============================================================================
256source_if_readable "$HOME/.sourcerc"
257source_if_readable "$HOME/.func"
258source_if_readable "$HOME/.pathrc"
259source_if_readable "$HOME/.alias"
260
261# =============================================================================
262# 4. STARSHIP PROMPT
263# =============================================================================
264if [[ -z "${STARSHIP_CONFIG:-}" && -f "$HOME/.config/starship.toml" ]]; then
265 export STARSHIP_CONFIG="$HOME/.config/starship.toml"
266fi
267
268if command -v starship >/dev/null 2>&1; then
269 eval "$(starship init zsh)"
270elif [[ -x "$HOME/.local/bin/starship" ]]; then
271 eval "$("$HOME/.local/bin/starship" init zsh)"
272fi
273EOF
274
275 ok ".zshrc configured for Oh My Zsh framework with Starship prompt"
276}
277
278switch_shell() {
279 log "Starting Zsh session..."
280 echo -e "${YELLOW}Type 'exit' to return to this installer menu.${NC}"
281 echo "----------------------------------------"
282 zsh -l
283 echo "----------------------------------------"
284 ok "Returned from Zsh session"
285}
286
287# =============================
288# INTERACTIVE MENU
289# =============================
290show_menu() {
291 echo "==========================================="
292 echo "Minimal Zsh Installer - Choose what to do"
293 echo "==========================================="
294 echo " 0) Run ALL steps (1-10)"
295 echo " 1) Update system packages"
296 echo " 2) Install core packages (zsh, git, vim, etc.)"
297 echo " 3) Set Timezone (Asia/Singapore)"
298 echo " 4) Install Homebrew"
299 echo " 5) Configure shell (chsh - sets default shell)"
300 echo " 6) Install Oh My Zsh + plugins"
301 echo " 7) Install Starship prompt"
302 echo " 8) Download custom configs (from OpenGist)"
303 echo " 9) Configure ~/.zshrc (Oh My Zsh + Starship)"
304 echo "10) Switch to Zsh (Temporary Sub-shell)"
305 echo "11) Quit"
306 echo "==========================================="
307}
308
309run_choices() {
310 local input
311 read -p "Select: " input
312 input="${input//,/ }"
313
314 local -a to_run=()
315 local -a to_exclude=()
316
317 for item in $input; do
318 if [[ "$item" == !* ]]; then
319 to_exclude+=("${item:1}")
320 elif [[ "$item" == "0" ]]; then
321 to_run+=(1 2 3 4 5 6 7 8 9 10)
322 else
323 to_run+=("$item")
324 fi
325 done
326
327 for choice in "${to_run[@]}"; do
328 local skip=false
329
330 for ex in "${to_exclude[@]}"; do
331 if [[ "$choice" == "$ex" ]]; then
332 skip=true
333 break
334 fi
335 done
336
337 $skip && continue
338
339 case "$choice" in
340 1) update_system ;;
341 2) install_packages ;;
342 3) set_timezone ;;
343 4) install_homebrew ;;
344 5) configure_shell ;;
345 6) install_oh_my_zsh ;;
346 7) install_starship ;;
347 8) download_configs ;;
348 9) configure_zshrc ;;
349 10) switch_shell ;;
350 11) log "Exiting..."; exit 0 ;;
351 *) warn "Skipping invalid option: $choice" ;;
352 esac
353 echo
354 done
355}
356
357# =============================
358# MAIN
359# =============================
360main() {
361 check_requirements
362 while true; do
363 show_menu
364 run_choices
365 read -p "Do you want to run more options? (y/n): " again
366 [[ "$again" =~ ^[Yy]$ ]] || break
367 done
368 ok "Zsh installation/configuration complete!"
369}
370
371main "$@"
372
zsh_wsl.sh Bruto
1#!/bin/bash
2set -euo pipefail
3
4# =============================
5# COLORS & LOGGING
6# =============================
7RED='\033[0;31m'
8GREEN='\033[0;32m'
9YELLOW='\033[1;33m'
10BLUE='\033[0;34m'
11NC='\033[0m'
12
13log() { echo -e "${BLUE}[INFO]${NC} $*"; }
14ok() { echo -e "${GREEN}[OK]${NC} $*"; }
15warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
16err() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
17
18# =============================
19# FLAGS & CONFIG
20# =============================
21SKIP_PACKAGES=false
22SKIP_SHELL_CHANGE=false
23INSTALL_HOMEBREW=false
24
25GIST_RAW_BASE="https://opengist.resetrix.work/weehong/f0d940c3c1214bf5b7996195199fdc09/raw/HEAD"
26CONFIG_FILES=(
27 ".alias"
28 ".func"
29 ".pathrc"
30 ".sourcerc"
31 ".vimrc"
32 ".zshrc"
33 ".config/starship.toml"
34)
35
36# =============================
37# PLATFORM DETECTION
38# =============================
39detect_os() {
40 if [ -f /etc/os-release ]; then
41 . /etc/os-release
42 echo "$ID"
43 else
44 echo "unknown"
45 fi
46}
47
48is_wsl() {
49 grep -qi "microsoft" /proc/version 2>/dev/null
50}
51
52# =============================
53# REQUIREMENTS
54# =============================
55check_requirements() {
56 if [[ $EUID -eq 0 ]]; then
57 err "Do not run as root. The script will request sudo when necessary."
58 exit 1
59 fi
60 if ! command -v sudo >/dev/null 2>&1; then
61 err "sudo required"
62 exit 1
63 fi
64 if ! is_wsl; then
65 err "This installer is intended for WSL"
66 exit 1
67 fi
68}
69
70# =============================
71# INSTALLATION FUNCTIONS
72# =============================
73update_system() {
74 $SKIP_PACKAGES && return
75 log "Updating system..."
76 local os=$(detect_os)
77
78 case "$os" in
79 ubuntu|debian)
80 sudo apt-get update -y
81 if ! sudo apt-get upgrade -y; then
82 warn "System upgrade did not complete. This can happen when apt wants to downgrade a package."
83 warn "Continuing because the Zsh setup does not require OS package upgrades to finish."
84 fi
85 ;;
86 fedora)
87 sudo dnf upgrade -y || warn "System upgrade failed; continuing installer"
88 ;;
89 arch)
90 sudo pacman -Syu --noconfirm || warn "System upgrade failed; continuing installer"
91 ;;
92 *) warn "Auto-update not supported for OS: $os" ;;
93 esac
94 ok "System update step finished"
95}
96
97install_packages() {
98 $SKIP_PACKAGES && return
99 log "Installing core packages..."
100 local os=$(detect_os)
101
102 case "$os" in
103 ubuntu|debian) sudo apt-get install -y zsh git vim curl wget unzip zip build-essential xz-utils ;;
104 fedora) sudo dnf install -y zsh git vim curl wget unzip zip @development-tools xz ;;
105 arch) sudo pacman -S --noconfirm zsh git vim curl wget unzip zip base-devel xz ;;
106 *) warn "Auto-install not supported for OS: $os. Please install zsh, git, vim, curl manually." ;;
107 esac
108 ok "Packages installed"
109}
110
111set_timezone() {
112 log "Checking timezone configuration..."
113 if command -v timedatectl >/dev/null 2>&1 && timedatectl status >/dev/null 2>&1; then
114 sudo timedatectl set-timezone Asia/Singapore
115 ok "Timezone set to Asia/Singapore"
116 return
117 fi
118
119 warn "Skipping timezone change: timedatectl is not available in this WSL environment"
120}
121
122install_homebrew() {
123 ! $INSTALL_HOMEBREW && return
124 command -v brew >/dev/null 2>&1 && ok "Homebrew already installed" && return
125 log "Installing Homebrew..."
126 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
127 ok "Homebrew installed"
128}
129
130configure_shell() {
131 $SKIP_SHELL_CHANGE && return
132 log "Changing default shell to zsh..."
133 local zsh_path
134 zsh_path="$(command -v zsh)"
135
136 if ! grep -qx "$zsh_path" /etc/shells; then
137 echo "$zsh_path" | sudo tee -a /etc/shells >/dev/null
138 fi
139 chsh -s "$zsh_path"
140 ok "Shell changed (open a new WSL session for it to take effect)"
141}
142
143install_oh_my_zsh() {
144 log "Installing Oh My Zsh and plugins..."
145 export RUNZSH=no
146 export CHSH=no
147 export KEEP_ZSHRC=yes
148
149 if [[ ! -d "$HOME/.oh-my-zsh" ]]; then
150 sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" --unattended
151 else
152 ok "Oh My Zsh already installed"
153 fi
154
155 local custom="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}"
156 local plugin_dir="$custom/plugins"
157 mkdir -p "$plugin_dir"
158
159 [[ -d "$plugin_dir/zsh-autosuggestions" ]] || git clone https://github.com/zsh-users/zsh-autosuggestions "$plugin_dir/zsh-autosuggestions"
160 [[ -d "$plugin_dir/zsh-syntax-highlighting" ]] || git clone https://github.com/zsh-users/zsh-syntax-highlighting "$plugin_dir/zsh-syntax-highlighting"
161
162 ok "Oh My Zsh plugins installed to $plugin_dir"
163}
164
165install_starship() {
166 if command -v starship >/dev/null 2>&1 || [[ -f "$HOME/.local/bin/starship" ]]; then
167 ok "Starship already installed"
168 else
169 log "Installing Starship prompt..."
170 if command -v brew >/dev/null 2>&1; then
171 brew install starship
172 else
173 mkdir -p "$HOME/.local/bin"
174 curl -fsSL https://starship.rs/install.sh | sh -s -- -y -b "$HOME/.local/bin"
175 fi
176 ok "Starship installed"
177 fi
178}
179
180download_configs() {
181 log "Downloading custom config files from OpenGist..."
182 local backup="$HOME/.config_backup_$(date +%Y%m%d_%H%M%S)"
183 mkdir -p "$backup"
184
185 for f in "${CONFIG_FILES[@]}"; do
186 local remote_file="${f#.}"
187 [[ "$f" == ".config/starship.toml" ]] && remote_file="starship.toml"
188 local url="$GIST_RAW_BASE/$remote_file"
189 local target="$HOME/$f"
190 local tmp="${target}.tmp.$$"
191
192 mkdir -p "$(dirname "$target")"
193
194 if [[ -f "$target" ]]; then
195 cp "$target" "$backup/"
196 fi
197
198 log "Fetching $remote_file -> $f ..."
199 if curl -fsSL "$url" -o "$tmp"; then
200 mv "$tmp" "$target"
201 else
202 rm -f "$tmp"
203 warn "Failed to download $remote_file"
204 fi
205 done
206
207 local skill_target="$HOME/.claude/skills/plan-build/SKILL.md"
208 local skill_tmp="${skill_target}.tmp.$$"
209 mkdir -p "$(dirname "$skill_target")"
210 log "Fetching orchestrate-loop.md -> .claude/skills/plan-build/SKILL.md ..."
211 if curl -fsSL "$GIST_RAW_BASE/orchestrate-loop.md" -o "$skill_tmp"; then
212 mv "$skill_tmp" "$skill_target"
213 else
214 rm -f "$skill_tmp"
215 warn "Failed to download orchestrate-loop.md"
216 fi
217
218 ok "Configs downloaded (Backup at $backup)"
219}
220
221configure_zshrc() {
222 local zshrc="$HOME/.zshrc"
223 log "Configuring .zshrc for Oh My Zsh and Starship..."
224
225 touch "$zshrc"
226 cp "$zshrc" "$HOME/.zshrc.backup_$(date +%Y%m%d_%H%M%S)"
227
228 cat > "$zshrc" <<'EOF'
229# =============================================================================
230# 1. HELPER FUNCTIONS & PATH
231# =============================================================================
232source_if_readable() {
233 local file="$1"
234 if [[ -f "$file" && -r "$file" ]]; then
235 source "$file"
236 fi
237}
238
239export PATH="$HOME/.local/bin:$PATH"
240
241# =============================================================================
242# 2. OH MY ZSH FRAMEWORK
243# =============================================================================
244export ZSH="${ZSH:-$HOME/.oh-my-zsh}"
245ZSH_THEME=""
246
247plugins=(
248 git
249 zsh-autosuggestions
250 zsh-syntax-highlighting
251)
252
253source_if_readable "$ZSH/oh-my-zsh.sh"
254
255# =============================================================================
256# 3. THIRD-PARTY INITIALIZATION & CUSTOM CONFIGS
257# =============================================================================
258source_if_readable "$HOME/.sourcerc"
259source_if_readable "$HOME/.func"
260source_if_readable "$HOME/.pathrc"
261source_if_readable "$HOME/.alias"
262
263# =============================================================================
264# 4. STARSHIP PROMPT
265# =============================================================================
266if [[ -z "${STARSHIP_CONFIG:-}" && -f "$HOME/.config/starship.toml" ]]; then
267 export STARSHIP_CONFIG="$HOME/.config/starship.toml"
268fi
269
270if command -v starship >/dev/null 2>&1; then
271 eval "$(starship init zsh)"
272elif [[ -x "$HOME/.local/bin/starship" ]]; then
273 eval "$("$HOME/.local/bin/starship" init zsh)"
274fi
275EOF
276
277 ok ".zshrc configured for Oh My Zsh framework with Starship prompt"
278}
279
280switch_shell() {
281 log "Starting Zsh session..."
282 echo -e "${YELLOW}Type 'exit' to return to this installer menu.${NC}"
283 echo "----------------------------------------"
284 zsh -l
285 echo "----------------------------------------"
286 ok "Returned from Zsh session"
287}
288
289# =============================
290# INTERACTIVE MENU
291# =============================
292show_menu() {
293 echo "==========================================="
294 echo "WSL Minimal Zsh Installer - Choose what to do"
295 echo "==========================================="
296 echo " 0) Run ALL steps (1-10)"
297 echo " 1) Update system packages"
298 echo " 2) Install core packages (zsh, git, vim, etc.)"
299 echo " 3) Set Timezone (best effort)"
300 echo " 4) Install Homebrew"
301 echo " 5) Configure shell (chsh - sets default shell)"
302 echo " 6) Install Oh My Zsh + plugins"
303 echo " 7) Install Starship prompt"
304 echo " 8) Download custom configs (from OpenGist)"
305 echo " 9) Configure ~/.zshrc (Oh My Zsh + Starship)"
306 echo "10) Switch to Zsh (Temporary Sub-shell)"
307 echo "11) Quit"
308 echo "==========================================="
309}
310
311run_choices() {
312 local input
313 read -p "Select: " input
314 input="${input//,/ }"
315
316 local -a to_run=()
317 local -a to_exclude=()
318
319 for item in $input; do
320 if [[ "$item" == !* ]]; then
321 to_exclude+=("${item:1}")
322 elif [[ "$item" == "0" ]]; then
323 to_run+=(1 2 3 4 5 6 7 8 9 10)
324 else
325 to_run+=("$item")
326 fi
327 done
328
329 for choice in "${to_run[@]}"; do
330 local skip=false
331
332 for ex in "${to_exclude[@]}"; do
333 if [[ "$choice" == "$ex" ]]; then
334 skip=true
335 break
336 fi
337 done
338
339 $skip && continue
340
341 case "$choice" in
342 1) update_system ;;
343 2) install_packages ;;
344 3) set_timezone ;;
345 4) install_homebrew ;;
346 5) configure_shell ;;
347 6) install_oh_my_zsh ;;
348 7) install_starship ;;
349 8) download_configs ;;
350 9) configure_zshrc ;;
351 10) switch_shell ;;
352 11) log "Exiting..."; exit 0 ;;
353 *) warn "Skipping invalid option: $choice" ;;
354 esac
355 echo
356 done
357}
358
359# =============================
360# MAIN
361# =============================
362main() {
363 check_requirements
364 while true; do
365 show_menu
366 run_choices
367 read -p "Do you want to run more options? (y/n): " again
368 [[ "$again" =~ ^[Yy]$ ]] || break
369 done
370 ok "WSL Zsh installation/configuration complete!"
371}
372
373main "$@"
374
zshrc Bruto
1# =============================================================================
2# 1. HELPER FUNCTIONS & PATH
3# =============================================================================
4source_if_readable() {
5 local file="$1"
6 if [[ -f "$file" && -r "$file" ]]; then
7 source "$file"
8 fi
9}
10
11# Ensure local bin is in PATH early (catches manual Starship installations)
12export PATH="$HOME/.local/bin:$PATH"
13
14# =============================================================================
15# 2. OH MY ZSH FRAMEWORK
16# =============================================================================
17export ZSH="${ZSH:-$HOME/.oh-my-zsh}"
18ZSH_THEME=""
19
20plugins=(
21 git
22 zsh-autosuggestions
23 zsh-syntax-highlighting
24)
25
26source_if_readable "$ZSH/oh-my-zsh.sh"
27
28# =============================================================================
29# 3. THIRD-PARTY INITIALIZATION & CUSTOM CONFIGS
30# =============================================================================
31# Load external initializers (SDKMAN, NVM, etc.)
32source_if_readable "$HOME/.sourcerc"
33
34# Custom Functions (must load before Path Management)
35source_if_readable "$HOME/.func"
36
37# Path Management (Relies on append_path from .func)
38source_if_readable "$HOME/.pathrc"
39
40# Aliases (Loaded late so they override framework/system defaults)
41source_if_readable "$HOME/.alias"
42
43# =============================================================================
44# 4. STARSHIP PROMPT
45# =============================================================================
46if [[ -z "${STARSHIP_CONFIG:-}" && -f "$HOME/.config/starship.toml" ]]; then
47 export STARSHIP_CONFIG="$HOME/.config/starship.toml"
48fi
49
50# Starship owns the prompt. It must initialize after Oh My Zsh.
51if command -v starship >/dev/null 2>&1; then
52 eval "$(starship init zsh)"
53elif [[ -x "$HOME/.local/bin/starship" ]]; then
54 eval "$("$HOME/.local/bin/starship" init zsh)"
55fi
56