Skip to content

The Saga Begins

I tried using Hyprland with Debian Linux a few months ago but the packages are a little out of date even when using the unstable (SID) distribution. As I had been using SID packages, then the Debian Linux system had become a little too unstable for day to day use. Interestingly I installed the KDE desktop and that worked very nicely before I started again from scratch.

Hyperland is kept up to date in Arch Linux although there is a lot more work to learn and maintain arch linux compared to Debian Linux. pacman command options are quite cryptic and I found them hard to memorise. Its also easy to break things doing an update (the downside of a rolling release based distribution).

Garuda Linux adds some nice tools on top of Arch Linux (and can also used the Nix sub-system) to simplify overall package upgrades and a range of other maintenance tools.

Supporting the editor-code-assistant/eca-nvim project via testing and feedback. Contributed a plugin spec for lazy.nvim plugin manager. The eca-nvim plugin is in very early stages, but I got a prompt to appear in Neovim 😃

Health and fitness continues to improve and the number of cycle rides and distance continues to increase. In July I completed 800km distance from 11 distinct rides, totalling around 35 hours of riding across the month (not including stoppage time).

Cycling maintenance seems to be complete for both the Ribble SL Disc and Bianchi 928 RC road bikes.

Aims:

  • DONE: new cleats for the black & red Lake shoes (and readjust cleats on white lake shoes)
  • DOING: upgrade OS on rangerone (Garuda Linux Hyprland flavor)
  • BLOCKED: continue eval of shuttle and rust experiments - on hold due to service outages
  • TODO: update Practicalli Clojure hot loading examples

Neovimλ

Editor Code assistant

Lazy plugin spec

Use Environment variable in spec to include key

Lazy plugin spec for eca-nvim

return {
  "editor-code-assistant/eca-nvim",
  dependencies = {
    "nvim-lua/plenary.nvim",
  },
  ft = { "clojure" },
  opts = {
    env = {
      -- OpenAI API key => https://platform.openai.com/settings/organization/api-keys
      OPENAI_API_KEY = ""
    },
  },
}

Open API keyλ

I didnt have a record of an account on OpenAPI, so signed up for a new account. I created a test account with the name Johnny Rocket and complementary email address. Sign up prompts for a key name (test key) and an organisation (Personal).

NOTE: for real work I will create an OpenAPI account within a Practicalli organisation

OpenAPI gave a very long API key, which I saved as the OPENAPI_API_KEY in .zshenv file for the moment. .zshrc is loaded when each new shell is loaded, so its a convenient location when testing.

Example API call provided when creating a key

curl https://api.openai.com/v1/responses \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPEN_API_KEY" \
  -d '{
    "model": "gpt-4o-mini",
    "input": "write a haiku about ai",
    "store": true
  }'

Response from OpenAI call

{
  "id": "resp_688f6eb0d0ac819b8e62d876f601a7cc072dad7021968688",
  "object": "response",
  "created_at": 1754230448,
  "status": "completed",
  "background": false,
  "error": null,
  "incomplete_details": null,
  "instructions": null,
  "max_output_tokens": null,
  "max_tool_calls": null,
  "model": "gpt-4o-mini-2024-07-18",
  "output": [
    {
      "id": "msg_688f6eb14420819ba0b284493085e1e0072dad7021968688",
      "type": "message",
      "status": "completed",
      "content": [
        {
          "type": "output_text",
          "annotations": [],
          "logprobs": [],
          "text": "Whispers of code wake,  \nThoughts of metal dreams unfold,  \nFuture's pulse in light."
        }
      ],
      "role": "assistant"
    }
  ],
  "parallel_tool_calls": true,
  "previous_response_id": null,
  "prompt_cache_key": null,
  "reasoning": {
    "effort": null,
    "summary": null
  },
  "safety_identifier": null,
  "service_tier": "default",
  "store": true,
  "temperature": 1.0,
  "text": {
    "format": {
      "type": "text"
    }
  },
  "tool_choice": "auto",
  "tools": [],
  "top_logprobs": 0,
  "top_p": 1.0,
  "truncation": "disabled",
  "usage": {
    "input_tokens": 13,
    "input_tokens_details": {
      "cached_tokens": 0
    },
    "output_tokens": 22,
    "output_tokens_details": {
      "reasoning_tokens": 0
    },
    "total_tokens": 35
  },
  "user": null,
  "metadata": {}
}%

OpenAI signup process also suggested Node.js and Python code snippets for making an API call

Nodejs example API call to OpenAI

Install the openai npm package first

npm install openai

Then save the following code into a file and run with node

import OpenAI from "openai";

const openai = new OpenAI({
  apiKey: "OPENAPI_API_KEY",
});

const response = openai.responses.create({
  model: "gpt-4o-mini",
  input: "write a haiku about ai",
  store: true,
});

response.then((result) => console.log(result.output_text));

Python example API call to OpenAI

Install the openai python package in your Python virtual environment

pip install openai

Then save the following code into a file and run with node

from openai import OpenAI

client = OpenAI(
  api_key="OPENAI_API_KEY"
)

response = client.responses.create(
  model="gpt-4o-mini",
  input="write a haiku about ai",
  store=True,
)

print(response.output_text);

Submit a Pull Request to update the readmeλ

Plugin spec to install eca-nvim via lazy.nvim. The spec will lazy load the plugin, loading only when a Clojure related file is opened. Create an environment variable within your operating system called OPENAI_API_KEY and set it to the value of your OpenAI key.

return {
  "editor-code-assistant/eca-nvim",
  dependencies = {
    "nvim-lua/plenary.nvim",
  },
  ft = { "clojure" },  -- lazy load, only run when Clojure file type is opened
  opts = {
    env = {OPENAI_API_KEY = vim.env.OPENAI_API_KEY},  -- Uses OS Environment Variable
  },
}

A refactor of the code on Sunday changes the plugin config a little, but its a very small change to the key names and structure. The project maintainer already updated the Lazy.nvim plugin spec with the change in the readme.

return {
  "editor-code-assistant/eca-nvim",
  dependencies = {
    "nvim-lua/plenary.nvim",
  },
  ft = { "clojure" },  -- lazy load, only run when Clojure file type is opened
  opts = {
    server = {
      spawn_args = {
        env = {OPENAI_API_KEY = vim.env.OPENAI_API_KEY},  -- set key from OS Env Var of same name
      }
    },
  },
}

Although the prompt shows in a new buffer, I cannot confirm if it is actually working correctly. The prompt returns an empty reply when entering / and pressing Enter.

The eca-nvim plugin has downloaded the eca.jar and started eca server, according to the ps ax | grep java command at the Linux terminal.

1877094 ?        Ssl    0:09 /usr/bin/java -jar /home/practicalli/.local/share/nvim-astro5/lazy/eca-nvim/lua/eca-nvim/eca.jar server

Checking my account on OpenAI website, I have already used all the free 'token' allowance. I used 26 tokens by calling a couple of curl test API calls. I would have to supply a payment method to make use of the prompt for ECA.

Git caught a naughty pushλ

I accidently copied the OPENAI key to the journal but Git picked it up before I pushed.

❯ git push --force-with-lease=main
Enumerating objects: 12, done.
Counting objects: 100% (12/12), done.
Delta compression using up to 12 threads
Compressing objects: 100% (7/7), done.
Writing objects: 100% (7/7), 7.42 KiB | 1.24 MiB/s, done.
Total 7 (delta 5), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (5/5), completed with 5 local objects.
remote: error: GH013: Repository rule violations found for refs/heads/main.
remote:
remote: - GITHUB PUSH PROTECTION
remote:   —————————————————————————————————————————
remote:     Resolve the following violations before pushing again
remote:
remote:     - Push cannot contain secrets
remote:
remote:
remote:      (?) Learn how to resolve a blocked push
remote:      https://docs.github.com/code-security/secret-scanning/working-with-secret-scanning-and-push-protection/working-with-push-protection-from-the-command-line#resolving-a-blocked-push
remote:
remote:      (?) This repository does not have Secret Scanning enabled, but is eligible. Enable Secret Scanning to view and manage detected secrets.
remote:      Visit the repository settings page, https://github.com/practicalli/journal/settings/security_analysis
remote:
remote:
remote:       —— OpenAI API Key ————————————————————————————————————
remote:        locations:
remote:          - commit: 6a5b9f4d20c2368fe6be9a3e6a87a1655f1daafd
remote:            path: docs/posts/2025/july/28.md:169
remote:          - commit: 6a5b9f4d20c2368fe6be9a3e6a87a1655f1daafd
remote:            path: docs/posts/2025/july/28.md:194
remote:
remote:        (?) To push, remove secret from commit(s) or follow this URL to allow the secret.
remote:        https://github.com/practicalli/journal/security/secret-scanning/unblock-secret/30nQLUA1pDemtWD422AmHbvBIpb
remote:
remote:
remote:
To github.com:practicalli/journal
 ! [remote rejected]   main -> main (push declined due to repository rule violations)
error: failed to push some refs to 'github.com:practicalli/journal'

Package updateλ

Once the eca-nvim plugin spec was added, a plugin update was run via SPC p a.

Sunday 3rd August 2025 at 14:42

  Breaking Changes (1)
    ● catppuccin 3.2ms colorscheme catppuccin-mocha  astrocore
        f67971c fix!: align treesitter colors with vscode/updated style guide (#804) (2 days ago)
        931a129 fix(blink-cmp): pmenu border bg (#897) (2 days ago)
        b313c96 feat!: add global floating window options (#892) (2 days ago)
        cb56659 chore(main): release 1.11.0 (#859) (3 days ago)
        5b9719f chore: revert "feat!: add global floating window options" (#891) (3 days ago)
        8345847 feat!: add global floating window options (#885) (3 days ago)
        6d0d9ae fix: add missing table based integrations to fix auto integration detection (#890) (3 days ago)
        94f6e8a fix(mini.picker): border and match styling (#889) (5 days ago)
        5fa8d24 fix: enable auto detected integrations with table options (#886) (5 days ago)
        25f356a docs(README): tidy up (#782) (5 days ago)
        702d15f docs: auto generate vimdoc (6 days ago)
        18e5870 docs: document `auto_integrations` option (#884) (6 days ago)


  Updated (5)
    ● astrocommunity 0.34ms  start
        8930cf0 refactor(catppuccin): leverage auto_integrations in pack (#1598) (27 hours ago)
        be0c1c8 fix(coc-nvim): resolve <CR> conflicts and enable proper insert-mode completion (#1552) (28 hours ago)

    ○ grug-far.nvim  GrugFar  GrugFarWithin
        4825177 fixing path provider file path escaping (6 days ago)

    ● neogit 36.7ms îȘ† User AstroGitFile
        a1bcc7b Merge pull request #1783 from AndrewChmutov/fix-reword-trim (5 days ago)
        0a97695 fix: disable commit message trimming on `reword` (8 days ago)

    ○ octo.nvim  Octo
        1d8abaf Merge pull request #1109 from kfpotts1/kenny/fix_pr_review_diff_merge_base (4 days ago)
        b4767a3 Merge branch 'master' into kenny/fix_pr_review_diff_merge_base (4 days ago)
        c4265a6 cast for typing on union typed node obj (3 weeks ago)
        00a37c0 fix typing (3 weeks ago)
        a7359ab Merge branch 'master' into kenny/fix_pr_review_diff_merge_base (3 weeks ago)
        c93d709 remove extra line (4 weeks ago)
        180fa9b Merge branch 'master' into kenny/fix_pr_review_diff_merge_base (4 weeks ago)
        7f38c28 inline fetch merge base + use create callback (4 weeks ago)
        17ca872 remove redef (6 weeks ago)
        14221c8 consolidate with a common pull request helper (6 weeks ago)
        7e98e5f also fix for PR from current branch (6 weeks ago)
        f2ea431 remove fallback for old behavior w/o callback (6 weeks ago)
        09006df fix pre review diff to properly use merge base instead of current base head (6 weeks ago)

    ○ schemastore.nvim
        34e3958 Update SchemaStore catalog (17 hours ago)
        3cd1c72 Update SchemaStore catalog (5 days ago)


  Installed (1)
    ○ eca-nvim

Ascii Generatorsλ

Large Headings:

Ascii Generator - Calvin S for large headings

Ascii Generator - from image file

Garuda Linuxλ

Trying out Garuda Linux on the Lenovo Thinkpad P14s (rangerone), using the hyprland flavour.

Created a USB memory stick

sudo dd if=garuda-linux-hyprland.iso of=/dev/sda bs=1M status=progress

Booted on the USB stick by selecting Enter on the bios prompt, then F12 to select boot device - USB Generic disk

The Live image ran and the Hyprland 'desktop' and a quick test of the essentials was run (ethernet, bluetooth mouse, external keyboard, browser & 1password extension).

The live image has a launcher for the Calamaris install tool (the installer fails if arch packages were updated before running the install).

Post install tasksλ

  • comment kitty as float in hyprland config

  • add Practicalli Kitty configuration (in Practicalli dotfiles)

  • add Practicalli Starship configuration (in Practicalli dotfiles)

  • add Practicalli shell-aliases configuration (in Practicalli dotfiles)

  • install github-cli package to use .ssh keys effectively without keyring (and without having to mess around with ssh-agent, which needs a different command for the fish shell)

sudo pacman -Sy github-cli

ssh-agent with fish shellλ

The commands used with Zsh to start and connect to the ssh-agent do not work for fish shell.

I forgot which key to add, but got a warning to remind me that it is the private key file that should be used.

λ eval (ssh-agent -c)
Agent pid 79802

λ ssh-add ~/.ssh/engineering.pub    ## added wrong key
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/home/practicalli/.ssh/engineering.pub' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.

λ ssh-add ~/.ssh/engineering
Enter passphrase for /home/practicalli/.ssh/engineering:
Identity added: /home/practicalli/.ssh/engineering (engineering@practical.li)

This approach is only valid for a specific terminal session.

Ideally there would be a key ring option I could use with Hyprland, which would unlock my SSH keys automatically when I successfully login.

Another approach to try is to in the shell resource file, i.e. .zshrc .bashrc (not sure about fish shell yet).

export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent.socket"

Ensure ssh-agent package installed and enabled.

Use ssh-add to add an SSH key, specifying the duration for the key to remain available, e.g. use SSH key for 24 hours without requiring the password.

ssh-add -t 24h ~/.ssh/engineering

Or set ssh-agent as a systemd process or see the Arch Linux article on starting ssh-agent with systemd user

Or generate and add an SSH Key to a Yubikey hardware security device. This should allow you to tap the Yubikey when signing a commit (or tag) and pushing commit(s) to a remove service.

A PIN must be set on the Yubikey to add the SSH Key, although this greatly simplifies using the key on any other machine (as the Yubikey contains the private key and can generate a suitable link on a new computer).

Or keychain to only need to enter the SSH key passphrase when starting/rebooting the operating system.

GitHub CLIλ

In theory I should be able to use the GitHub CLI as a way to authenticate with GitHub using my existing SSH keys.

Cyclingλ

I had modified the position of the cleats on my white Lake shoes a couple of weeks ago needed to adjust them again as my ankle and side of my calf was feeling a little strained.

I used the Ergon bike ergonomics cleat positioning tool to ensure the cleats were in the optimum position.

  • The ball of each foot should be over the spindle of each pedal. The cleat tool provides a guide to line this up.

  • The shoe should be parallel to the pedal & bike

Whilst the Q-factor is supposed to be very narrow for optimum aerodynamics, I adjusted the cleats to be as wide as possible without compromising the above to positions. I have a larger frame so a smaller Q-factor isnt good for my foot and leg position.

At a previous bike fit, cleat spacers were recommended to help with foot position. However, the more I have been riding the less the spacers feel right. I refitted the cleats without the spacers.

A new chain was fitted to the Ribble Endurance SL and it is very smooth now, as well as being very quiet.


Thank you.

🌐 Practical.li Website

Practical.li GitHub Org practicalli-johnny profile

@practicalli@clj.social @practical_li