aboutsummaryrefslogtreecommitdiff
path: root/modules/ai-config.el
Commit message (Collapse)AuthorAgeFilesLines
* feat(ai): remember the AI-Assistant panel width across togglesCraig Jennings9 days1-6/+22
| | | | | | | | | | The *AI-Assistant* side window always opened at a fixed 0.4 width, so resizing it by hand was lost the next time it opened. Now the F-key toggle captures the panel's width when it closes and reopens at that width for the rest of the session, the same way the music playlist remembers its height. The panel has three entry points that all open the same buffer: the toggle, loading a saved conversation, and escalating a quick-ask. I gave them one shared remembered-width var (cj/--ai-assistant-width, owned by ai-config; the other two forward-declare it to avoid a circular require), so the panel comes back at one consistent width whichever door opens it. Capture lives only in the toggle's close path; the other two just replay. One latent edge: ai-conversations replays with its configurable side, which defaults to right. If that's ever set to top or bottom, the remembered width fraction would land as a height. It can't happen at the default, so I left it as a known edge rather than complicating the call now. The escalation test needed a top-level defvar for the shared var: a value-less defvar in the module is only file-local to the byte-compiler, so without it the function read the var dynamically and hit void when the test loaded ai-quick-ask without ai-config. ai-quick-ask 13/13 and ai-conversations 47/47 green.
* refactor(load-graph): route C-; registration through the keymap APICraig Jennings12 days1-1/+1
| | | | | | Migrated all 31 cj/custom-keymap registration sites across 24 modules from direct (keymap-set cj/custom-keymap ...) calls to cj/register-prefix-map and cj/register-command. Consumers no longer reference cj/custom-keymap directly, so keybindings.el is the sole owner of the C-; prefix and modules reach it only through the API (each already requires keybindings from Phase 2). Behavior-preserving: I dumped every C-; binding before and after the migration and they're identical: 279 bindings, each resolving to the same command. The which-key label blocks are untouched, since they use string key descriptions and never assumed the keymap existed. I byte-compiled all 24 files (no new free-variable warnings, because the cj/custom-keymap references are gone), and make test, validate-modules, and an init load all pass.
* docs(load-graph): classify domain, integration, and optional modulesCraig Jennings12 days1-0/+10
| | | | | | | | | | Eighth classification batch: 17 domain/integration/optional modules — ai-config, ai-vterm, browser-config, calendar-sync, calibredb-epub-config, chrono-tools, dirvish-config, dwim-shell-config, erc-config, eshell-config, eww-config, flyspell-and-abbrev, games-config, gloss-config, httpd-config, jumper, latex-config. I annotated each header, added a Batch 8 table to the inventory, and extended the validation allowlist. 82 of 102 modules are now classified. Almost all are eager only by init order and become command/hook/mode-loaded. calendar-sync stays eager when its .local.el is present. One new hidden dependency: calendar-sync guards its C-; g registration with a boundp shim and doesn't require keybindings, so the binding drops standalone. I deferred elfeed-config rather than annotate it. Its header edit triggers byte-compilation, and the existing tests only pass when the module loads as interpreted source — the compiled cj/elfeed-process-entries inlines an elfeed struct accessor the stubs can't intercept, and the batch test environment has no elfeed package to build real structs. It needs its tests rewritten first, recorded in the inventory and a new todo task. Also made the header allowlist scoping test durable: it used games-config (now classified) as its unclassified example; switched to a sentinel name plus a duplicate-entry guard.
* refactor(auth): consolidate the auth-source secret lookup into one helperCraig Jennings14 days1-8/+6
| | | | | | | | The auth-source-search + funcall-the-secret block was copied four times: calendar-sync--calendar-url, cj/auth-source-secret (ai-config), cj/--auth-source-password (transcription), and cj/slack--get-credential. Each searched authinfo, pulled :secret, and called it when the netrc backend returned a function. I pulled that into cj/auth-source-secret-value in system-lib (a leaf, so calendar-sync doesn't have to depend on ai-config and drag in the gptel stack). It takes an optional user and returns the secret or nil. The four callers now delegate to it: ai-config layers its required-secret error on top, and the others keep their nil-on-miss behavior. With the direct auth-source-search calls gone, I dropped the now-unused (require 'auth-source) from transcription, slack, and calendar-sync. The helper's autoload covers it. The transcription tests that exercise the delegated path stay green, and the primitive and the error wrapper get their own tests.
* fix(ai-config): require gptel backend libs so the fork's constructors loadCraig Jennings2026-05-221-1/+11
| | | | | | cj/toggle-gptel and gptel chat errored with "Symbol's function definition is void: gptel-make-anthropic". The local gptel fork on :load-path with :ensure nil ships no generated autoloads, so (require 'gptel) loads gptel.el but never gptel-anthropic.el or gptel-openai.el, where the gptel-make-* constructors live. cj/ensure-gptel-backends then reached gptel-make-anthropic before it was defined. cj/ensure-gptel-backends now requires gptel-anthropic and gptel-openai first, through a small cj/--gptel-load-backend-libs helper. Verified end-to-end: with the fork on load-path, the constructors are fbound and both backends build.
* refactor(ai-config): switch gptel to local fork, drop tab-width adviceCraig Jennings2026-05-181-22/+2
| | | | | | | | | | | | | | I switched the gptel use-package form to `:load-path "~/code/gptel"` with `:ensure nil` so Emacs loads from the fork instead of the MELPA release. The fork now carries the narrow `tab-width' copy in `gptel-org--create-prompt' that karthink redirected the upstream PR to, which replaces the local `:around' advice on `gptel--with-buffer-copy-internal' I'd been carrying. I also dropped the stale test file `tests/test-ai-config-gptel-prompt-tab-width.el' and the matching stub in `tests/testutil-ai-config.el'. Both existed only to test the advice I removed.
* fix(ai-config): gptel-model must be a symbol, not a stringCraig Jennings2026-05-161-3/+5
| | | | | | | | | | | | | | The default-backend swap to gpt-5.5 (commit 0f029ab5) set `gptel-model' as the string "gpt-5.5". gptel's modeline-display code calls `symbolp' on the model value and signals `wrong-type-argument symbolp "gpt-5.5"' on every render, which manifested as Emacs freezing in the AI-Assistant buffer ("Querying ChatGPT..." → error in process sentinel → repeated redisplay errors). Both default-setting sites now use `'gpt-5.5' (interned symbol). The Anthropic backend tolerated string model names so the original "claude-opus-4-7" string worked, which is why this hadn't surfaced before.
* chore(ai-config): switch default gptel backend to ChatGPT / gpt-5.5Craig Jennings2026-05-161-5/+5
| | | | | | | | Two places set the default backend + model on gptel initialization -- `cj/ensure-gptel-backends' (the lazy-init fallback) and the `use-package gptel :config' block (the eager-set after initialization). Both now pick the ChatGPT backend with `gpt-5.5' instead of Claude with `claude-opus-4-7'.
* feat(gptel-tools): wire web_fetch as a local toolCraig Jennings2026-05-161-1/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Fourth ADOPT entry from `docs/design/gptel-tools-shortlist.org'. Lets gptel pull a URL into the conversation so the model can read docs / current API shapes / etc. without me copy-pasting. Shape: - URL must be `http://' or `https://' (file://, ftp://, javascript:, scheme-less, etc. are rejected at the validator). - HTML responses go through `pandoc -f html -t plain' so the model gets a reading shape that isn't full of markup; falls back to `w3m -dump -T text/html' if pandoc isn't on PATH; signals `user-error' if neither is. Pass `raw=t' to skip stripping. - Output capped at 200KB by default, hard cap 1MB; `max_bytes' argument lets the caller pick a lower cap. Truncation reported inline. - 4xx / 5xx response codes signal `error' with the code -- the alternative is returning an error page body, which the model would treat as content. `:confirm t' on the tool because every call is a real outbound network request. The tool's description warns that URLs go wherever the user-agent points, including internal networks if that's what the URL names. `tests/test-gptel-tools-web-fetch.el' -- 20 tests across Normal / Boundary / Error. URL validator covers http / https / non-string / empty / non-http schemes. `--effective-max-bytes' covers default / low-clamp / hard-cap / passthrough. Truncate helper covers under-cap / at-cap / over-cap with the marker. HTML stripper runs against real pandoc / w3m (both installed in dev env, neither should mangle simple markup). Orchestrator stubs `cj/gptel-web-fetch--retrieve' via `cl-letf' to cover normal / raw / 4xx / 5xx / oversize / bad-scheme paths. Wired into `cj/gptel-local-tool-features' so gptel exposes the tool on next restart. Note: `call-process-region' invocation flattened to a single `with-temp-buffer' with DELETE=t -- the initial draft nested a second temp buffer and routed output to the inner one, which got killed before `buffer-string' on the outer ran. Test caught it.
* feat(gptel-tools): wire git_status / git_log / git_diff as local toolsCraig Jennings2026-05-161-1/+4
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Three read-only git context tools so gptel can see what's changed without me pasting `git status` / `git log` / `git diff` output into every chat turn. Builds the first batch from the ADOPT bucket in `docs/design/gptel-tools-shortlist.org`. Shape per tool: - `gptel-tools/git_status.el` — `git status --short --branch` for a directory inside a git working tree under HOME. Returns the porcelain output, or a "Clean working tree" marker when only the branch line is present. - `gptel-tools/git_log.el` — `git log --oneline -nN` with an optional `--since` filter. N defaults to 20, capped at 100; nil / non- integer / out-of-range N falls back to the default. - `gptel-tools/git_diff.el` — `git diff [REF1 [REF2]] [-- FILE]`. Output capped at ~500KB so a runaway diff can't blow up context; truncation is reported inline. Validation is uniform: path must resolve under HOME, must be a directory, must be inside a git working tree (verified via `git rev-parse --is-inside-work-tree`). Color is disabled via `-c color.ui=false` at the git level (`git status` doesn't accept `--no-color` directly). Tests run against real temp git repos created via `process-file`, not mocked — there's nothing in gptel-tools/git_*.el that's process-mockable in a meaningful way, and a real `git init` + a couple of commits is cheaper than building a fake. 31 tests total: 7 for git_status, 11 for git_log, 13 for git_diff. Wired into `cj/gptel-local-tool-features` so gptel exposes the three tools on next restart.
* feat(ai-conversations-browser): dired-style browser for saved GPTel ↵Craig Jennings2026-05-161-0/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | conversations `cj/gptel-load-conversation` prompts via `completing-read`. A dedicated browser shows what each conversation is about at a glance and supports single-key load / delete / rename without having to scroll a minibuffer list. New module `modules/ai-conversations-browser.el` + `cj/gptel-browse-conversations` entry point bound to `C-; a b` ("browse conversations"). Opens `*GPTel-Conversations*` in `cj/gptel-browser-mode` (a `special-mode` derivative). Each row shows date, time, topic slug, and a preview of the most recent message (length configurable via `cj/gptel-browser-preview-length`, default 60 chars). Rows sort newest first. In the browser: - `RET` / `l`: load the conversation (delegates to `cj/gptel-load-conversation` with the file pre-selected via a `cl-letf` stub on `completing-read` so the user isn't prompted twice), then bury the window. - `d`: delete the file under point after `y-or-n-p` confirmation, re-render. - `r`: rename the file under point. Preserves the timestamp, slugifies the new topic, refuses unchanged input and existing targets. - `g`: refresh. - `n` / `p`: next / previous row. - `q`: quit-window. 21 tests cover the helpers (topic parsing, header stripping, preview shaping for truncate / short / empty cases, row-for-file with conversation + non-conversation filenames, rows enumeration, render output for empty + populated cases, newest-first sort, rename-target preservation of timestamp + slug, rename-target error on missing timestamp) and the file-touching actions (delete with y, cancel with n, rename, rename-on-empty-line error).
* feat(ai-rewrite): add directive-picker wrappers around gptel-rewriteCraig Jennings2026-05-161-2/+6
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | `gptel-rewrite` is the killer feature for the keep-gptel decision, and it now lives behind two commands instead of the bare call: - `cj/gptel-rewrite-with-directive` (`C-; a r`, replacing the former bare `gptel-rewrite` binding): completing-read on a directive name from `cj/gptel-rewrite-directives`, then rewrite the active region. - `cj/gptel-rewrite-redo-with-different-directive` (`C-; a R`): replay the prior region with a different directive. The region is preserved via markers stored buffer-local on the first call so it survives accept/reject of the prior rewrite. I picked the hook injection approach over an `:after`-advice + state-capture pattern. `gptel-rewrite-directives-hook` is an abnormal hook gptel-rewrite already consults for a per-call system message. Wrapping the call in a one-shot `let`-binding on that hook gives the directive exactly the lifetime of the rewrite and leaves nothing to clean up. Mutating `gptel-directives` globally would mean either restoring it afterward or living with the change -- both worse than the hook. Directives ship inline as a `defcustom` alist with the six names called out in the task -- `terse`, `fix-grammar`, `refactor-readability`, `add-docstring`, `explain-as-comment`, `shorten`. Customization is a `customize-variable` or `setq` away. 9 tests cover the defcustom shape (default names present, bodies non-empty strings), the wrapper (normal path, no-region error, unknown-directive error, last-state recording), and the redo (replays the prior region, errors when no previous, excludes the current directive from the re-pick prompt). `gptel-rewrite` stubbed in tests so no rewrite UI fires.
* feat(ai-quick-ask): add cj/gptel-quick-ask one-shot commandCraig Jennings2026-05-161-0/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | New module `modules/ai-quick-ask.el`. Bound to `C-; a q` via `cj/ai-keymap` ("quick ask"). `cj/gptel-quick-ask` reads a prompt in the minibuffer, creates a transient `*GPTel-Quick*` buffer in `cj/gptel-quick-mode` (a special-mode derivative with `q` / `escape` / `c` bindings), inserts "Q: <prompt>" plus a response marker, then calls `gptel-request` with `:stream t` so the answer streams into the buffer. Doesn't touch `*AI-Assistant*`, doesn't autosave. Two follow-up commands work in the buffer: - `cj/gptel-quick-dismiss` (`q` / `escape`): delete the window and kill the buffer. Idempotent when the buffer is absent. - `cj/gptel-quick-continue` (`c`): extract the prompt + response, seed them into `*AI-Assistant*` under proper org headings (matching the `cj/gptel--fresh-org-prefix` shape), display the side window, then dismiss the quick buffer. 13 tests cover the pure helpers (initial-text shape, response extraction across normal / multi-line / no-marker / empty inputs, seed-text shape), the ask path (buffer created in right mode, prompt recorded, gptel-request called, empty-prompt error), the dismiss path (kills buffer / no-op when absent), and the continue path (seeds `*AI-Assistant*`, dismisses quick buffer, errors outside a quick buffer). `gptel-request` is stubbed in tests so nothing hits the network.
* feat(ai-conversations): add cj/gptel-autosave-toggle with [AS] mode-line ↵Craig Jennings2026-05-161-0/+3
| | | | | | | | | | | | | | | | | | | | | | | | | indicator `cj/gptel-autosave-enabled` flipped to t inside the save/load entry points with no way back off short of editing the variable or clearing the buffer, and no visible indicator that it was on. Two pieces: - `cj/gptel-autosave-toggle` flips the buffer-local state in the current GPTel buffer. Bound to `C-; a A` via `cj/ai-keymap` (which-key: "toggle autosave"). When autosave is OFF and no filepath is configured yet, the command prompts to save the conversation first so a save target exists; otherwise it just flips the bit. - `cj/gptel-autosave-mode-line-format` surfaces " [AS]" in the mode-line when autosave is on, blank when off. Installed via a `gptel-mode-hook` so every GPTel buffer picks it up. The install helper is idempotent. 6 new tests cover enable/disable paths, the no-filepath prompt path, the not-a-gptel-buffer error path, the mode-line format evaluation, and the install idempotence.
* fix(ai-config): hook gptel-magit wiring per-feature, not on magitCraig Jennings2026-05-161-6/+13
| | | | | | | | | | | | | | | | | | | | | | | | | | | The wiring keyed on `with-eval-after-load 'magit` fires while two of its three references are still undefined. `magit.el` calls `(provide 'magit)` BEFORE its `cl-eval-when (load eval)` block requires `magit-commit` and `magit-stash`. At that moment the `magit-commit` transient prefix doesn't exist, and `transient-append-suffix` silently no-ops on missing prefixes (default `transient-error-on-insert-failure` is nil). The "g Generate commit" and "x Explain" suffixes never landed. Only the M-g binding worked, because `git-commit` IS required before provide. Three per-feature hooks replace the single `'magit` hook: one each on `git-commit`, `magit-commit`, and `magit-diff`. Each hooks the exact dependency the wiring needs, side-stepping the load-order race entirely. The companion test was rewritten to check `after-load-alist` registration rather than drive the hooks through `provide`. Emacs 30 batch mode doesn't fire registered `eval-after-load` callbacks on `provide` alone -- only an actual `load` does. Inspecting the registration is the stronger guard anyway: the regression is "a single `'magit` hook," and the right shape of that check is "no entry under `magit`, entries under `git-commit`, `magit-commit`, `magit-diff`."
* feat(gptel-tools): wire update_text_file as a local tool with testsCraig Jennings2026-05-161-0/+1
| | | | | | | | | | | | | | | | | | | | | I rewrote `update_text_file.el` in pure Elisp. The previous version shelled out to sed for everything, had a stray quote terminator at EOF (line 149) that broke loading, produced literal backslash-n where actual newlines were expected, and prompted via `y-or-n-p` redundantly with gptel's own `:confirm t` flag. The five operations -- replace, append, prepend, insert-at-line, delete-lines -- split into pure string transforms that test without touching the disk. The file-level wrapper validates the path, enforces a 10MB size limit, takes a timestamped backup, and writes atomically. No backup is created when the operation is a no-op. Patterns are literal substrings (not regex) so the model can't trip over metacharacter quoting. `tests/test-update-text-file.el` covers Normal / Boundary / Error per operation plus the file-level wrapper. 48 tests green. Added `update_text_file` to `cj/gptel-local-tool-features` so gptel exposes the tool after restart.
* fix(ai-config): force tab-width=8 in gptel org-mode prompt buffersCraig Jennings2026-05-161-0/+22
| | | | | | | | gptel's `gptel--with-buffer-copy-internal` copies the source buffer's `major-mode` symbol but doesn't run mode hooks. An inherited-org-mode prompt buffer keeps `tab-width` at this config's global default of 4 instead of the 8 that `org-mode-hook` would set. When gptel later parses the prompt buffer with `org-element`, Org's `tab-width=8` guard raises "Tab width in Org files must be 8, not 4." I was hitting this on every second `gptel-magit-generate-message` from COMMIT_EDITMSG. `vc-config.el` sets `git-commit-major-mode 'org-mode'`, and the diffs contained list-shaped content that `org-element--list-struct` parsed. The advice forces `tab-width=8` in the prompt buffer when its inherited mode is org-mode. It's a local workaround for an upstream gap. An upstream patch to run `(delay-mode-hooks (funcall major-mode))` in the buffer-copy is the real fix. I'll send it next.
* fix(ai-config): Ensure gptel-magit is installed via use-packageCraig Jennings2026-05-151-16/+16
| | | | | | | Replace raw autoload calls with a `use-package` declaration so `use-package-always-ensure` installs gptel-magit on machines that haven't run `package-install`, fixing the "Cannot open load file" error on transient setup.
* chore(ai-config): refresh gptel model menusCraig Jennings2026-05-141-8/+7
| | | | | | | | | | | | | | | | | | | Anthropic: bump Opus 4.6 → 4.7 (current frontier). Sonnet 4.6 and Haiku 4.5 stay -- still current. Default `gptel-model' setq also bumped to claude-opus-4-7 in both places it was set. OpenAI: drop the cohort retired from ChatGPT on 2026-02-13 (gpt-4o, gpt-5 original, gpt-4.1, o1). Replace with the current lineup: gpt-5.5 (flagship), gpt-5.4-mini (fast/cheap), o3 (reasoning). All three are documented at developers.openai.com/api/docs/models/. Drive-by: stale docstring example in cj/gptel--current-model-selection bumped to match. gptel's bundled :models list only knows up to May-2025 IDs but gptel-make-anthropic / gptel-make-openai accept any string and pass it straight to the API, so newer names work without a gptel upgrade.
* Move GPTel tool loading into AI configCraig Jennings2026-05-101-3/+46
| | | | Move the local GPTel tool wiring out of init.el and into ai-config. The tools directory and feature list are now configurable, missing optional tools are non-fatal, and focused tests cover the loading behavior.
* feat(ai-vterm): F9 toggle/redisplay/pick + persistent split geometryCraig Jennings2026-05-081-5/+4
| | | | | | | | | | | | | | | | | | | | | | | F9 was a single command that always opened the project picker. Three small frustrations stacked up. With one claude buffer open and not visible, F9 was a redundant prompt to pick a project that already had a session. With claude visible, there was no way to bury it without M-x quit-window. With two projects' buffers alive, swapping between them was a buffer-switch chore. F9 is now a dispatch: - Claude visible in this frame: quit the window (toggle off) and capture the geometry first. - Exactly one claude buffer alive but hidden: re-display it (DWIM single-buffer case). - Zero or two-plus alive: fall through to the project picker. C-F9 is the always-pick-project entry point for explicit project switches. M-F9 is a buffer picker over the alive claude buffers. If a claude window is currently shown, the picked buffer replaces it in that window so the split orientation and size carry over. The shown buffer sorts last in the picker with a [shown] marker so RET picks "the other one." Split geometry persists across toggles. Two module-level vars (cj/--ai-vterm-last-direction, cj/--ai-vterm-last-size) capture at toggle-off and feed a custom display action. After M-S-t flips claude from right to bottom, F9 toggle-off-then-on returns it at the bottom. After a mouse resize, the next toggle restores that fraction. State is per-session. Restarts reset to default right/0.5. Two display-buffer fixes came out of testing: - save-window-excursion around (vterm name) keeps the dashboard from being buried on a fresh F9 at startup. vterm calls pop-to-buffer-same-window internally, which would otherwise replace the selected window's buffer before the alist could route the new one. - The action chain swaps display-buffer-use-some-window for a more specific cj/--ai-vterm-reuse-existing-claude. The generic version stole non-claude windows on C-F9 when the user was focused inside claude (claude on bottom, code on top -> new project landed in the code window). The specific version only reuses windows that already show a claude buffer. I reclaimed C-F9 from the gptel toggle in ai-config.el. C-; a t still binds gptel. I added eight new test files (claude-buffers, displayed-claude-window, dispatch, pick-buffer-candidates, window-geometry, capture-state, display-saved, reuse-existing-claude) plus a regression test on cj/--ai-vterm-show-or-create for the dashboard-preservation fix. All 73 ai-vterm tests pass and the full make test suite is green.
* feat(ai-vterm): add Claude launcher with vertical-split vtermCraig Jennings2026-05-071-1/+3
| | | | | | | | The new module picks a Claude-template project from a filtered completing-read list. It scans the same roots the `ai` shell launcher uses, then opens or reuses a vterm buffer named `claude [<repo>]` on the right. F9 launches it. The prior `cj/toggle-gptel` binding moves from F9 to C-F9 so both AI tools share the same physical key. The display rule chains reuse-window -> use-some-window -> in-direction (right). The resulting window isn't dedicated. That matters because side-window dedication was breaking `buffer-move` (C-M-arrows) and `switch-to-buffer` replacement on the claude buffer. I also narrowed `vterm-toggle`'s display rule to skip `claude [` buffers. Otherwise it claimed them first with its bottom-split + dedicated treatment. I added 23 tests across 5 files: the buffer-name transform, candidate walker, show-or-create dispatch, picker, and display rule. Design lives at docs/design/ai-vterm.org.
* fix(calendar-sync): handle variable-length date lists in RRULE UNTILCraig Jennings2026-03-091-2/+4
| | | | | | | date-to-time used (reverse date) which broke when RRULE UNTIL values were parsed as 5-element lists (year month day hour minute) from UTC timestamps. This caused recurring events with UTC UNTIL dates to expand to 0 occurrences, producing stale calendar entries.
* refactor(gptel): extract model-list and selection logic for testabilityCraig Jennings2026-03-061-20/+42
| | | | | | | - Extract cj/gptel--build-model-list from cj/gptel-change-model - Extract cj/gptel--current-model-selection from cj/gptel-change-model - Add test-ai-config-build-model-list.el (9 tests) - Add test-ai-config-current-model-selection.el (8 tests)
* refactor(gptel): lazy-load gptel-magit, rebind rewrite/context keysCraig Jennings2026-03-061-29/+47
| | | | | | | | | | - Replace use-package gptel-magit hook with autoloads via with-eval-after-load 'magit (loads gptel only on key press) - Move org header defuns above use-package to fix load order - Set gptel-include-reasoning to "*AI-Reasoning*" buffer - Rebind rewrite to C-; a r, clear context to C-; a c - Add test-ai-config-gptel-magit-lazy-loading.el (8 tests) - Mark all ai-config cleanup items DONE in todo.org
* refactor(gptel): move config defuns to top level, rebind keys, set reasoningCraig Jennings2026-03-061-39/+34
| | | | | | | | | - Move cj/gptel--fresh-org-prefix, cj/gptel--refresh-org-prefix, cj/gptel-backend-and-model, cj/gptel-insert-model-heading out of use-package :config to top level (fixes byte-compile warnings) - Set gptel-include-reasoning to "*AI-Reasoning*" buffer - Rebind rewrite to C-; a r, clear context to C-; a c - Update todo.org with completed cleanup items
* test(gptel): add unit tests for ai-config, remove dead cj/gptel-backendsCraig Jennings2026-03-061-6/+0
| | | | | | | | - Add testutil-ai-config.el with gptel stubs for batch testing - Add tests for cj/gptel--model-to-string (9 tests) - Add tests for cj/gptel--fresh-org-prefix (8 tests) - Add tests for cj/gptel-backend-and-model (8 tests) - Remove dead cj/gptel-backends defvar (duplicates cj/gptel--available-backends)
* fix(gptel): fix docstring warnings, rename model->string to model-to-stringCraig Jennings2026-03-061-15/+10
| | | | | | | | - Escape single quote in cj/ensure-gptel-backends docstring - Wrap cj/gptel--available-backends docstring to 80 chars - Add missing docstring to cj/gptel--model-to-string - Rename cj/gptel--model->string to cj/gptel--model-to-string - Mark stale model list task as DONE in todo.org
* fix(gptel): remove duplicate backend setter, fix commentary keybindingsCraig Jennings2026-03-061-10/+9
| | | | | | Remove redundant gptel-backend setq and orphaned section header. Fix commentary keybinding references: M-a → C-; a, correct buffer add (.) and rewrite (&) bindings.
* feat(gptel): update Claude models, fix default system promptCraig Jennings2026-03-061-5/+8
| | | | | | | | Update Anthropic model list to current: claude-opus-4-6, claude-sonnet-4-6, claude-haiku-4-5-20251001. Fix gptel--system-message not picking up the custom default.org directive (defvar set at load time before gptel-prompts replaces the default entry). Add cleanup tasks for ai-config, calibredb, and slack reaction workflow to todo.org.
* chore(ai-config): switch default gptel backend to ClaudeCraig Jennings2026-02-031-5/+4
|
* fix(ai-config): remove redundant autoload for cj/toggle-gptelCraig Jennings2026-01-241-2/+0
|
* feat:which-key: Add descriptive labels for custom keymapsCraig Jennings2025-10-271-0/+17
| | | | | | | | | | | | Enhance which-key integration by providing detailed descriptions for new key bindings across multiple modules. This improves the usability of custom keymaps by clarifying the purpose of each keybinding, making it easier for users to navigate and understand different menus and options available within the configuration. This update ensures that all custom keymaps now display a descriptive label in the which-key popup to explain their functionality, aiding users in identifying keymap purposes promptly.
* refactor: unify and simplify key binding setupsCraig Jennings2025-10-231-186/+186
| | | | | | | | Optimized key binding configurations across modules for consistency and reduced redundancy. Improved conditional requiring to handle errors gracefully in `music-config.el`, ensuring robustness across different machine environments. Eliminated comments clutter and adjusted function definitions to adhere to revised standards.
* chore: ai-config: migrate key bindings to keymap-set functionCraig Jennings2025-10-201-14/+14
| | | | | Switch key bindings from define-key to the keymap-set function for improved clarity and modern syntax within the AI operations keymap.
* changing repositoriesCraig Jennings2025-10-121-0/+419