llm stuff?

This commit is contained in:
Joe Frikker
2026-10-07 14:26:39 -04:00
parent 402002e6e6
commit 55c2311bd7
2 changed files with 265 additions and 169 deletions
Generated
+15 -15
View File
@@ -8,11 +8,11 @@
"systems": "systems"
},
"locked": {
"lastModified": 1790280108,
"narHash": "sha256-bApGLOF5XDhf3UCM1/e1yIQCoY5wW1+B88lHkcYlp2U=",
"lastModified": 1791071338,
"narHash": "sha256-YBAFoulgm9qEavWtmb2gHwk7lgpVZL7vtr4+yi6H3UU=",
"owner": "sadjow",
"repo": "claude-code-nix",
"rev": "f10421316cc6df045497f5aa0009adce648e89ee",
"rev": "b17e42820330bd34ab890632c2f6f80daf4e517f",
"type": "github"
},
"original": {
@@ -28,11 +28,11 @@
]
},
"locked": {
"lastModified": 1790215976,
"narHash": "sha256-VkR1rMGyjU9jtw8ISFx5uqR9e+LEaOlhHwrseFkiIVk=",
"lastModified": 1790996401,
"narHash": "sha256-f+kOxEmOUnQazVAmGg0i8hzwgd1kX/geY+k/Dpheye8=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "a6631107a83ceab5872f298a2ea710859c80c4cb",
"rev": "db7d5e2332710f5abb088f6b5de927d7f9511b35",
"type": "github"
},
"original": {
@@ -49,11 +49,11 @@
]
},
"locked": {
"lastModified": 1789892076,
"narHash": "sha256-Dkg4VKPmDPqTwaiw2WH5br73tXoL1qrOO0xJnR4TlcA=",
"lastModified": 1791105314,
"narHash": "sha256-oTM7QFgRQ+vxBpMBMc7hEYpqBtpXAzXu2xuB/tru84U=",
"owner": "nix-community",
"repo": "nix-index-database",
"rev": "9ad722673ab3b3f91f02135e53775825b240b869",
"rev": "e740bc4e9d8918d7819a51486d5099930c65c72e",
"type": "github"
},
"original": {
@@ -64,11 +64,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1790218706,
"narHash": "sha256-6e4Na3z008XpdVyOXgfasXIn1aN+z8AXFG+jXdQSyuI=",
"lastModified": 1791143449,
"narHash": "sha256-9+n3ReS2y9xS199LoJjKUWHwZrISGHWgkD/LUZTDZyA=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "c508844df6c28fa6dabc1b6af70f3ccbd65c5201",
"rev": "0d9e9b832d03ac387417e16ce1febf73b2e631e1",
"type": "github"
},
"original": {
@@ -94,11 +94,11 @@
]
},
"locked": {
"lastModified": 1788678358,
"narHash": "sha256-xFu9deElkBd81tRYo2yRSZQ40LDXMnVd/gO1/uELxhI=",
"lastModified": 1790708379,
"narHash": "sha256-UShpDPmypGviorjBWDwRtKWUTdVik7Zp6bt1t875jV4=",
"owner": "~rycee",
"repo": "nur-expressions",
"rev": "2554c0e25259c6ab3e71dac5177f8d38f76a6e1c",
"rev": "b549e36268fa8c975535d0405f66e013b234a0a4",
"type": "sourcehut"
},
"original": {
+250 -154
View File
@@ -801,46 +801,69 @@ Actually, nix *really* requires ~bash~. This is a plugin that allows using zsh f
}
#+END_SRC
** Claude Code
** LLM
:PROPERTIES:
:header-args:elisp: :tangle target/emacs.el
:END:
#+BEGIN_SRC nix
(let gptel = (epkgs: epkgs.melpaBuild {
pname = "gptel";
version = "0.9.9.6";
src = pkgs.fetchFromGitHub {
owner = "karthink";
repo = "gptel";
rev = "v0.9.9.6";
sha256 = "sha256-jurm3AQz+NiKZh7ssJkQYP73EmBEoyjzuC4phARYBak=";
};
recipe = pkgs.writeText "gptel-recipe" ''
(gptel :repo "karthink/gptel" :fetcher github)
'';
packageRequires = [ epkgs.transient ];
});
in
{
programs.claude-code = {
enable = true;
};
programs.emacs.init.usePackage = {
claude-code = {
enable = true;
package = (epkgs: epkgs.trivialBuild {
pname = "claude-code";
version = "0.4.5";
src = pkgs.fetchFromGitHub {
owner = "stevemolitor";
repo = "claude-code.el";
rev = "03199df8b3a1e9cd4857f0851f7a912ba524aff3";
sha256 = "sha256-5QJrWIu4EgnHcOqMwlrs2JBBx7aI9OaSJswesr6Apfk=";
};
packageRequires = [ epkgs.inheritenv ];
});
bindKeyMap."C-c c" = "claude-code-command-map";
custom.claude-code-optimize-window-resize = false;
};
programs.emacs = {
extraPackages = epkgs: with epkgs;
let gptel = (epkgs.melpaBuild {
pname = "gptel";
version = "0.9.9.6";
src = pkgs.fetchFromGitHub {
owner = "karthink";
repo = "gptel";
rev = "v0.9.9.6";
sha256 = "sha256-jurm3AQz+NiKZh7ssJkQYP73EmBEoyjzuC4phARYBak=";
};
recipe = pkgs.writeText "gptel-recipe" ''
(gptel :repo "karthink/gptel" :fetcher github)
'';
packageRequires = [ epkgs.transient ];
});
in [
(epkgs.trivialBuild {
pname = "claude-code";
version = "0.4.5";
src = pkgs.fetchFromGitHub {
owner = "stevemolitor";
repo = "claude-code.el";
rev = "03199df8b3a1e9cd4857f0851f7a912ba524aff3";
sha256 = "sha256-5QJrWIu4EgnHcOqMwlrs2JBBx7aI9OaSJswesr6Apfk=";
};
packageRequires = [ epkgs.inheritenv ];
})
(epkgs.melpaBuild {
gptel = {
enable = true;
package = gptel;
command = ["gptel"];
bind."C-c g" = "gptel-menu";
custom = {
gptel-default-mode = "'org-mode";
gptel-model = "'claude-sonnet-5";
gptel-org-branching-context = true;
gptel-include-reasoning = false;
};
config = ''
<<emacs-gptel-config>>
'';
};
gptel-agent = {
enable = true;
package = (epkgs: epkgs.melpaBuild {
pname = "gptel-agent";
version = "20260823";
src = pkgs.fetchFromGitHub {
@@ -852,58 +875,89 @@ Actually, nix *really* requires ~bash~. This is a plugin that allows using zsh f
recipe = pkgs.writeText "gptel-agent-recipe" ''
(gptel-agent :fetcher github :repo "karthink/gptel-agent" :files (:defaults "agents"))
'';
packageRequires = [ gptel ];
})
gptel
];
packageRequires = [ (gptel epkgs) ];
});
after = ["gptel"];
config = ''
<<emacs-gptel-agent-config>>
'';
};
};
}
})
#+END_SRC
#+BEGIN_SRC elisp
(use-package claude-code
:bind-keymap ("C-c c" . claude-code-command-map)
:custom (claude-code-optimize-window-resize nil))
#+BEGIN_SRC elisp :tangle no :noweb-ref emacs-gptel-config
(setf (alist-get 'org-mode gptel-prompt-prefix-alist) "@user\n")
(setf (alist-get 'org-mode gptel-response-prefix-alist) "@assistant\n")
(gptel-make-tool
:name "git_uncommitted_diff"
:function (lambda ()
(magit-git-output "diff" "HEAD"))
:description "Return a unified diff of all uncommitted changes (staged and
unstaged) in the current git repository, relative to HEAD. Note: this does
not include brand-new untracked files that have never been added to the
index."
:args nil
:category "git")
(use-package gptel
:commands "gptel"
:bind (("C-c g" . gptel-menu))
:custom
(require 'org)
(gptel-default-mode 'org-mode)
(gptel-model 'claude-sonnet-5))
(gptel-make-tool
:name "git_head_commit_diff"
:function (lambda ()
(magit-git-output "show" "HEAD"))
:description "Return the diff (including commit message) introduced by the
HEAD commit in the current git repository, i.e. =git show HEAD=."
:args nil
:category "git")
(use-package gptel-agent
:after gptel
:config
(require 'gptel-agent-tools-introspection)
(gptel-agent-update)
(gptel-make-preset 'coding
:description "Preset for agentic coding"
:backend "Claude"
:model 'claude-sonnet-5
:use-tools t
:tools '("Agent" "Skill" "TodoWrite" "Grep" "Read" "Glob" "Write" "Insert"
"Edit" "Mkdir" "Diagnostics" "WebFetch" "WebSearch" "Bash"))
(gptel-make-preset 'research
:description "Preset for answering questions with web results"
:backend "Claude"
:model 'claude-sonnet-5
:use-tools t
:confirm-tool-calls nil
:tools '("WebFetch" "WebSearch"))
(gptel-make-preset 'emacs-research
:description "Preset for answering questions about the live emacs instance"
:backend "Claude"
:model 'claude-sonnet-5
:use-tools t
:confirm-tool-calls nil
:tools '("WebFetch" "WebSearch"
"variable_completions" "command_completions" "function_completions"
"variable_documentation" "function_documentation" "variable_value"
"variable_source" "function_source" "symbol_manual_section"
"library_source" "manual_node_contents" "manual_nodes" "manual_names"
"features" "load_paths" "symbol_exists")))
(gptel-make-tool
:name "git_branch_diff"
:function (lambda ()
(let* ((upstream (magit-get-upstream-branch))
(merge-base (and upstream (magit-git-string "merge-base" "HEAD" upstream))))
(if (not merge-base)
"Could not determine upstream branch / merge base for the current branch."
(magit-git-output "diff" merge-base))))
:description "Return a unified diff of all committed changes on the current
branch relative to its upstream tracking branch's merge base. This shows
everything this branch has added since it diverged from upstream."
:args nil
:category "git")
#+END_SRC
#+BEGIN_SRC elisp :tangle no :noweb-ref emacs-gptel-agent-config
(require 'gptel-agent-tools-introspection)
(gptel-agent-update)
(gptel-make-preset 'coding
:description "Preset for agentic coding"
:backend "Claude"
:model 'claude-sonnet-5
:use-tools t
:max-tokens 128000
:tools '("Grep" "Read" "Glob" "Write" "Insert"
"Edit" "Mkdir" "Diagnostics" "WebFetch" "WebSearch" "Bash" "git_uncommitted_diff" "git_head_commit_diff"
"git_branch_diff"))
(gptel-make-preset 'research
:description "Preset for answering questions with web results"
:backend "Claude"
:model 'claude-sonnet-5
:use-tools t
:max-tokens 128000
:confirm-tool-calls nil
:tools '("WebFetch" "WebSearch"))
(gptel-make-preset 'emacs-research
:description "Preset for answering questions about the live emacs instance"
:backend "Claude"
:model 'claude-sonnet-5
:use-tools t
:max-tokens 128000
:confirm-tool-calls nil
:tools '("WebFetch" "WebSearch"
"variable_completions" "command_completions" "function_completions"
"variable_documentation" "function_documentation" "variable_value"
"variable_source" "function_source" "symbol_manual_section"
"library_source" "manual_node_contents" "manual_nodes" "manual_names"
"features" "load_paths" "symbol_exists"))
#+END_SRC
** Age
@@ -992,14 +1046,12 @@ These are just some general settings that apply everywhere. First, a few package
{
programs.emacs = {
extraPackages = epkgs: with epkgs; [
ace-window
doom-modeline
free-keys
ligature
mixed-pitch
popper
];
};
programs.emacs.init.usePackage.emacs.enable = true;
}
#+END_SRC
@@ -1106,36 +1158,52 @@ especially on monitors with a lot of vertical space.
From the docs: "If ‘complete’, TAB first tries to indent the current line, and if the line was
already indented, then try to complete the thing at point." Recommended by [[https://github.com/minad/corfu][the ~corfu~ readme]].
#+BEGIN_SRC elisp
(setopt tab-always-indent 'complete)
#+BEGIN_SRC nix
{
programs.emacs.init.usePackage.emacs.custom.tab-always-indent = "'complete";
}
#+END_SRC
Set the cursor to be a bar. I'm not sure what the benefit would be of a block cursor, since the
current character isn't really "selected" like it would be in Helix. I switch this to a box when in
god mode / read-only buffers, like vim would.
#+BEGIN_SRC elisp
(setq-default cursor-type 'bar)
#+BEGIN_SRC nix
{
programs.emacs.init.usePackage.emacs.config = ''
(setq-default cursor-type 'bar)
'';
}
#+END_SRC
Repeat-mode takes care of eliminating some annoying duplicate keypresses. I mostly use repeat-mode
for two-character commands that need to be repeated; ~devil~ also has some single-key-with-modifier
repeats set up that only apply in that mode.
#+BEGIN_SRC elisp
(add-hook 'after-init-hook 'repeat-mode)
#+BEGIN_SRC nix
{
programs.emacs.init.usePackage.repeat = {
enable = true;
hook = ["after-init"];
};
}
#+END_SRC
Save the minibuffer history to disk, so recently-used options show up at the top of the picker
across restarts.
#+BEGIN_SRC elisp
(add-hook 'after-init-hook 'savehist-mode)
#+BEGIN_SRC nix
{
programs.emacs.init.usePackage.savehist = {
enable = true;
hook = ["after-init"];
};
}
#+END_SRC
Let's unbind some builtin keys that I don't use. I'm not sure if I'll replace them with anything or not, but at a minimum it'll avoid accidental keypresses and clean up which-key output.
#+begin_src elisp
#+begin_src elisp :tangle no
(require 'bind-key)
(unbind-key "C-x RET")
(unbind-key "C-x #")
@@ -1161,60 +1229,90 @@ it would update after every command, slowing down everything). I don't need the
I have the tab bar at the top of the window. The other settings just clear up some space, and make
things look at little nicer.
#+BEGIN_SRC elisp
(require 'doom-modeline)
(setopt doom-modeline-irc nil)
(setopt doom-modeline-workspace-name nil)
(setopt doom-modeline-buffer-encoding nil)
(setopt doom-modeline-buffer-file-name-style 'relative-to-project)
; (setopt doom-modeline-vcs-max-length 30)
(doom-modeline-remove-segment 'vcs 'main)
(doom-modeline-mode)
#+BEGIN_SRC nix
{
programs.emacs.init.usePackage.doom-modeline = {
enable = true;
hook = ["after-init"];
custom = {
doom-modeline-irc = false;
doom-modeline-workspace-name = false;
doom-modeline-buffer-encoding = false;
doom-modeline-buffer-file-name-style = "'relative-to-project";
};
config = ''
(doom-modeline-remove-segment 'vcs 'main)
'';
};
}
#+END_SRC
Ligature mode combines character sequences like <= into a single on-screen glyph. It looks kind of
cool, so I'm turning it on in prog mode. The enabled ligatures are font-specific, taken from [[https://github.com/mickeynp/ligature.el/wiki#jetbrains-mono][the
ligature wiki]].
#+BEGIN_SRC elisp :noweb no
(ligature-set-ligatures 'prog-mode
'("--" "---" "==" "===" "!=" "!==" "=!="
"=:=" "=/=" "<=" ">=" "&&" "&&&" "&=" "++" "+++" "***" ";;" "!!"
"??" "???" "?:" "?." "?=" "<:" ":<" ":>" ">:" "<:<" "<>" "<<<" ">>>"
"<<" ">>" "||" "-|" "_|_" "|-" "||-" "|=" "||=" "##" "###" "####"
"#{" "#[" "]#" "#(" "#?" "#_" "#_(" "#:" "#!" "#=" "^=" "<$>" "<$"
"$>" "<+>" "<+" "+>" "<*>" "<*" "*>" "</" "</>" "/>" "<!--" "<#--"
"-->" "->" "->>" "<<-" "<-" "<=<" "=<<" "<<=" "<==" "<=>" "<==>"
"==>" "=>" "=>>" ">=>" ">>=" ">>-" ">-" "-<" "-<<" ">->" "<-<" "<-|"
"<=|" "|=>" "|->" "<->" "<~~" "<~" "<~>" "~~" "~~>" "~>" "~-" "-~"
"~@" "[||]" "|]" "[|" "|}" "{|" "[<" ">]" "|>" "<|" "||>" "<||"
"|||>" "<|||" "<|>" "..." ".." ".=" "..<" ".?" "::" ":::" ":=" "::="
":?" ":?>" "//" "///" "/*" "*/" "/=" "//=" "/==" "@_" "__" "???"
"<:<" ";;;"))
(global-ligature-mode t)
#+BEGIN_SRC nix :noweb no
{
programs.emacs.init.usePackage.ligature = {
enable = true;
hook = ["(after-init . global-ligature-mode)"];
config = ''
(ligature-set-ligatures 'prog-mode
'("--" "---" "==" "===" "!=" "!==" "=!="
"=:=" "=/=" "<=" ">=" "&&" "&&&" "&=" "++" "+++" "***" ";;" "!!"
"??" "???" "?:" "?." "?=" "<:" ":<" ":>" ">:" "<:<" "<>" "<<<" ">>>"
"<<" ">>" "||" "-|" "_|_" "|-" "||-" "|=" "||=" "##" "###" "####"
"#{" "#[" "]#" "#(" "#?" "#_" "#_(" "#:" "#!" "#=" "^=" "<$>" "<$"
"$>" "<+>" "<+" "+>" "<*>" "<*" "*>" "</" "</>" "/>" "<!--" "<#--"
"-->" "->" "->>" "<<-" "<-" "<=<" "=<<" "<<=" "<==" "<=>" "<==>"
"==>" "=>" "=>>" ">=>" ">>=" ">>-" ">-" "-<" "-<<" ">->" "<-<" "<-|"
"<=|" "|=>" "|->" "<->" "<~~" "<~" "<~>" "~~" "~~>" "~>" "~-" "-~"
"~@" "[||]" "|]" "[|" "|}" "{|" "[<" ">]" "|>" "<|" "||>" "<||"
"|||>" "<|||" "<|>" "..." ".." ".=" "..<" ".?" "::" ":::" ":=" "::="
":?" ":?>" "//" "///" "/*" "*/" "/=" "//=" "/==" "@_" "__" "???"
"<:<" ";;;"))
'';
};
}
#+END_SRC
Use variable-width fonts in text buffers. ~mixed-pitch-mode~ keeps most code blocks as monospace.
#+BEGIN_SRC elisp
(add-hook 'text-mode-hook 'mixed-pitch-mode)
#+BEGIN_SRC nix
{
programs.emacs.init.usePackage.mixed-pitch = {
enable = true;
command = ["mixed-pitch-mode"];
hook = ["text-mode"];
init = ''
(defun no-mixed-pitch ()
(mixed-pitch-mode -1))
(defun no-mixed-pitch ()
(mixed-pitch-mode -1))
(add-hook 'yaml-ts-mode-hook 'no-mixed-pitch)
(add-hook 'mhtml-mode 'no-mixed-pitch)
(add-hook 'nxml-mode 'no-mixed-pitch)
(add-hook 'yaml-ts-mode-hook 'no-mixed-pitch)
(add-hook 'mhtml-mode 'no-mixed-pitch)
(add-hook 'nxml-mode 'no-mixed-pitch)
'';
};
}
#+END_SRC
A basic ace-window setup. Allows switching between windows quickly (although avy can do the same...).
#+BEGIN_SRC elisp
(bind-keys ("M-o" . ace-window)
("s-o" . ace-window)
("C-c o" . ace-window))
(setopt aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))
(setopt aw-dispatch-always t)
#+BEGIN_SRC nix
{
programs.emacs.init.usePackage.ace-window = {
enable = true;
bind = {
"M-o" = "ace-window";
"s-o" = "ace-window";
"C-c o" = "ace-window";
};
custom = {
aw-keys = "'(?a ?s ?d ?f ?g ?h ?j ?k ?l)";
aw-dispatch-always = true;
};
};
}
#+END_SRC
Set up a few project- / workspace-related key bindings.
@@ -1239,23 +1337,13 @@ Set up a few project- / workspace-related key bindings.
}
#+END_SRC
Don't run a server. I'm not using this, I can always turn it back on if I need to.
#+BEGIN_SRC elisp
(server-mode -1)
#+END_SRC
All the beeping is annoying. This flashes a huge icon over the screen, which is still annoying, but
less so?
#+BEGIN_SRC elisp
(setopt visible-bell t)
#+END_SRC
I'm trying this out. This disables visual selections. It's bold, which is why I like it :)
#+BEGIN_SRC elisp
(transient-mark-mode -1)
#+BEGIN_SRC nix
{
programs.emacs.init.usePackage.emacs.custom.visible-bell = true;
}
#+END_SRC
I use tabs to manage multiple "workspaces". I don't tie a project to a tab or anything; often fixing
@@ -1266,18 +1354,27 @@ fix / issue, and leave it open until I'm done with that issue.
change when drilling into a help page or something. I've set up repeat mode to make it easier to
move backward / forward through history.
#+BEGIN_SRC elisp
(bind-keys :repeat-map tab-bar-repeat-map
("<left>" . tab-bar-history-back)
("<right>" . tab-bar-history-forward))
(tab-bar-history-mode)
#+BEGIN_SRC nix
{
programs.emacs.init.usePackage.tab-bar = {
enable = true;
hook = ["(after-init . tab-bar-history-mode)"];
config = ''
(bind-keys :repeat-map tab-bar-repeat-map
("<left>" . tab-bar-history-back)
("<right>" . tab-bar-history-forward))
'';
};
}
#+END_SRC
Make compilation mode buffers auto-scroll to the first error:
#+BEGIN_SRC elisp
(setopt compilation-scroll-output 'first-error)
#+BEGIN_SRC nix
{
programs.emacs.init.usePackage.compile.enable = true;
programs.emacs.init.usePackage.compile.custom.compilation-scroll-output = true;
}
#+END_SRC
#+begin_src nix
@@ -1331,7 +1428,7 @@ content to be.
};
custom = {
popper-display-control = false;
popper-reference-buffers = ''(concat my/bottom-popups my/right-popups)'';
popper-reference-buffers = ''(append jf/bottom-popups jf/right-popups)'';
};
};
};
@@ -1367,7 +1464,6 @@ content to be.
"\\*claude:.*"
"\\*Man .*"
"\\*List .*"
"\\*Embark Actions\\*"
"\\*eldoc\\*"))
#+end_src
@@ -1768,7 +1864,7 @@ I don't like UI elements that insert fake text into the file. They mess up the s
programs.emacs.init.usePackage.eglot = {
custom = {
eglot-ignored-server-capabilities = "'(:inlayHintProvider :semanticTokensProvider)";
eglot-code-actions-indications = false;
eglot-code-action-indications = false;
eglot-events-buffer-config = "'(:size 300000 :format full)";
};
config = "(add-hook 'eglot-managed-mode-hook (lambda () (eglot-inlay-hints-mode -1)) nil t)";