diff --git a/README.md b/README.md
index f568db0..2545a95 100644
--- a/README.md
+++ b/README.md
@@ -78,20 +78,20 @@ A good spec is short, concrete, and verifiable. Start from [`skills/devloop-spec
 
 ```sh
 devloop spec
-devloop spec --agent claude --output .specs/chat-retry.md notes.md
+devloop spec --agent claude notes.md
 ```
 
-By default, generated specs are written under `.specs/`, and the interactive spec picker searches `.specs/`. If your specs live somewhere else, open `devloop`, choose `Settings`, and add one extra spec path. Generated specs will be written there too.
+Devloop maintains a global config at `~/.devloop/config`. By default, generated specs are written under `~/Projects/specs/`, and the interactive spec picker searches that directory plus the current repo's `.specs/` fallback.
 
-The custom path is saved in `.devloop/config` and can be repo-relative or absolute:
+Change the shared spec directory from `devloop` > `Settings`, or edit `~/.devloop/config`:
 
 ```ini
 spec_dir=/Users/satya/Projects/specs
 ```
 
-The Settings menu expands `~/...` before saving. It also lets you change the per-run timeout. `.devloop/` is ignored by git because absolute paths are machine-local.
+Repo-local `.devloop/config` is still supported for explicit overrides. Prefer absolute paths there unless the override should be repo-relative.
 
-Runs time out after 30 minutes by default. Change that in `Settings`, in `.devloop/config`, or per command:
+Runs time out after 30 minutes by default. Change that in `Settings`, in `~/.devloop/config`, or per command:
 
 ```ini
 timeout_minutes=45
@@ -134,11 +134,11 @@ devloop [options] <spec.md> [max=5]
 
 When stdout is a terminal, running `devloop` without arguments opens a menu:
 
-- `Run a spec`: pick a spec from the configured spec path or `.specs/`.
+- `Run a spec`: pick a spec from the shared spec directory or `.specs/`.
 - `Create a spec`: choose the spec agent and provide source context.
 - `Continue a run`: pick a prior `.codex/tracks/*.md` and continue in that worktree.
 - `Open reports`: pick a prior report from any registered worktree.
-- `Settings`: view spec search paths, add or remove one custom spec path, and set the run timeout.
+- `Settings`: view or change the shared spec path and set the run timeout.
 - `Doctor`: verify required commands, optional UI tools, and installed skills.
 
 Nested menu screens keep `Back` as the final option so you can return to the previous menu without exiting Devloop.
diff --git a/devloop b/devloop
index 3c81367..0c8a7fe 100755
--- a/devloop
+++ b/devloop
@@ -6,6 +6,7 @@ CODEX_REASONING_ARGS=(-c 'model_reasoning_effort="xhigh"')
 CLAUDE_MODEL_ARGS=(--model claude-opus-4-8)
 CLAUDE_EFFORT_ARGS=(--effort max)
 DEFAULT_TIMEOUT_MINUTES=30
+DEFAULT_SPEC_DIR_SUFFIX="Projects/specs"
 
 SCRIPT_PATH="${BASH_SOURCE[0]}"
 while [ -L "$SCRIPT_PATH" ]; do
@@ -286,7 +287,7 @@ devloop_global_config_file() {
 }
 
 devloop_config_file() {
-  devloop_local_config_file
+  devloop_global_config_file || devloop_local_config_file
 }
 
 devloop_config_file_for_scope() {
@@ -323,9 +324,43 @@ config_file_value() {
   done < "$file" | tail -n 1
 }
 
+devloop_default_spec_dir() {
+  if [ -n "${HOME:-}" ]; then
+    printf '%s/%s\n' "${HOME%/}" "$DEFAULT_SPEC_DIR_SUFFIX"
+  else
+    printf '%s\n' ".specs"
+  fi
+}
+
+ensure_global_config() {
+  local file value spec_dir
+  file="$(devloop_global_config_file 2>/dev/null)" || return 0
+  mkdir -p "$(dirname "$file")" || return 1
+  if [ ! -f "$file" ]; then
+    spec_dir="$(devloop_default_spec_dir)"
+    mkdir -p "$spec_dir" || return 1
+    {
+      printf 'spec_dir=%s\n' "$spec_dir"
+      printf 'timeout_minutes=%s\n' "$DEFAULT_TIMEOUT_MINUTES"
+    } > "$file"
+    return 0
+  fi
+  value="$(config_file_value spec_dir "$file" || true)"
+  if [ -z "$value" ]; then
+    spec_dir="$(devloop_default_spec_dir)"
+    mkdir -p "$spec_dir" || return 1
+    printf 'spec_dir=%s\n' "$spec_dir" >> "$file"
+  fi
+  value="$(config_file_value timeout_minutes "$file" || true)"
+  if [ -z "$value" ]; then
+    printf 'timeout_minutes=%s\n' "$DEFAULT_TIMEOUT_MINUTES" >> "$file"
+  fi
+}
+
 devloop_config_value() {
   local key="$1"
   local file value
+  ensure_global_config >/dev/null 2>&1 || true
   file="$(devloop_local_config_file)"
   if value="$(config_file_value "$key" "$file")" && [ -n "$value" ]; then
     printf '%s\n' "$value"
@@ -406,46 +441,53 @@ expand_spec_dir_input() {
 }
 
 devloop_spec_dir() {
-  local value normalized
-  value="$(devloop_config_value spec_dir || true)"
-  if normalized="$(normalize_spec_dir "$value")"; then
-    printf '%s\n' "$normalized"
-  else
-    printf '%s\n' ".specs"
-  fi
+  local scope file value normalized
+  ensure_global_config >/dev/null 2>&1 || true
+  for scope in local global; do
+    file="$(devloop_config_file_for_scope "$scope" 2>/dev/null)" || continue
+    value="$(config_file_value spec_dir "$file" || true)"
+    [ -n "$value" ] || continue
+    if normalized="$(normalize_spec_dir "$value")"; then
+      printf '%s\n' "$normalized"
+      return 0
+    fi
+  done
+  devloop_default_spec_dir
 }
 
 is_default_spec_dir() {
-  case "$1" in
-    ".specs") return 0 ;;
-    *) return 1 ;;
-  esac
+  local default_spec_dir
+  default_spec_dir="$(devloop_default_spec_dir)"
+  [ "$1" = "$default_spec_dir" ]
 }
 
 configured_spec_dir() {
-  local value normalized
-  value="$(devloop_config_value spec_dir || true)"
-  [ -n "$value" ] || return 1
-  normalized="$(normalize_spec_dir "$value")" || return 1
-  is_default_spec_dir "$normalized" && return 1
-  printf '%s\n' "$normalized"
+  local scope file value normalized
+  ensure_global_config >/dev/null 2>&1 || true
+  for scope in local global; do
+    file="$(devloop_config_file_for_scope "$scope" 2>/dev/null)" || continue
+    value="$(config_file_value spec_dir "$file" || true)"
+    [ -n "$value" ] || continue
+    normalized="$(normalize_spec_dir "$value")" || continue
+    is_default_spec_dir "$normalized" && return 1
+    printf '%s\n' "$normalized"
+    return 0
+  done
+  return 1
 }
 
 configured_spec_dir_scope() {
-  local file value normalized
-  file="$(devloop_local_config_file)"
-  if value="$(config_file_value spec_dir "$file")" && [ -n "$value" ]; then
-    normalized="$(normalize_spec_dir "$value")" || return 1
-    is_default_spec_dir "$normalized" && return 1
-    printf '%s\n' "local"
-    return 0
-  fi
-  if file="$(devloop_global_config_file 2>/dev/null)" && value="$(config_file_value spec_dir "$file")" && [ -n "$value" ]; then
-    normalized="$(normalize_spec_dir "$value")" || return 1
+  local scope file value normalized
+  ensure_global_config >/dev/null 2>&1 || true
+  for scope in local global; do
+    file="$(devloop_config_file_for_scope "$scope" 2>/dev/null)" || continue
+    value="$(config_file_value spec_dir "$file" || true)"
+    [ -n "$value" ] || continue
+    normalized="$(normalize_spec_dir "$value")" || continue
     is_default_spec_dir "$normalized" && return 1
-    printf '%s\n' "global"
+    printf '%s\n' "$scope"
     return 0
-  fi
+  done
   return 1
 }
 
@@ -493,7 +535,7 @@ spec_dir_status() {
 }
 
 write_config_spec_dir() {
-  local scope="local"
+  local scope="global"
   local dir
   local expanded normalized file
   if [ "$#" -eq 2 ]; then
@@ -510,6 +552,12 @@ write_config_spec_dir() {
     printf '%s\n' "spec directory must be absolute, or relative without '..'" >&2
     return 2
   }
+  if [ "$scope" = "global" ]; then
+    case "$normalized" in
+      /*) ;;
+      *) normalized="$(absolute_path "$normalized")" || return 1 ;;
+    esac
+  fi
   file="$(devloop_config_file_for_scope "$scope")" || return $?
   mkdir -p "$(dirname "$file")" "$normalized" || return 1
   write_config_value "$scope" spec_dir "$normalized" || return $?
@@ -517,7 +565,7 @@ write_config_spec_dir() {
 }
 
 remove_config_spec_dir() {
-  local scope="local"
+  local scope="global"
   if [ "$#" -eq 1 ]; then scope="$1"; fi
   remove_config_value "$scope" spec_dir
 }
@@ -531,44 +579,52 @@ normalize_timeout_minutes() {
 }
 
 devloop_timeout_minutes() {
-  local value normalized
-  value="$(devloop_config_value timeout_minutes || true)"
-  if normalized="$(normalize_timeout_minutes "$value")"; then
-    printf '%s\n' "$normalized"
-  else
-    printf '%s\n' "$DEFAULT_TIMEOUT_MINUTES"
-  fi
+  local scope file value normalized
+  ensure_global_config >/dev/null 2>&1 || true
+  for scope in local global; do
+    file="$(devloop_config_file_for_scope "$scope" 2>/dev/null)" || continue
+    value="$(config_file_value timeout_minutes "$file" || true)"
+    [ -n "$value" ] || continue
+    if normalized="$(normalize_timeout_minutes "$value")"; then
+      printf '%s\n' "$normalized"
+      return 0
+    fi
+  done
+  printf '%s\n' "$DEFAULT_TIMEOUT_MINUTES"
 }
 
 configured_timeout_minutes() {
-  local value normalized
-  value="$(devloop_config_value timeout_minutes || true)"
-  [ -n "$value" ] || return 1
-  normalized="$(normalize_timeout_minutes "$value")" || return 1
-  [ "$normalized" = "$DEFAULT_TIMEOUT_MINUTES" ] && return 1
-  printf '%s\n' "$normalized"
+  local scope file value normalized
+  ensure_global_config >/dev/null 2>&1 || true
+  for scope in local global; do
+    file="$(devloop_config_file_for_scope "$scope" 2>/dev/null)" || continue
+    value="$(config_file_value timeout_minutes "$file" || true)"
+    [ -n "$value" ] || continue
+    normalized="$(normalize_timeout_minutes "$value")" || continue
+    [ "$normalized" = "$DEFAULT_TIMEOUT_MINUTES" ] && return 1
+    printf '%s\n' "$normalized"
+    return 0
+  done
+  return 1
 }
 
 configured_timeout_minutes_scope() {
-  local file value normalized
-  file="$(devloop_local_config_file)"
-  if value="$(config_file_value timeout_minutes "$file")" && [ -n "$value" ]; then
-    normalized="$(normalize_timeout_minutes "$value")" || return 1
-    [ "$normalized" = "$DEFAULT_TIMEOUT_MINUTES" ] && return 1
-    printf '%s\n' "local"
-    return 0
-  fi
-  if file="$(devloop_global_config_file 2>/dev/null)" && value="$(config_file_value timeout_minutes "$file")" && [ -n "$value" ]; then
-    normalized="$(normalize_timeout_minutes "$value")" || return 1
+  local scope file value normalized
+  ensure_global_config >/dev/null 2>&1 || true
+  for scope in local global; do
+    file="$(devloop_config_file_for_scope "$scope" 2>/dev/null)" || continue
+    value="$(config_file_value timeout_minutes "$file" || true)"
+    [ -n "$value" ] || continue
+    normalized="$(normalize_timeout_minutes "$value")" || continue
     [ "$normalized" = "$DEFAULT_TIMEOUT_MINUTES" ] && return 1
-    printf '%s\n' "global"
+    printf '%s\n' "$scope"
     return 0
-  fi
+  done
   return 1
 }
 
 write_config_timeout_minutes() {
-  local scope="local"
+  local scope="global"
   local value normalized
   if [ "$#" -eq 2 ]; then
     scope="$1"
@@ -585,7 +641,7 @@ write_config_timeout_minutes() {
 }
 
 remove_config_timeout_minutes() {
-  local scope="local"
+  local scope="global"
   if [ "$#" -eq 1 ]; then scope="$1"; fi
   remove_config_value "$scope" timeout_minutes
 }
@@ -1036,21 +1092,24 @@ interactive_create_spec() {
 
 interactive_settings() {
   local choice custom_spec_dir custom_timeout scope saved value timeout_display
+  local default_spec_dir
   local choices=()
   while true; do
     custom_spec_dir="$(configured_spec_dir || true)"
     custom_timeout="$(configured_timeout_minutes || true)"
     timeout_display="$(devloop_timeout_minutes) minutes"
-    ui_header "Settings" "Spec paths and run timeout"
+    default_spec_dir="$(devloop_default_spec_dir)"
+    ui_header "Settings" "Global spec path and run timeout"
     if [ -n "$custom_spec_dir" ]; then
       ui_print_key_values \
-        "custom" "$custom_spec_dir" \
-        "default" ".specs" \
+        "configured" "$custom_spec_dir" \
+        "fallback" ".specs" \
         "timeout" "$timeout_display"
       choices=("Remove spec path" "Set timeout" "Back")
     else
       ui_print_key_values \
-        "default" ".specs" \
+        "default" "$default_spec_dir" \
+        "fallback" ".specs" \
         "timeout" "$timeout_display"
       choices=("Add spec path" "Set timeout" "Back")
     fi
@@ -1061,7 +1120,7 @@ interactive_settings() {
     case "$choice" in
       "Add spec path")
         value="$(ui_input "Spec path" "")" || return 130
-        if saved="$(write_config_spec_dir local "$value")"; then
+        if saved="$(write_config_spec_dir global "$value")"; then
           printf 'spec path saved: %s\n' "$saved" >&2
         else
           printf '%s\n' "spec path not saved" >&2
@@ -1076,7 +1135,7 @@ interactive_settings() {
         ;;
       "Set timeout")
         value="$(ui_input "Timeout minutes, 1-1440" "$(devloop_timeout_minutes)")" || return 130
-        if saved="$(write_config_timeout_minutes local "$value")"; then
+        if saved="$(write_config_timeout_minutes global "$value")"; then
           printf 'timeout saved: %s minutes\n' "$saved" >&2
         else
           printf '%s\n' "timeout not saved" >&2
diff --git a/tests/devloop_test.sh b/tests/devloop_test.sh
index b338134..e53324b 100755
--- a/tests/devloop_test.sh
+++ b/tests/devloop_test.sh
@@ -272,16 +272,23 @@ equals "$(extract_generated_spec "$spec_output")" $'---\nstatus: draft\n---\n\n#
 
 config_repo="$work/config-repo"
 config_home="$work/config-home"
+config_default_specs="$config_home/Projects/specs"
 mkdir -p "$config_repo/.specs" "$config_repo/.devloop/specs" "$config_home"
 config_repo_real="$(cd "$config_repo" && pwd)"
-equals "$(devloop_config_file)" ".devloop/config" "default config file"
+equals "$(HOME="$config_home" devloop_config_file)" "$config_home/.devloop/config" "default config file"
 printf '%s\n' "# Default" > "$config_repo/.specs/default.md"
 printf '%s\n' "# Devloop" > "$config_repo/.devloop/specs/devloop.md"
 config_specs="$(cd "$config_repo" && HOME="$config_home" list_spec_files)"
+[[ -f "$config_home/.devloop/config" ]] || fail "global config was not created"
+contains "$(cat "$config_home/.devloop/config")" "spec_dir=$config_default_specs" "global config default spec dir"
+contains "$(cat "$config_home/.devloop/config")" "timeout_minutes=30" "global config default timeout"
+[[ -d "$config_default_specs" ]] || fail "global default spec dir was not created"
 contains "$config_specs" ".specs/default.md" "default spec search"
 if printf '%s\n' "$config_specs" | grep -Fq ".devloop/specs/devloop.md"; then fail "default spec search included .devloop/specs"; fi
-equals "$(cd "$config_repo" && HOME="$config_home" spec_search_label)" ".specs" "spec search label"
-equals "$(cd "$config_repo" && HOME="$config_home" write_config_spec_dir "custom-specs")" "custom-specs" "write config spec dir"
+equals "$(cd "$config_repo" && HOME="$config_home" devloop_spec_dir)" "$config_default_specs" "default global spec dir"
+equals "$(cd "$config_repo" && HOME="$config_home" spec_search_label)" "$config_default_specs, .specs" "spec search label"
+if (cd "$config_repo" && HOME="$config_home" configured_spec_dir) >/dev/null 2>&1; then fail "default global spec dir reported as custom"; fi
+equals "$(cd "$config_repo" && HOME="$config_home" write_config_spec_dir local "custom-specs")" "custom-specs" "write local config spec dir"
 equals "$(cd "$config_repo" && HOME="$config_home" devloop_spec_dir)" "custom-specs" "configured spec dir"
 equals "$(cd "$config_repo" && HOME="$config_home" configured_spec_dir)" "custom-specs" "custom spec dir"
 equals "$(cd "$config_repo" && HOME="$config_home" configured_spec_dir_scope)" "local" "custom spec dir scope"
@@ -293,7 +300,7 @@ equals "$(cd "$config_repo" && HOME="$config_home" generated_spec_path "$spec_ou
 if (cd "$config_repo" && HOME="$config_home" write_config_spec_dir "../bad") >/dev/null 2>&1; then fail "write_config_spec_dir accepted path traversal"; fi
 
 absolute_specs="$work/shared-specs"
-equals "$(cd "$config_repo" && HOME="$config_home" write_config_spec_dir "$absolute_specs")" "$absolute_specs" "write absolute config spec dir"
+equals "$(cd "$config_repo" && HOME="$config_home" write_config_spec_dir local "$absolute_specs")" "$absolute_specs" "write absolute config spec dir"
 equals "$(cd "$config_repo" && HOME="$config_home" devloop_spec_dir)" "$absolute_specs" "absolute configured spec dir"
 equals "$(cd "$config_repo" && HOME="$config_home" configured_spec_dir)" "$absolute_specs" "absolute custom spec dir"
 [[ -d "$absolute_specs" ]] || fail "absolute configured spec dir was not created"
@@ -306,8 +313,8 @@ equals "$(spec_dir_status "$absolute_specs")" "exists" "spec dir status exists"
 equals "$(spec_dir_status "$work/missing-specs")" "missing" "spec dir status missing"
 (cd "$config_repo" && HOME="$config_home" remove_config_spec_dir local)
 if (cd "$config_repo" && HOME="$config_home" configured_spec_dir) >/dev/null 2>&1; then fail "custom spec dir was not removed"; fi
-equals "$(cd "$config_repo" && HOME="$config_home" devloop_spec_dir)" ".specs" "removed custom spec dir falls back"
-equals "$(cd "$config_repo" && HOME="$config_home" spec_search_label)" ".specs" "removed custom spec search label"
+equals "$(cd "$config_repo" && HOME="$config_home" devloop_spec_dir)" "$config_default_specs" "removed custom spec dir falls back"
+equals "$(cd "$config_repo" && HOME="$config_home" spec_search_label)" "$config_default_specs, .specs" "removed custom spec search label"
 
 global_repo="$work/global-repo"
 global_home="$work/global-home"
@@ -325,18 +332,31 @@ equals "$(cd "$global_repo" && HOME="$global_home" devloop_spec_dir)" "local-spe
 equals "$(cd "$global_repo" && HOME="$global_home" configured_spec_dir)" "local-specs" "local custom spec dir"
 equals "$(cd "$global_repo" && HOME="$global_home" configured_spec_dir_scope)" "local" "local custom spec dir scope"
 equals "$(cd "$global_repo" && HOME="$global_home" devloop_config_value coder)" "codex" "global config fills missing local key"
+printf '%s\n' "spec_dir=.specs" > "$global_repo/.devloop/config"
+equals "$(cd "$global_repo" && HOME="$global_home" devloop_spec_dir)" ".specs" "repo .specs overrides global spec dir"
+equals "$(cd "$global_repo" && HOME="$global_home" configured_spec_dir)" ".specs" "repo .specs reported as override"
+equals "$(cd "$global_repo" && HOME="$global_home" configured_spec_dir_scope)" "local" "repo .specs override scope"
+
+default_scope_repo="$work/default-scope-repo"
+default_scope_home="$work/default-scope-home"
+default_scope_specs="$work/default-scope-specs"
+mkdir -p "$default_scope_repo" "$default_scope_home"
+equals "$(cd "$default_scope_repo" && HOME="$default_scope_home" write_config_spec_dir "$default_scope_specs")" "$default_scope_specs" "default write config spec dir is global"
+equals "$(cat "$default_scope_home/.devloop/config")" "spec_dir=$default_scope_specs" "default write config path"
+equals "$(cd "$default_scope_repo" && HOME="$default_scope_home" configured_spec_dir_scope)" "global" "default write config scope"
 
 tilde_repo="$work/tilde-repo"
 tilde_home="$work/home"
 tilde_input="~"/shared-specs
 mkdir -p "$tilde_repo" "$tilde_home"
 equals "$(cd "$tilde_repo" && HOME="$tilde_home" write_config_spec_dir "$tilde_input")" "$tilde_home/shared-specs" "tilde input expands when saved"
-equals "$(cat "$tilde_repo/.devloop/config")" "spec_dir=$tilde_home/shared-specs" "tilde input saved as absolute path"
+equals "$(cat "$tilde_home/.devloop/config")" "spec_dir=$tilde_home/shared-specs" "tilde input saved as absolute path"
 
 raw_tilde_repo="$work/raw-tilde-repo"
-mkdir -p "$raw_tilde_repo/.devloop"
+raw_tilde_home="$work/raw-tilde-home"
+mkdir -p "$raw_tilde_repo/.devloop" "$raw_tilde_home"
 printf '%s\n' "spec_dir=~/raw-specs" > "$raw_tilde_repo/.devloop/config"
-equals "$(cd "$raw_tilde_repo" && devloop_spec_dir)" ".specs" "raw tilde config falls back"
+equals "$(cd "$raw_tilde_repo" && HOME="$raw_tilde_home" devloop_spec_dir)" "$raw_tilde_home/Projects/specs" "raw tilde config falls back"
 
 equals "$(normalize_timeout_minutes 1)" "1" "timeout lower bound"
 equals "$(normalize_timeout_minutes 30)" "30" "timeout normalize"
@@ -347,8 +367,8 @@ if normalize_timeout_minutes nope >/dev/null 2>&1; then fail "timeout accepted n
 equals "$(cd "$config_repo" && HOME="$config_home" devloop_timeout_minutes)" "30" "default timeout"
 equals "$(cd "$config_repo" && HOME="$config_home" write_config_timeout_minutes 45)" "45" "write timeout"
 equals "$(cd "$config_repo" && HOME="$config_home" devloop_timeout_minutes)" "45" "configured timeout"
-equals "$(cd "$config_repo" && HOME="$config_home" configured_timeout_minutes_scope)" "local" "configured timeout scope"
-(cd "$config_repo" && HOME="$config_home" remove_config_timeout_minutes local)
+equals "$(cd "$config_repo" && HOME="$config_home" configured_timeout_minutes_scope)" "global" "configured timeout scope"
+(cd "$config_repo" && HOME="$config_home" remove_config_timeout_minutes)
 equals "$(cd "$config_repo" && HOME="$config_home" devloop_timeout_minutes)" "30" "removed timeout falls back"
 
 lint_spec_text=$'---\ntype: feat\n---\n# Title\n\n## Acceptance criteria\n1. Thing'
@@ -508,13 +528,16 @@ AGENT
 chmod +x "$agent"
 
 repo="$work/repo"
-mkdir -p "$repo/.devloop"
+spec_home="$work/spec-home"
 repo_specs="$work/repo-specs"
-printf 'spec_dir=%s\n' "$repo_specs" > "$repo/.devloop/config"
+mkdir -p "$repo" "$spec_home/.devloop"
+printf 'spec_dir=%s\n' "$repo_specs" > "$spec_home/.devloop/config"
 old_use_tui="$USE_TUI"
 USE_TUI=false
 (
   cd "$repo"
+  HOME="$spec_home"
+  export HOME
   main spec --agent "$agent" "Keep devloop as Bash." >/tmp/devloop-spec-test.out
 )
 USE_TUI="$old_use_tui"
