diff --git a/config/claude/keybindings.json b/config/claude/keybindings.json new file mode 100644 index 0000000..ad474d5 --- /dev/null +++ b/config/claude/keybindings.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://www.schemastore.org/claude-code-keybindings.json", + "$docs": "https://code.claude.com/docs/en/keybindings", + "bindings": [ + { + "context": "Chat", + "bindings": { + "shift+enter": "chat:newline" + } + } + ] +} diff --git a/config/claude/settings.json b/config/claude/settings.json deleted file mode 100644 index b492a60..0000000 --- a/config/claude/settings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/claude-code-settings.json", - "env": { - "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1" - }, - "permissions": { - "defaultMode": "bypassPermissions" - }, - "hooks": { - "PreToolUse": [ - { - "matcher": "Read", - "hooks": [ - { - "type": "command", - "command": "if [[ $(jq -r .tool_input.file_path 2>/dev/null) == */Users/rathi/Documents/GitHub/claude-code-docs/* ]]; then LAST_PULL=\"/Users/rathi/Documents/GitHub/claude-code-docs/.last_pull\" && NOW=$(date +%s) && GITHUB_TS=$(jq -r .last_updated \"/Users/rathi/Documents/GitHub/claude-code-docs/docs/docs_manifest.json\" 2>/dev/null | cut -d. -f1) && GITHUB_UNIX=$(date -j -u -f \"%Y-%m-%dT%H:%M:%S\" \"$GITHUB_TS\" \"+%s\" 2>/dev/null || echo 0) && if [[ -f \"$LAST_PULL\" ]]; then LAST=$(cat \"$LAST_PULL\"); if [[ $GITHUB_UNIX -gt $LAST ]]; then echo \"🔄 Updating docs to latest version...\" >&2 && (cd /Users/rathi/Documents/GitHub/claude-code-docs && git pull --quiet) && echo $NOW > \"$LAST_PULL\"; fi; else echo \"🔄 Syncing docs for the first time...\" >&2 && (cd /Users/rathi/Documents/GitHub/claude-code-docs && git pull --quiet) && echo $NOW > \"$LAST_PULL\"; fi; fi" - } - ] - } - ] - }, - "statusLine": { - "type": "command", - "command": "/Users/rathi/.claude/statusline.sh" - }, - "voiceEnabled": true -} diff --git a/config/claude/statusline.sh b/config/claude/statusline.sh index 069011c..07c243e 100755 --- a/config/claude/statusline.sh +++ b/config/claude/statusline.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Full-featured status line with context window usage # Usage: Copy to ~/.claude/statusline.sh and make executable # diff --git a/config/claude/sync-docs.sh b/config/claude/sync-docs.sh new file mode 100644 index 0000000..65c550e --- /dev/null +++ b/config/claude/sync-docs.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -euo pipefail + +docs_dir="${HOME}/Documents/GitHub/claude-code-docs" +manifest="${docs_dir}/docs/docs_manifest.json" +last_pull="${docs_dir}/.last_pull" + +file_path="$(jq -r '.tool_input.file_path // empty' 2>/dev/null || true)" +case "${file_path}" in + "${docs_dir}"/*) ;; + *) exit 0 ;; +esac + +if [[ ! -d "${docs_dir}/.git" || ! -f "${manifest}" ]]; then + exit 0 +fi + +github_unix="$( + python3 - "${manifest}" <<'PY' +import datetime +import json +import sys + +manifest_path = sys.argv[1] + +try: + with open(manifest_path, encoding="utf-8") as handle: + last_updated = json.load(handle).get("last_updated", "") + last_updated = last_updated.split(".", 1)[0] + parsed = datetime.datetime.strptime(last_updated, "%Y-%m-%dT%H:%M:%S") + print(int(parsed.replace(tzinfo=datetime.timezone.utc).timestamp())) +except Exception: + print(0) +PY +)" + +last_synced=0 +if [[ -f "${last_pull}" ]]; then + last_synced="$(cat "${last_pull}" 2>/dev/null || echo 0)" +fi + +if [[ "${github_unix}" -le "${last_synced}" ]]; then + exit 0 +fi + +printf 'Syncing Claude docs to latest version...\n' >&2 +git -C "${docs_dir}" pull --quiet +date +%s > "${last_pull}" diff --git a/home/claude.nix b/home/claude.nix index 2a60dd5..86dcf8c 100644 --- a/home/claude.nix +++ b/home/claude.nix @@ -1,24 +1,60 @@ { + config, inputs, pkgs, ... }: let claudePackage = inputs.claudeCode.packages.${pkgs.stdenv.hostPlatform.system}.default; + jsonFormat = pkgs.formats.json { }; + claudeSettings = jsonFormat.generate "claude-settings.json" { + "$schema" = "https://json.schemastore.org/claude-code-settings.json"; + env = { + CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = "1"; + }; + permissions = { + defaultMode = "bypassPermissions"; + }; + hooks = { + PreToolUse = [ + { + matcher = "Read"; + hooks = [ + { + type = "command"; + command = "${config.home.homeDirectory}/.claude/sync-docs.sh"; + } + ]; + } + ]; + }; + statusLine = { + type = "command"; + command = "${config.home.homeDirectory}/.claude/statusline.sh"; + }; + voiceEnabled = true; + }; in { home.file.".local/bin/claude".source = "${claudePackage}/bin/claude"; - # Claude Code still resolves user settings from ~/.claude rather than XDG. + # Claude Code stores shared config, commands, plugins, and skills in ~/.claude. + # Global UI settings changed through /config still live in ~/.claude.json and are + # intentionally left user-managed because Claude mutates that file directly. home.file.".claude/CLAUDE.md".source = ../config/claude/CLAUDE.md; home.file.".claude/commands" = { source = ../config/claude/commands; recursive = true; }; - home.file.".claude/settings.json".source = ../config/claude/settings.json; + home.file.".claude/settings.json".source = claudeSettings; home.file.".claude/settings.local.json".source = ../config/claude/settings.local.json; + home.file.".claude/keybindings.json".source = ../config/claude/keybindings.json; home.file.".claude/statusline.sh" = { source = ../config/claude/statusline.sh; executable = true; }; + home.file.".claude/sync-docs.sh" = { + source = ../config/claude/sync-docs.sh; + executable = true; + }; } diff --git a/home/xdg.nix b/home/xdg.nix index 064ee57..5f94f09 100644 --- a/home/xdg.nix +++ b/home/xdg.nix @@ -40,9 +40,6 @@ in AWS_SHARED_CREDENTIALS_FILE = "${config.xdg.configHome}/aws/credentials"; AWS_CONFIG_FILE = "${config.xdg.configHome}/aws/config"; }) - (lib.mkIf (f.claude or false) { - CLAUDE_CONFIG_DIR = "${config.xdg.configHome}/claude"; - }) { PSQL_HISTORY = "${config.xdg.stateHome}/psql_history"; SQLITE_HISTORY = "${config.xdg.stateHome}/sqlite_history";