commit cfa64a51f05ad53e17b7ece424a1b0739898a046 Author: fenix Date: Mon Mar 16 12:28:01 2026 +0100 Let's evolve our skills with Claude Code help diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000..521dfa7 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,33 @@ +{ + "name": "fenix-skills", + "owner": { + "name": "Fénix", + "email": "fenix@local" + }, + "metadata": { + "description": "Fénix's Claude Code skills — Emacs integration (forked from xenodium/emacs-skills) + custom productivity tools", + "version": "1.0.0" + }, + "plugins": [ + { + "name": "fenix-tools", + "description": "Fénix's Emacs integration and productivity skills for Claude Code", + "source": "./", + "strict": false, + "skills": [ + "./skills/d2", + "./skills/describe", + "./skills/dired", + "./skills/emacsclient", + "./skills/extract-pdf-pages", + "./skills/file-links", + "./skills/gnuplot", + "./skills/highlight", + "./skills/mermaid", + "./skills/open", + "./skills/plantuml", + "./skills/select" + ] + } + ] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..0a4e612 --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +# Fenix Skills — Claude Code Plugin + +Custom Claude Code skills for Fénix's workflow. +Includes all [xenodium/emacs-skills](https://github.com/xenodium/emacs-skills) (forked & rebranded) plus Fénix's own productivity tools. + +--- + +## Plugin Structure + +``` +~/Emacs/fenix-skills/ +├── .claude-plugin/ +│ └── marketplace.json # Plugin manifest +├── README.md +└── skills/ + ├── d2/ # /d2 — Create D2 diagrams + ├── describe/ # /describe — Emacs describe-* lookups + ├── dired/ # /dired — Open files in dired buffer + ├── emacsclient/ # (auto) Always use emacsclient, not emacs + ├── extract-pdf-pages/ # /extract-pdf-pages — Extract page ranges with pdftk + ├── file-links/ # (auto) Format file refs as markdown #L links + ├── gnuplot/ # /gnuplot — Plot data with gnuplot + ├── highlight/ # /highlight — Highlight regions in Emacs + ├── mermaid/ # /mermaid — Create Mermaid diagrams + ├── open/ # /open — Open files in Emacs buffers + ├── plantuml/ # /plantuml — Create PlantUML diagrams + └── select/ # /select — Select regions in Emacs +``` + +## Registration + +Registered in `~/.claude/settings.json` as: + +```json +"extraKnownMarketplaces": { + "fenix-skills": { + "source": { + "source": "directory", + "path": "/home/fenix/Emacs/fenix-skills" + } + } +} +``` + +```json +"enabledPlugins": { + "fenix-tools@fenix-skills": true +} +``` + +## Usage + +All slash-command skills are available **system-wide** in any Claude Code session: + +| Skill | Invocation | Description | +|:---|:---:|:---| +| **d2** | `/d2` | Create diagrams with D2 | +| **describe** | `/describe` | Look up Emacs docs (functions, variables, keys) | +| **dired** | `/dired` | Open recent files in Emacs dired buffer | +| **emacsclient** | _(auto)_ | Always prefer `emacsclient` over `emacs` | +| **extract-pdf-pages** | `/extract-pdf-pages` | Extract page range from PDF via `pdftk` | +| **file-links** | _(auto)_ | Format file references as `file.py#L42` links | +| **gnuplot** | `/gnuplot` | Plot data with gnuplot | +| **highlight** | `/highlight` | Highlight line regions in Emacs buffers | +| **mermaid** | `/mermaid` | Create Mermaid diagrams | +| **open** | `/open` | Open files in Emacs (with optional line jump) | +| **plantuml** | `/plantuml` | Create PlantUML diagrams | +| **select** | `/select` | Select (mark) regions in Emacs buffers | + +## Origin + +- **Emacs skills** (d2, describe, dired, emacsclient, file-links, gnuplot, highlight, mermaid, open, plantuml, select) — forked from [xenodium/emacs-skills](https://github.com/xenodium/emacs-skills) by Alvaro Ramirez +- **extract-pdf-pages** — original, requires `pdftk` (`apt install pdftk`) + +## Requirements + +- GNU Emacs with running server daemon (`emacsclient` accessible) +- `pdftk` for PDF extraction skill +- Claude Code with plugins enabled diff --git a/skills/d2/SKILL.md b/skills/d2/SKILL.md new file mode 100644 index 0000000..adff015 --- /dev/null +++ b/skills/d2/SKILL.md @@ -0,0 +1,65 @@ +--- +name: d2 +description: 'This skill should be used when the user invokes "/d2" to create a diagram from the current context using D2 and output the resulting image path.' +tools: Bash +disable-model-invocation: true +--- + +# Create diagrams with D2 + +Create a diagram from the most recent interaction context using D2. Generate a PNG image and output it as a markdown image so it renders inline. + +## How to create a diagram + +1. Extract or derive diagrammable data from the current context. +2. If the Emacs foreground color and background mode are not already known from a previous diagram in this session, query them: + ```sh + emacsclient --eval '(face-foreground (quote default))' + emacsclient --eval '(frame-parameter nil (quote background-mode))' + ``` + The first returns a hex color like `"#eeffff"`. The second returns `dark` or `light`. Reuse both for all subsequent diagrams. +3. Write a D2 file to a temporary file using the foreground color. +4. Run D2 with `--theme 200` if background mode is `dark`, or `--theme 0` if `light`. +5. Output the result as a markdown image on its own line: + ``` + ![description](/tmp/agent-diagram-XXXX.png) + ``` + +```sh +# Use --theme 200 for dark, --theme 0 for light +d2 --theme 200 --pad 40 /tmp/agent-diagram-XXXX.d2 /tmp/agent-diagram-XXXX.png +``` + +## D2 template + +Use `--theme 200` for dark or `--theme 0` for light, based on the Emacs background mode. Apply the queried foreground color to `style.font-color` and `style.stroke` on nodes and edges. + +```d2 +direction: right + +node1: Label { + style.font-color: "#eeffff" + style.fill: "#2d3748" + style.stroke: "#eeffff" +} + +node2: Label { + style.font-color: "#eeffff" + style.fill: "#2d3748" + style.stroke: "#eeffff" +} + +node1 -> node2: label {style.stroke: "#eeffff"; style.font-color: "#eeffff"} +``` + +## Rules + +- Query the Emacs foreground color once per session and reuse it for all subsequent diagrams. Only query again if the color is not already known. +- Query the Emacs background mode once per session via `(frame-parameter nil 'background-mode)`. Use `--theme 200` for `dark` or `--theme 0` for `light`. Always use `--pad 40`. +- Always use a timestamp in the filename (e.g., `/tmp/agent-diagram-$(date +%s).png`). Never use descriptive names. +- Set the queried foreground color on `style.font-color` and `style.stroke` for all nodes and edges so the diagram is readable on the user's Emacs background. +- Use meaningful fill colors to distinguish different types of elements. +- After D2 runs successfully, output a markdown image (`![description](path)`) on its own line. +- Choose an appropriate layout direction and structure for the data. +- Include labels on edges when they add clarity. +- If no diagrammable data exists in the recent context, inform the user. diff --git a/skills/describe/SKILL.md b/skills/describe/SKILL.md new file mode 100644 index 0000000..3b734f1 --- /dev/null +++ b/skills/describe/SKILL.md @@ -0,0 +1,25 @@ +--- +name: describe +description: 'This skill should be used when the user invokes "/describe" to look up Emacs documentation via emacsclient.' +tools: Bash +disable-model-invocation: true +--- + +# Look up Emacs documentation + +Look up Emacs documentation using `emacsclient --eval` and summarize the findings. The query is searched across multiple mechanisms (function, variable, face, key binding, and apropos) in one call, returning all findings as a single string. + +First, locate `agent-skill-describe.el` which lives alongside this skill file at `skills/describe/agent-skill-describe.el` in the emacs-skills plugin directory. + +```sh +emacsclient --eval ' +(progn + (load "/path/to/skills/describe/agent-skill-describe.el" nil t) + (agent-skill-describe :query "dired-mark"))' +``` + +## Rules + +- Summarize the returned documentation for the user in the conversation. +- Locate `agent-skill-describe.el` relative to this skill file's directory. +- Run the `emacsclient --eval` command via the Bash tool. diff --git a/skills/describe/agent-skill-describe.el b/skills/describe/agent-skill-describe.el new file mode 100644 index 0000000..dd46484 --- /dev/null +++ b/skills/describe/agent-skill-describe.el @@ -0,0 +1,76 @@ +(require 'cl-lib) + +(cl-defun agent-skill-describe (&key query) + "Look up QUERY across multiple Emacs documentation mechanisms. + +Searches for QUERY as a function, variable, and via apropos, +returning all findings in a single string." + (let ((sym (intern-soft query)) + (sections nil)) + ;; Function documentation + (when (and sym (fboundp sym)) + (push (format "## Function: %s\n\n%s\n\nSignature: %s\n\n%s" + query + (cond ((subrp (symbol-function sym)) "Built-in function") + ((macrop sym) "Macro") + ((commandp sym) "Interactive command") + (t "Function")) + (or (help-function-arglist sym t) "()") + (or (documentation sym t) "No documentation available.")) + sections)) + ;; Variable documentation + (when (and sym (boundp sym)) + (let ((val (symbol-value sym))) + (push (format "## Variable: %s\n\nCurrent value: %s\n\n%s" + query + (let ((printed (format "%S" val))) + (if (> (length printed) 200) + (concat (substring printed 0 200) "...") + printed)) + (or (documentation-property sym 'variable-documentation t) + "No documentation available.")) + sections))) + ;; Face documentation + (when (and sym (facep sym)) + (push (format "## Face: %s\n\n%s" + query + (or (documentation-property sym 'face-documentation t) + "No documentation available.")) + sections)) + ;; Key binding (if it looks like a key sequence) + (when (string-match-p "\\`[CMSs]-\\|\\`<" query) + (let* ((keyseq (ignore-errors (kbd query))) + (binding (and keyseq (key-binding keyseq)))) + (when binding + (push (format "## Key binding: %s\n\n%s runs `%s'\n\n%s" + query query binding + (or (documentation binding t) + "No documentation available.")) + sections)))) + ;; Apropos matches (up to 20) + (let ((matches nil)) + (mapatoms + (lambda (s) + (when (and (string-match-p (regexp-quote query) (symbol-name s)) + (not (eq s sym)) + (or (fboundp s) (boundp s))) + (push (format " %s%s" + (symbol-name s) + (cond ((and (fboundp s) (boundp s)) + " [function, variable]") + ((fboundp s) " [function]") + (t " [variable]"))) + matches)))) + (when matches + (let ((sorted (sort matches #'string<))) + (push (format "## Related symbols\n\n%s%s" + (mapconcat #'identity (seq-take sorted 20) "\n") + (if (> (length sorted) 20) + (format "\n ... and %d more" (- (length sorted) 20)) + "")) + sections)))) + (if sections + (mapconcat #'identity (nreverse sections) "\n\n") + (format "No documentation found for \"%s\"." query)))) + +(provide 'agent-skill-describe) diff --git a/skills/dired/SKILL.md b/skills/dired/SKILL.md new file mode 100644 index 0000000..1e0d76f --- /dev/null +++ b/skills/dired/SKILL.md @@ -0,0 +1,37 @@ +--- +name: dired +description: 'This skill should be used when the user invokes "/dired" to open files from the latest interaction in an Emacs dired buffer via emacsclient.' +tools: Bash +disable-model-invocation: true +--- + +# Open files in Emacs dired + +Open files from the most recent interaction in an Emacs dired buffer using `emacsclient --eval`. Only include files relevant to the latest interaction (files just generated, edited, listed, or produced by the most recent tool output), not all files mentioned throughout the conversation. + +## Strategy + +Determine whether the relevant files all reside in the same directory or span multiple directories, then call `agent-skill-dired` accordingly. + +- **Same directory**: `:dir` is the directory, `:files` are basenames. Opens dired at that directory with the files marked in context. +- **Multiple directories**: `:dir` is the common ancestor, `:files` are relative paths. Creates a curated `*agent-files*` buffer with all files marked. + +First, locate `agent-skill-dired.el` which lives alongside this skill file at `skills/dired/agent-skill-dired.el` in the emacs-skills plugin directory. + +```sh +emacsclient --eval ' +(progn + (load "/path/to/skills/dired/agent-skill-dired.el" nil t) + (agent-skill-dired + :dir "/path/to/directory" + :files (quote ("file1.txt" + "file2.txt" + "file3.txt"))))' +``` + +## Rules + +- Use relative paths in `:files` relative to `:dir`. +- Locate `agent-skill-dired.el` relative to this skill file's directory. +- If no relevant files exist in the recent interaction, inform the user. +- Run the `emacsclient --eval` command via the Bash tool. diff --git a/skills/dired/agent-skill-dired.el b/skills/dired/agent-skill-dired.el new file mode 100644 index 0000000..dff139c --- /dev/null +++ b/skills/dired/agent-skill-dired.el @@ -0,0 +1,26 @@ +(require 'cl-lib) +(require 'dired) + +(cl-defun agent-skill-dired (&key dir files) + "Open a dired buffer at DIR with FILES marked. + +When all files share the same parent directory, DIR is that +directory and FILES are basenames shown in context. + +When files span multiple directories, DIR is the common ancestor +and FILES are relative paths. A curated `*agent-files*' buffer +is created instead." + (let ((same-dir-p (cl-every (lambda (f) (not (string-match-p "/" f))) files))) + (if same-dir-p + (progn + (dired dir) + (dired-unmark-all-marks) + (dolist (file files) + (dired-goto-file (expand-file-name file dir)) + (dired-mark 1))) + (let ((default-directory (file-name-as-directory dir))) + (dired (cons "*agent-files*" files)) + (dired-unmark-all-marks) + (dired-toggle-marks))))) + +(provide 'agent-skill-dired) diff --git a/skills/emacsclient/SKILL.md b/skills/emacsclient/SKILL.md new file mode 100644 index 0000000..e0ddf8e --- /dev/null +++ b/skills/emacsclient/SKILL.md @@ -0,0 +1,42 @@ +--- +name: emacsclient +description: 'Always use emacsclient instead of emacs. This applies to all Emacs operations: user requests, byte compilation, check-parens, running ERT tests, and any other elisp evaluation.' +tools: Bash +--- + +# Always use emacsclient + +The user has an Emacs server running. **All** Emacs operations must go through `emacsclient`, never `emacs` or `emacs --batch`. This includes both user-requested actions and agent-initiated operations like byte compilation, syntax checking, or running tests. + +## Examples + +- Open a file: `emacsclient --no-wait "/path/to/file"` +- Evaluate elisp: `emacsclient --eval '(some-function)'` +- Open at a line: `emacsclient --no-wait +42 "/path/to/file"` +- Byte compile a file: + ```sh + emacsclient --eval ' + (byte-compile-file "/path/to/file.el")' + ``` +- Check parentheses: + ```sh + emacsclient --eval ' + (with-temp-buffer + (insert-file-contents "/path/to/file.el") + (check-parens))' + ``` +- Run ERT tests: + ```sh + emacsclient --eval ' + (progn + (load "/path/to/test-file.el" nil t) + (ert-run-tests-batch-and-exit "pattern"))' + ``` + +## Rules + +- Always use `emacsclient`, never `emacs` or `emacs --batch`. +- Use `--no-wait` when opening files so the command returns immediately. +- Use `--eval` when evaluating elisp. +- Always format `--eval` elisp across multiple lines with proper indentation. +- Run `emacsclient` commands via the Bash tool. diff --git a/skills/extract-pdf-pages/SKILL.md b/skills/extract-pdf-pages/SKILL.md new file mode 100644 index 0000000..1dd62eb --- /dev/null +++ b/skills/extract-pdf-pages/SKILL.md @@ -0,0 +1,32 @@ +--- +name: extract-pdf-pages +description: 'Use when the user invokes "/extract-pdf-pages" to extract a page range from a PDF file using pdftk.' +tools: Bash +disable-model-invocation: true +--- + +# Extract PDF Pages + +Extract a page range from a PDF file into a new PDF using `pdftk`. + +## Usage + +When invoked, ask the user for (if not already provided): +1. **Source PDF** — absolute path to the input PDF file +2. **Page range** — start and end pages (e.g., `200-204`) +3. **Output filename** — name for the new PDF (defaults to current working directory) + +## Command + +```bash +pdftk "" cat - output "" +``` + +## Rules + +- Always use absolute paths for both source and output files. +- If the user provides only a filename (no directory), place the output in the current working directory. +- If the output file already exists, warn the user before overwriting. +- Support multiple extractions in a single invocation (run them in parallel). +- After extraction, confirm with file size: `ls -lh ""` +- Requires `pdftk` to be installed (`apt install pdftk`). diff --git a/skills/file-links/SKILL.md b/skills/file-links/SKILL.md new file mode 100644 index 0000000..7bc4e7e --- /dev/null +++ b/skills/file-links/SKILL.md @@ -0,0 +1,48 @@ +--- +name: file-links +description: 'When referencing files, format them as markdown links with line numbers using GitHub-style #L syntax.' +--- + +# Format file references as markdown links + +When referencing files in your output, always format them as markdown links. Use the GitHub-style `#L` fragment for line numbers. + +## Format + +With a line number: + +``` +[filename.el:42](relative/path/to/filename.el#L42) +``` + +With a line range: + +``` +[filename.el:42-50](relative/path/to/filename.el#L42-L50) +``` + +Without a line number: + +``` +[filename.el](relative/path/to/filename.el) +``` + +## Important + +- The link text uses `:` for line numbers (e.g., `filename.el:42`). +- The URL uses `#L` for line numbers (e.g., `filename.el#L42`). +- For ranges, the link text uses `-` (e.g., `filename.el:42-50`) and the URL uses `-L` (e.g., `filename.el#L42-L50`). +- The range must appear in both the link text and the URL. + +Do NOT do this: + +``` +[filename.el#L42-L50](filename.el#L42) +``` + +## Rules + +- Use paths relative to the project root. +- Include line numbers when they are relevant (e.g., error locations, function definitions, modified lines). +- Use line ranges when referring to a block of code. +- The link text should be the filename (or relative path if needed for clarity) followed by the line number. diff --git a/skills/gnuplot/SKILL.md b/skills/gnuplot/SKILL.md new file mode 100644 index 0000000..bdd0d72 --- /dev/null +++ b/skills/gnuplot/SKILL.md @@ -0,0 +1,60 @@ +--- +name: gnuplot +description: 'This skill should be used when the user invokes "/gnuplot" to plot data from the current context using gnuplot and output the resulting image path.' +tools: Bash +disable-model-invocation: true +--- + +# Plot data with gnuplot + +Plot data from the most recent interaction context using gnuplot. Generate a PNG image with a transparent background and output it as a markdown image so it renders inline. + +## How to plot + +1. Extract or derive plottable data from the current context. +2. If the Emacs foreground color is not already known from a previous plot in this session, query it: + ```sh + emacsclient --eval ' + (face-foreground (quote default))' + ``` + This returns a hex color like `"#eeffff"`. Reuse it for all subsequent plots. +3. Write a gnuplot script to a temporary file using that color. +4. Run gnuplot on the script. +5. Output the result as a markdown image on its own line: + ``` + ![description](/tmp/agent-plot-XXXX.png) + ``` + +```sh +gnuplot /tmp/agent-plot-XXXX.gp +``` + +## Gnuplot script template + +```gnuplot +set terminal pngcairo transparent enhanced size 800,500 +set output "/tmp/agent-plot-XXXX.png" + +FG = "#eeffff" # from emacsclient query +set border lc rgb FG +set key textcolor rgb FG +set xlabel textcolor rgb FG +set ylabel textcolor rgb FG +set title textcolor rgb FG +set xtics textcolor rgb FG +set ytics textcolor rgb FG + +# ... plot commands using the data ... +``` + +## Rules + +- Query the Emacs foreground color once per session and reuse it for all subsequent plots. Only query again if the color is not already known. +- Always use `pngcairo transparent` terminal for transparent background. +- Always use a timestamp in the filename (e.g., `/tmp/agent-plot-$(date +%s).png`). Never use descriptive names like `agent-plot-lorenz.png`. +- Use inline data (`$DATA << EOD ... EOD`) when practical. For large datasets, write a separate data file. +- After gnuplot runs successfully, output a markdown image (`![description](path)`) on its own line. +- Choose an appropriate plot type for the data (lines, bars, histogram, scatter, etc.). +- Include a title, axis labels, and a legend when they add clarity. +- Use `enhanced` text mode for subscripts/superscripts when needed. +- If no plottable data exists in the recent context, inform the user. diff --git a/skills/highlight/SKILL.md b/skills/highlight/SKILL.md new file mode 100644 index 0000000..2b26d67 --- /dev/null +++ b/skills/highlight/SKILL.md @@ -0,0 +1,39 @@ +--- +name: highlight +description: 'This skill should be used when the user invokes "/highlight" to highlight relevant regions in one or more files in Emacs via emacsclient.' +tools: Bash +disable-model-invocation: true +--- + +# Highlight regions in Emacs + +Highlight relevant regions in one or more files in Emacs using `emacsclient --eval`. Files are opened in a temporary read-only minor mode with highlighted overlays. The user presses `q` to exit the mode and remove all highlights in that buffer. + +Determine the relevant files and line ranges from the most recent interaction context. + +## How to highlight + +First, determine the path to `agent-skill-highlight.el`. It lives alongside this skill file at `skills/highlight/agent-skill-highlight.el` in the emacs-skills plugin directory. + +```sh +emacsclient --eval ' +(progn + (load "/path/to/skills/highlight/agent-skill-highlight.el" nil t) + (agent-skill-highlight + :files (quote (("/path/to/file1.el" + :regions ((:start 90 :lines 18) + (:start 114 :lines 49))) + ("/path/to/file2.el" + :regions ((:start 94 :lines 18)))))))' +``` + +- `:start` is the 1-indexed line number. +- `:lines` is the number of lines to highlight from that start line. +- Add as many files and regions as needed. + +## Rules + +- Use absolute paths for files. +- Locate `agent-skill-highlight.el` relative to this skill file's directory. +- If no relevant files or regions exist in the recent interaction, inform the user. +- Run the `emacsclient --eval` command via the Bash tool. diff --git a/skills/highlight/agent-skill-highlight.el b/skills/highlight/agent-skill-highlight.el new file mode 100644 index 0000000..6fe1a9f --- /dev/null +++ b/skills/highlight/agent-skill-highlight.el @@ -0,0 +1,53 @@ +(eval-when-compile + (require 'cl-lib)) +(require 'hi-lock) + +(defvar-local agent-skill-highlight--overlays nil) +(defvar-local agent-skill-highlight--was-read-only nil) + +(defun agent-skill-highlight--remove-overlays () + (mapc #'delete-overlay agent-skill-highlight--overlays) + (setq agent-skill-highlight--overlays nil)) + +(defun agent-skill-highlight-exit () + (interactive) + (agent-skill-highlight-mode -1)) + +(define-minor-mode agent-skill-highlight-mode + "Temporary read-only mode with highlighted regions. Press q to exit." + :lighter " Highlight" + :keymap (let ((map (make-sparse-keymap))) + (define-key map (kbd "q") #'agent-skill-highlight-exit) + map) + (if agent-skill-highlight-mode + (progn + (setq agent-skill-highlight--was-read-only buffer-read-only) + (read-only-mode 1) + (message "Press q to exit highlights")) + (agent-skill-highlight--remove-overlays) + (unless agent-skill-highlight--was-read-only + (read-only-mode -1)))) + +(cl-defun agent-skill-highlight (&key files) + "Highlight regions in FILES. + +FILES is a list of (FILE-PATH :regions REGIONS) where REGIONS is +a list of (:start LINE :lines COUNT)." + (dolist (file-spec files) + (let ((file-path (car file-spec)) + (regions (plist-get (cdr file-spec) :regions))) + (find-file file-path) + (dolist (region regions) + (let* ((start-line (plist-get region :start)) + (num-lines (plist-get region :lines)) + (ov (make-overlay + (progn (goto-char (point-min)) + (forward-line (1- start-line)) + (point)) + (progn (forward-line num-lines) + (point))))) + (overlay-put ov 'face 'hi-yellow) + (push ov agent-skill-highlight--overlays))) + (agent-skill-highlight-mode 1)))) + +(provide 'agent-skill-highlight) diff --git a/skills/mermaid/SKILL.md b/skills/mermaid/SKILL.md new file mode 100644 index 0000000..ee65f9f --- /dev/null +++ b/skills/mermaid/SKILL.md @@ -0,0 +1,86 @@ +--- +name: mermaid +description: 'This skill should be used when the user invokes "/mermaid" to create a diagram from the current context using Mermaid and output the resulting image path.' +tools: Bash +disable-model-invocation: true +--- + +# Create diagrams with Mermaid + +Create a diagram from the most recent interaction context using Mermaid. Generate a PNG image with a transparent background and output it as a markdown image so it renders inline. + +## How to create a diagram + +1. Extract or derive diagrammable data from the current context. +2. If the Emacs foreground color and background mode are not already known from a previous diagram in this session, query them: + ```sh + emacsclient --eval '(face-foreground (quote default))' + emacsclient --eval '(frame-parameter nil (quote background-mode))' + ``` + The first returns a hex color like `"#eeffff"`. The second returns `dark` or `light`. Reuse both for all subsequent diagrams. +3. Write a Mermaid file to a temporary file. +4. Write a JSON config file that overrides theme variables with the queried foreground color. Set `"theme"` to `"dark"` or `"default"` based on the background mode. +5. Run `mmdc` with `-t dark` if background mode is `dark`, or `-t default` if `light`. +6. Output the result as a markdown image on its own line: + ``` + ![description](/tmp/agent-diagram-XXXX.png) + ``` + +```sh +# Use -t dark for dark, -t default for light +PUPPETEER_EXECUTABLE_PATH=/opt/homebrew/bin/chromium mmdc \ + -i /tmp/agent-diagram-XXXX.mmd \ + -o /tmp/agent-diagram-XXXX.png \ + -t dark -b transparent --scale 2 \ + --configFile /tmp/agent-diagram-XXXX-config.json +``` + +## Mermaid config template + +Write this JSON config file to apply the Emacs foreground color. Replace `#eeffff` with the queried color. Set `"theme"` to `"dark"` or `"default"` based on the Emacs background mode. + +```json +{ + "theme": "dark", + "themeVariables": { + "primaryTextColor": "#eeffff", + "secondaryTextColor": "#eeffff", + "tertiaryTextColor": "#eeffff", + "primaryBorderColor": "#eeffff", + "lineColor": "#eeffff", + "textColor": "#eeffff", + "actorTextColor": "#eeffff", + "actorBorder": "#eeffff", + "signalColor": "#eeffff", + "signalTextColor": "#eeffff", + "labelTextColor": "#eeffff", + "loopTextColor": "#eeffff", + "noteTextColor": "#eeffff", + "noteBorderColor": "#eeffff", + "sectionTextColor": "#eeffff", + "titleColor": "#eeffff" + } +} +``` + +## Mermaid diagram template + +```mermaid +sequenceDiagram + participant a as Alice + participant b as Bob + a->>b: Hello + b-->>a: Hi back +``` + +## Rules + +- Query the Emacs foreground color once per session and reuse it for all subsequent diagrams. Only query again if the color is not already known. +- Query the Emacs background mode once per session via `(frame-parameter nil 'background-mode)`. Use `-t dark` for `dark` or `-t default` for `light`. Always use `-b transparent --scale 2`. +- Always use `PUPPETEER_EXECUTABLE_PATH=/opt/homebrew/bin/chromium` when invoking `mmdc`. +- Always write a JSON config file with `themeVariables` set to the queried foreground color and pass it via `--configFile`. +- Always use a timestamp in the filename (e.g., `/tmp/agent-diagram-$(date +%s).png`). Never use descriptive names. +- After mmdc runs successfully, output a markdown image (`![description](path)`) on its own line. +- Choose an appropriate diagram type for the data (sequence, flowchart, class, state, er, gantt, etc.). +- Include a title when it adds clarity. +- If no diagrammable data exists in the recent context, inform the user. diff --git a/skills/open/SKILL.md b/skills/open/SKILL.md new file mode 100644 index 0000000..3f8018c --- /dev/null +++ b/skills/open/SKILL.md @@ -0,0 +1,35 @@ +--- +name: open +description: 'This skill should be used when the user invokes "/open" to open files from the latest interaction in Emacs buffers via emacsclient.' +tools: Bash +disable-model-invocation: true +--- + +# Open files in Emacs + +Open files from the most recent interaction in Emacs buffers using `emacsclient --eval`. Only include files relevant to the latest interaction (files just generated, edited, listed, or produced by the most recent tool output), not all files mentioned throughout the conversation. + +## How to open + +First, locate `agent-skill-open.el` which lives alongside this skill file at `skills/open/agent-skill-open.el` in the emacs-skills plugin directory. + +Each file spec in `:files` is either a string (file path) or a plist with `:file` and optional `:line`. + +```sh +emacsclient --eval ' +(progn + (load "/path/to/skills/open/agent-skill-open.el" nil t) + (agent-skill-open + :files (quote ((:file "/path/to/file1.txt" + :line 42) + "/path/to/file2.txt" + "/path/to/file3.txt"))))' +``` + +## Rules + +- Use absolute paths for files. +- Use `:line` when a specific line is relevant (e.g., an error location or a newly added function). +- Locate `agent-skill-open.el` relative to this skill file's directory. +- If no relevant files exist in the recent interaction, inform the user. +- Run the `emacsclient --eval` command via the Bash tool. diff --git a/skills/open/agent-skill-open.el b/skills/open/agent-skill-open.el new file mode 100644 index 0000000..924a786 --- /dev/null +++ b/skills/open/agent-skill-open.el @@ -0,0 +1,18 @@ +(require 'cl-lib) + +(cl-defun agent-skill-open (&key files) + "Open FILES in Emacs buffers. + +FILES is a list of file specs. Each spec is either a string +\(file path) or a plist (:file PATH :line LINE)." + (dolist (spec files) + (if (stringp spec) + (find-file spec) + (let ((file (plist-get spec :file)) + (line (plist-get spec :line))) + (find-file file) + (when line + (goto-char (point-min)) + (forward-line (1- line))))))) + +(provide 'agent-skill-open) diff --git a/skills/plantuml/SKILL.md b/skills/plantuml/SKILL.md new file mode 100644 index 0000000..841c5df --- /dev/null +++ b/skills/plantuml/SKILL.md @@ -0,0 +1,82 @@ +--- +name: plantuml +description: 'This skill should be used when the user invokes "/plantuml" to create a diagram from the current context using PlantUML and output the resulting image path.' +tools: Bash +disable-model-invocation: true +--- + +# Create diagrams with PlantUML + +Create a diagram from the most recent interaction context using PlantUML. Generate a PNG image with a transparent background and output it as a markdown image so it renders inline. + +## How to create a diagram + +1. Extract or derive diagrammable data from the current context. +2. If the Emacs foreground color is not already known from a previous diagram in this session, query it: + ```sh + emacsclient --eval ' + (face-foreground (quote default))' + ``` + This returns a hex color like `"#eeffff"`. Reuse it for all subsequent diagrams. +3. Write a PlantUML file to a temporary file using that color. +4. Run PlantUML on the file. +5. Output the result as a markdown image on its own line: + ``` + ![description](/tmp/agent-diagram-XXXX.png) + ``` + +```sh +plantuml -tpng /tmp/agent-diagram-XXXX.puml +``` + +## PlantUML template + +```plantuml +@startuml +skinparam backgroundColor transparent +skinparam shadowing true +skinparam roundcorner 10 +skinparam defaultFontName "Helvetica" +skinparam defaultFontColor #eeffff + +' Set foreground color on all element types +skinparam titleFontColor #eeffff +skinparam sequenceLifeLineBorderColor #eeffff +skinparam sequenceArrowColor #eeffff +skinparam sequenceGroupHeaderFontColor #eeffff +skinparam sequenceGroupBorderColor #eeffff +skinparam sequenceDividerFontColor #eeffff +skinparam sequenceDividerBorderColor #eeffff +skinparam actorBorderColor #eeffff +skinparam actorFontColor #eeffff +skinparam participantFontColor #eeffff +skinparam participantBorderColor #eeffff +skinparam collectionsFontColor #eeffff +skinparam collectionsBorderColor #eeffff +skinparam noteFontColor #eeffff +skinparam noteBorderColor #eeffff +skinparam arrowFontColor #eeffff +skinparam classFontColor #eeffff +skinparam classBorderColor #eeffff +skinparam classAttributeFontColor #eeffff +skinparam packageFontColor #eeffff +skinparam packageBorderColor #eeffff +skinparam componentFontColor #eeffff +skinparam componentBorderColor #eeffff +skinparam interfaceFontColor #eeffff +skinparam interfaceBorderColor #eeffff + +' ... diagram content ... +@enduml +``` + +## Rules + +- Query the Emacs foreground color once per session and reuse it for all subsequent diagrams. Only query again if the color is not already known. +- Always use `skinparam backgroundColor transparent` for transparent background. +- Always use a timestamp in the filename (e.g., `/tmp/agent-diagram-$(date +%s).png`). Never use descriptive names. +- Set the queried foreground color on `defaultFontColor` and all relevant `skinparam` entries for borders, arrows, and text so the diagram is readable on the user's Emacs background. +- After PlantUML runs successfully, output a markdown image (`![description](path)`) on its own line. +- Choose an appropriate diagram type for the data (sequence, class, component, activity, state, etc.). +- Include a title when it adds clarity. +- If no diagrammable data exists in the recent context, inform the user. diff --git a/skills/select/SKILL.md b/skills/select/SKILL.md new file mode 100644 index 0000000..9eb49da --- /dev/null +++ b/skills/select/SKILL.md @@ -0,0 +1,42 @@ +--- +name: select +description: 'This skill should be used when the user invokes "/select" to open one or more files in Emacs and select a region relevant to the current discussion via emacsclient.' +tools: Bash +disable-model-invocation: true +--- + +# Select region in Emacs + +Open one or more files in Emacs and select (activate the region around) the code or text most relevant to the current discussion using `emacsclient --eval`. This allows the user to immediately act on the selection: narrow, copy, refactor, comment, etc. + +Determine the relevant files and line ranges from the most recent interaction context. + +## How to select + +First, locate `agent-skill-select.el` which lives alongside this skill file at `skills/select/agent-skill-select.el` in the emacs-skills plugin directory. + +```sh +emacsclient --eval ' +(progn + (load "/path/to/skills/select/agent-skill-select.el" nil t) + (agent-skill-select + :selections (quote (("/path/to/file1.el" + :start 10 + :end 25) + ("/path/to/file2.el" + :start 5 + :end 12)))))' +``` + +- `:start` is the 1-indexed start line. +- `:end` is the 1-indexed end line. +- The last file visited will have the visually active region. Other files have mark and point set (use `C-x C-x` to reactivate when switching to them). + +## Rules + +- Use absolute paths for files. +- Choose the region most relevant to the current discussion (e.g., a function just modified, a block with an error, code just generated). +- If no specific region is apparent, select the entire relevant function or block. +- Locate `agent-skill-select.el` relative to this skill file's directory. +- If no relevant files or regions exist in the recent interaction, inform the user. +- Run the `emacsclient --eval` command via the Bash tool. diff --git a/skills/select/agent-skill-select.el b/skills/select/agent-skill-select.el new file mode 100644 index 0000000..167491e --- /dev/null +++ b/skills/select/agent-skill-select.el @@ -0,0 +1,19 @@ +(require 'cl-lib) + +(cl-defun agent-skill-select (&key selections) + "Open files in Emacs and select a region in each. + +SELECTIONS is a list of (FILE :start LINE :end LINE)." + (dolist (sel selections) + (let ((file (car sel)) + (start (plist-get (cdr sel) :start)) + (end (plist-get (cdr sel) :end))) + (find-file file) + (goto-char (point-min)) + (forward-line (1- start)) + (set-mark (point)) + (forward-line (- end start)) + (end-of-line) + (activate-mark)))) + +(provide 'agent-skill-select)