Asi gh-emacs

gh-emacs Skill

install
source · Clone the upstream repo
git clone https://github.com/plurigrid/asi
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/plurigrid/asi "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/gh-emacs" ~/.claude/skills/plurigrid-asi-gh-emacs && rm -rf "$T"
manifest: skills/gh-emacs/SKILL.md
source content

gh-emacs Skill

Decentralized Emacs: Self-hosted elisp evaluation via GitHub Actions

Core Concept

GitHub Actions as a decentralized Emacs compute fabric:

  • Trigger elisp evaluation from local Emacs
  • GitHub runners execute
    emacs --batch --eval
  • Results stream back via workflow artifacts or gh CLI
  • Self-hosted runners = your own Emacs cloud
┌─────────────────┐    gh workflow run    ┌──────────────────┐
│  Local Emacs    │ ─────────────────────▶│  GitHub Actions  │
│  (controller)   │                       │  (emacs --batch) │
└────────┬────────┘                       └────────┬─────────┘
         │                                         │
         │  gh run watch / artifacts               │
         ◀─────────────────────────────────────────┘

Package Ecosystem

PackageStarsDescription
elisp-check~50⭐GitHub Action for elisp CI
actions-batch183⭐Time-sharing supercomputer on GH Actions
consult-gh148⭐Interactive gh CLI via Consult/Vertico
forge1.4k⭐Work with forges from Magit
ghub158⭐GitHub/GitLab/Gitea API client

consult-gh: Interactive gh CLI

(use-package consult-gh
  :after consult
  :custom
  (consult-gh-default-clone-directory "~/src/")
  (consult-gh-show-preview t)
  :config
  ;; Enable forge integration
  (require 'consult-gh-forge)
  
  ;; Enable embark actions
  (require 'consult-gh-embark)
  
  ;; Preview with syntax highlighting
  (setq consult-gh-issue-preview-major-mode 'gfm-mode)
  (setq consult-gh-file-preview-major-mode 'fundamental-mode))

Core Commands

CommandDescription
consult-gh-search-repos
Fuzzy search repositories
consult-gh-search-issues
Search issues/PRs across repos
consult-gh-search-code
Search code with preview
consult-gh-repo-clone
Clone with completion
consult-gh-repo-fork
Fork repository
consult-gh-pr-list
List PRs for current repo
consult-gh-issue-list
List issues for current repo

Embark Actions

(with-eval-after-load 'embark
  (defvar-keymap consult-gh-embark-repo-map
    :doc "Embark actions for consult-gh repos"
    "c" #'consult-gh-repo-clone
    "f" #'consult-gh-repo-fork
    "v" #'consult-gh-repo-view
    "w" #'consult-gh-repo-browse)
  
  (add-to-list 'embark-keymap-alist
               '(consult-gh-repo . consult-gh-embark-repo-map)))

forge: Magit Integration

(use-package forge
  :after magit
  :config
  ;; Configure forge database
  (setq forge-database-file (expand-file-name "forge-database.sqlite" user-emacs-directory))
  
  ;; Add GitHub Enterprise hosts
  ;; (add-to-list 'forge-alist '("github.company.com" "github.company.com/api/v3" 
  ;;                             "github.company.com" forge-github-repository))
  )

Forge Commands

BindingCommandDescription
N f f
forge-fetch
Fetch issues/PRs from forge
N c p
forge-create-pullreq
Create PR
N c i
forge-create-issue
Create issue
N b b
forge-browse-dwim
Browse at point
N l i
forge-list-issues
List issues
N l p
forge-list-pullreqs
List PRs

ghub: API Client

(use-package ghub
  :config
  ;; GraphQL queries
  (defun gh/viewer-repos ()
    "Fetch current user's repositories via GraphQL."
    (ghub-graphql
     "query { viewer { repositories(first: 100) { nodes { nameWithOwner } } } }")))

;; REST API example
(defun gh/repo-topics (owner repo)
  "Get topics for OWNER/REPO."
  (ghub-get (format "/repos/%s/%s/topics" owner repo)
            nil :accept "application/vnd.github.mercy-preview+json"))

Gay.jl Color Integration

(defvar gh-emacs-stream-colors
  '("#63B6F0" "#89DF91" "#CF6971" "#8E9EDF" "#C25828" "#ED6BDB" "#D1E598")
  "Stream 3 colors for GitHub operations (Bitter Lesson stream).")

(defun gh/colorize-by-state (state)
  "Return color based on PR/issue STATE."
  (pcase state
    ("open" "#89DF91")      ; green
    ("closed" "#CF6971")    ; red  
    ("merged" "#8E9EDF")    ; purple
    (_ "#63B6F0")))         ; blue default

(defun gh/consult-gh-colorize ()
  "Add Gay.jl colors to consult-gh results."
  (let ((inhibit-read-only t))
    (save-excursion
      (goto-char (point-min))
      (while (re-search-forward "\\[\\(open\\|closed\\|merged\\)\\]" nil t)
        (let* ((state (match-string 1))
               (color (gh/colorize-by-state state)))
          (put-text-property (match-beginning 0) (match-end 0)
                             'face `(:foreground ,color :weight bold)))))))

Transient Menu

(require 'transient)

(transient-define-prefix gh-emacs-transient ()
  "GitHub CLI interactive commands."
  ["Search"
   ("s r" "Repos" consult-gh-search-repos)
   ("s i" "Issues" consult-gh-search-issues)
   ("s c" "Code" consult-gh-search-code)]
  ["Current Repo"
   ("p l" "PR list" consult-gh-pr-list)
   ("i l" "Issue list" consult-gh-issue-list)
   ("p c" "Create PR" forge-create-pullreq :if (lambda () (forge-get-repository nil)))
   ("i c" "Create issue" forge-create-issue :if (lambda () (forge-get-repository nil)))]
  ["Actions"
   ("c" "Clone" consult-gh-repo-clone)
   ("f" "Fork" consult-gh-repo-fork)
   ("b" "Browse" forge-browse-dwim)]
  ["Forge"
   ("F" "Fetch" forge-fetch)
   ("L p" "List PRs" forge-list-pullreqs)
   ("L i" "List Issues" forge-list-issues)])

(global-set-key (kbd "C-c g h") 'gh-emacs-transient)

dwim-shell-command Recipes

(dwim-shell-command-define
 :name "gh: View PR diff"
 :command "gh pr diff <<*>>"
 :utils "gh"
 :documentation "View PR diff for number at point")

(dwim-shell-command-define
 :name "gh: Create release"
 :command "gh release create <<*>> --generate-notes"
 :utils "gh"
 :documentation "Create release with auto-generated notes")

(dwim-shell-command-define
 :name "gh: Run workflow"
 :command "gh workflow run <<f>>"
 :utils "gh"
 :documentation "Trigger workflow file")

(dwim-shell-command-define
 :name "gh: PR review"
 :command "gh pr review <<*>> --approve"
 :utils "gh"
 :documentation "Approve PR")

Org-mode Integration

(defun gh/org-capture-issue ()
  "Capture new GitHub issue from org-mode."
  (interactive)
  (let* ((title (read-string "Issue title: "))
         (body (org-export-string-as (buffer-substring-no-properties
                                      (region-beginning) (region-end))
                                     'gfm t)))
    (shell-command (format "gh issue create --title %s --body %s"
                           (shell-quote-argument title)
                           (shell-quote-argument body)))))

(add-to-list 'org-capture-templates
  '("g" "GitHub Issue" entry
    (file+headline "~/org/github.org" "Issues")
    "* TODO %^{Title} :github:
:PROPERTIES:
:REPO: %^{Repo}
:END:
%?
** Body
"
    :jump-to-captured t))

chatgpt-shell Integration

(defun gh/ai-review-pr (pr-number)
  "Use chatgpt-shell to review PR."
  (interactive "nPR number: ")
  (let* ((diff (shell-command-to-string (format "gh pr diff %d" pr-number)))
         (prompt (format "Review this pull request diff for:
1. Potential bugs or issues
2. Code style improvements
3. Security concerns
4. Performance implications

```diff
%s
```" diff)))
    (chatgpt-shell-send-to-buffer prompt)))

(defun gh/ai-generate-pr-description ()
  "Generate PR description from staged changes."
  (interactive)
  (let* ((diff (shell-command-to-string "git diff --staged"))
         (prompt (format "Generate a clear PR description for these changes:

```diff
%s

Format: Title, Summary, Key Changes (bullet points), Testing Notes" diff))) (chatgpt-shell-send-to-buffer prompt)))


## Workflow: Full Interactive Session

  1. C-c g h → Open gh-emacs transient
  2. s r → Search repos (consult-gh-search-repos)
  3. <select repo> → Embark: c=clone, f=fork, v=view
  4. After clone:
    • N f f → forge-fetch (get issues/PRs)
    • N l p → forge-list-pullreqs
    • <select PR> → M-x gh/ai-review-pr
  5. Create PR:
    • N c p → forge-create-pullreq
    • M-x gh/ai-generate-pr-description

## Neighbor Skills

- **xenodium-elisp**: chatgpt-shell, dwim-shell-command integration
- **gwern-emacs**: Self-experimentation (track PR review quality)
- **gh-skill-explorer**: Discover repos and SKILL.md patterns
- **code-review**: Automated code review skill
- **gay-mcp**: Deterministic colors for PR/issue states

## Decentralized Elisp Evaluation

### GitHub Actions Workflow Template

```yaml
# .github/workflows/elisp-eval.yml
name: Elisp Eval
on:
  workflow_dispatch:
    inputs:
      elisp_code:
        description: 'Elisp code to evaluate'
        required: true
        type: string
      emacs_version:
        description: 'Emacs version'
        default: '29.4'
        type: string

jobs:
  eval:
    runs-on: ubuntu-latest
    steps:
      - uses: purcell/setup-emacs@master
        with:
          version: ${{ inputs.emacs_version }}
      
      - name: Evaluate Elisp
        id: eval
        run: |
          result=$(emacs --batch --eval "${{ inputs.elisp_code }}" 2>&1)
          echo "result<<EOF" >> $GITHUB_OUTPUT
          echo "$result" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT
      
      - name: Upload Result
        uses: actions/upload-artifact@v4
        with:
          name: elisp-result
          path: |
            /tmp/elisp-output.txt

Self-Hosted Runner for Persistent Emacs

# .github/workflows/elisp-self-hosted.yml
name: Self-Hosted Elisp
on:
  workflow_dispatch:
    inputs:
      elisp_code:
        description: 'Elisp to evaluate'
        required: true

jobs:
  eval:
    runs-on: [self-hosted, emacs]  # Your labeled runner
    steps:
      - name: Eval with persistent Emacs daemon
        run: |
          emacsclient --eval "${{ inputs.elisp_code }}" \
            || emacs --batch --eval "${{ inputs.elisp_code }}"

Trigger from Local Emacs

(defun gh-emacs-remote-eval (code &optional repo)
  "Evaluate CODE on GitHub Actions runner.
REPO defaults to current repository."
  (interactive "sElisp code: ")
  (let* ((repo (or repo (gh-emacs--current-repo)))
         (escaped-code (shell-quote-argument code)))
    (async-shell-command
     (format "gh workflow run elisp-eval.yml -R %s -f elisp_code=%s"
             repo escaped-code))
    (message "Dispatched to %s - use gh run watch to monitor" repo)))

(defun gh-emacs-remote-eval-buffer ()
  "Send current buffer to remote Emacs for evaluation."
  (interactive)
  (let ((code (buffer-substring-no-properties (point-min) (point-max))))
    (gh-emacs-remote-eval
     (format "(progn %s)" code))))

(defun gh-emacs-remote-eval-region (start end)
  "Send region to remote Emacs for evaluation."
  (interactive "r")
  (gh-emacs-remote-eval
   (buffer-substring-no-properties start end)))

Fetch Remote Results

(defun gh-emacs-fetch-result (run-id &optional repo)
  "Fetch result artifact from RUN-ID."
  (interactive "nRun ID: ")
  (let* ((repo (or repo (gh-emacs--current-repo)))
         (cmd (format "gh run download %d -R %s -n elisp-result -D /tmp/gh-elisp"
                      run-id repo)))
    (shell-command cmd)
    (find-file "/tmp/gh-elisp/elisp-output.txt")))

(defun gh-emacs-watch-run (run-id &optional repo)
  "Watch RUN-ID and fetch result when complete."
  (interactive "nRun ID: ")
  (let* ((repo (or repo (gh-emacs--current-repo))))
    (async-shell-command
     (format "gh run watch %d -R %s && gh run download %d -R %s -n elisp-result"
             run-id repo run-id repo))))

Batch Queue Pattern (actions-batch inspired)

(defvar gh-emacs-batch-queue nil
  "Queue of elisp jobs to dispatch.")

(defun gh-emacs-batch-enqueue (code priority)
  "Add CODE to batch queue with PRIORITY."
  (push (list :code code :priority priority :time (current-time))
        gh-emacs-batch-queue)
  (setq gh-emacs-batch-queue
        (sort gh-emacs-batch-queue
              (lambda (a b) (> (plist-get a :priority)
                               (plist-get b :priority))))))

(defun gh-emacs-batch-dispatch-all ()
  "Dispatch all queued jobs to GitHub Actions."
  (interactive)
  (dolist (job gh-emacs-batch-queue)
    (gh-emacs-remote-eval (plist-get job :code)))
  (message "Dispatched %d jobs" (length gh-emacs-batch-queue))
  (setq gh-emacs-batch-queue nil))

Self-Hosted Runner Setup

# On your server/container:

# 1. Download runner
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64-2.311.0.tar.gz -L \
  https://github.com/actions/runner/releases/download/v2.311.0/actions-runner-linux-x64-2.311.0.tar.gz
tar xzf ./actions-runner-linux-x64-2.311.0.tar.gz

# 2. Configure with Emacs label
./config.sh --url https://github.com/OWNER/REPO \
  --token YOUR_TOKEN \
  --labels emacs,elisp-eval

# 3. Install Emacs
sudo apt-get install emacs

# 4. Start persistent daemon
emacs --daemon

# 5. Run the runner
./run.sh

Gay.jl Integration for Job Coloring

(defvar gh-emacs-job-colors
  '((queued . "#63B6F0")
    (in_progress . "#ED6BDB") 
    (completed . "#89DF91")
    (failure . "#CF6971")
    (cancelled . "#8E9EDF"))
  "Colors for job states.")

(defun gh-emacs-colorize-job (job-status)
  "Get color for JOB-STATUS."
  (alist-get (intern job-status) gh-emacs-job-colors "#D1E598"))

Transient Menu (Updated)

(transient-define-prefix gh-emacs-transient ()
  "Decentralized Emacs via GitHub Actions."
  ["Remote Eval"
   ("e e" "Eval expression" gh-emacs-remote-eval)
   ("e b" "Eval buffer" gh-emacs-remote-eval-buffer)
   ("e r" "Eval region" gh-emacs-remote-eval-region)]
  ["Jobs"
   ("j w" "Watch run" gh-emacs-watch-run)
   ("j f" "Fetch result" gh-emacs-fetch-result)
   ("j l" "List runs" gh-emacs-run-list)]
  ["Batch"
   ("b q" "Enqueue job" (lambda () (interactive) 
                          (gh-emacs-batch-enqueue (read-string "Code: ") 
                                                  (read-number "Priority: " 5))))
   ("b d" "Dispatch all" gh-emacs-batch-dispatch-all)]
  ["Local"
   ("s r" "Search repos" consult-gh-search-repos :if (lambda () (fboundp 'consult-gh-search-repos)))
   ("p l" "PR list" gh-emacs-pr-list)
   ("i l" "Issue list" gh-emacs-issue-list)])

(global-set-key (kbd "C-c g h") 'gh-emacs-transient)

Resources


End-of-Skill Interface

Commands

# gh CLI direct
gh repo list --limit 20
gh pr list --state open
gh issue create --title "Title" --body "Body"
gh run list --limit 10

# Just recipes
just gh-search-repos "query"
just gh-clone owner/repo
just gh-pr-create

Autopoietic Marginalia

The interaction IS the skill improving itself.

Every use of this skill is an opportunity for worlding:

  • MEMORY (-1): Record what was learned
  • REMEMBERING (0): Connect patterns to other skills
  • WORLDING (+1): Evolve the skill based on use

Add Interaction Exemplars here as the skill is used.