Compare commits

..

2 commits

Author SHA1 Message Date
Geir Okkenhaug Jerstad
ae5b0cf8d0 Update Claude Task Master AI package with correct hashes
- Add correct source hash: sha256-hYXIvhXx1qJefnEbsllwm7TATPU8ihVV6XchaMjTACQ=
- Add correct npm dependencies hash: sha256-WjPFg/jYTbxrKNzTyqb6e0Z+PLPg6O2k8LBIELwozo8=
- Add dontNpmBuild = true to skip build phase
- Package builds successfully and creates binaries: task-master, task-master-ai, task-master-mcp
2025-06-14 15:42:55 +02:00
Geir Okkenhaug Jerstad
a17326a72e Add Claude Task Master AI package and documentation
- Add Nix package for task-master-ai in packages/claude-task-master-ai.nix
- Update packages/default.nix to export the new package
- Add comprehensive documentation for packaging and MCP integration
- Add guile scripting solution documentation
2025-06-14 15:40:23 +02:00
4 changed files with 481 additions and 0 deletions

View file

@ -0,0 +1,333 @@
# Replacing Bash with Guile Scheme for Home Lab Tools
This document outlines a proposal to migrate the `home-lab-tools` script from Bash to GNU Guile Scheme. This change aims to address the increasing complexity of the script and leverage the benefits of a more powerful programming language.
## 1. Introduction: Why Guile Scheme?
GNU Guile is the official extension language for the GNU Project. It is an implementation of the Scheme programming language, a dialect of Lisp. Using Guile for scripting offers several advantages over Bash, especially as scripts grow in size and complexity.
Key reasons for considering Guile:
* **Expressiveness and Power:** Scheme is a full-fledged programming language with features like first-class functions, macros, and a rich standard library. This allows for more elegant and maintainable solutions to complex problems.
* **Better Error Handling:** Guile provides robust error handling mechanisms (conditions and handlers) that are more sophisticated than Bash's `set -e` and trap.
* **Modularity:** Guile supports modules, making it easier to organize code into reusable components.
* **Data Manipulation:** Scheme excels at handling structured data, which can be beneficial for managing configurations or parsing output from commands.
* **Readability (for Lisp programmers):** While Lisp syntax can be initially unfamiliar, it can lead to very clear and concise code once learned.
* **Interoperability:** Guile can easily call external programs and libraries, and can be extended with C code if needed.
## 2. Advantages over Bash for `home-lab-tools`
Migrating `home-lab-tools` from Bash to Guile offers specific benefits:
* **Improved Logic Handling:** Complex conditional logic, loops, and function definitions are more naturally expressed in Guile. The current Bash script uses case statements and string comparisons extensively, which can become unwieldy.
* **Structured Data Management:** Machine definitions, deployment modes, and status information could be represented as Scheme data structures (lists, association lists, records), making them easier to manage and query.
* **Enhanced Error Reporting:** More descriptive error messages and better control over script termination in case of failures.
* **Code Reusability:** Functions for common tasks (e.g., SSHing to a machine, running `nixos-rebuild`) can be more cleanly defined and reused.
* **Easier Testing:** Guile's nature as a programming language makes it more amenable to unit testing individual functions or modules.
* **Future Extensibility:** Adding new commands, machines, or features will be simpler and less error-prone in a more structured language.
## 3. Setting up Guile
Guile is often available through system package managers. On NixOS, it can be added to your environment or system configuration.
```nix
# Example: Adding Guile to a Nix shell
nix-shell -p guile
```
A Guile script typically starts with a shebang line:
```scheme
#!/usr/bin/env guile
!#
```
The `!#` at the end is a Guile-specific convention that allows the script to be both executable and loadable into a Guile REPL.
## 4. Basic Guile Scripting Concepts
* **S-expressions:** Code is written using S-expressions (Symbolic Expressions), which are lists enclosed in parentheses, e.g., `(function arg1 arg2)`.
* **Definitions:** `(define variable value)` and `(define (function-name arg1 arg2) ...body...)`.
* **Procedures (Functions):** Core of Guile programming.
* **Control Flow:** `(if condition then-expr else-expr)`, `(cond (test1 expr1) (test2 expr2) ... (else else-expr))`, `(case ...)`
* **Modules:** `(use-modules (ice-9 popen))` for using libraries.
## 5. Interacting with the System
Guile provides modules for system interaction:
* **(ice-9 popen):** For running external commands and capturing their output (similar to backticks or `$(...)` in Bash).
* `open-pipe* command mode`: Opens a pipe to a command.
* `get-string-all port`: Reads all output from a port.
* **(ice-9 rdelim):** For reading lines from ports.
* **(ice-9 filesys):** For file system operations (checking existence, deleting, etc.).
* `file-exists? path`
* `delete-file path`
* **(srfi srfi-1):** List processing utilities.
* **(srfi srfi-26):** `cut` for partial application, useful for creating specialized functions.
* **Environment Variables:** `(getenv "VAR_NAME")`, `(setenv "VAR_NAME" "value")`.
**Example: Running a command**
```scheme
(use-modules (ice-9 popen))
(define (run-command . args)
(let* ((cmd (string-join args " "))
(port (open-pipe* cmd OPEN_READ)))
(let ((output (get-string-all port)))
(close-pipe port)
output)))
(display (run-command "echo" "Hello from Guile"))
(newline)
```
## 6. Error Handling
Guile uses a condition system for error handling.
* `catch`: Allows you to catch specific types of errors.
* `throw`: Raises an error.
```scheme
(use-modules (ice-9 exceptions))
(catch #t
(lambda ()
(display "Trying something that might fail...
")
;; Example: Force an error
(if #t (error "Something went wrong!"))
(display "This won't be printed if an error occurs above.
"))
(lambda (key . args)
(format (current-error-port) "Caught an error: ~a - Args: ~a
" key args)
#f)) ; Return value indicating an error was caught
```
For `home-lab-tools`, this means we can provide more specific feedback when a deployment fails or a machine is unreachable.
## 7. Modularity and Code Organization
Guile's module system allows splitting the code into logical units. For `home-lab-tools`, we could have modules for:
* `lab-config`: Machine definitions, paths.
* `lab-deploy`: Functions related to deploying configurations.
* `lab-ssh`: SSH interaction utilities.
* `lab-status`: Functions for checking machine status.
* `lab-utils`: General helper functions, logging.
**Example module structure:**
```scheme
;; file: lab-utils.scm
(define-module (lab utils)
#:export (log success warn error))
(define blue "")
(define nc "")
(define (log msg)
(format #t "~a[lab]~a ~a
" blue nc msg))
;; ... other logging functions
```
```scheme
;; file: main-lab-script.scm
#!/usr/bin/env guile
!#
(use-modules (lab utils) (ice-9 popen))
(log "Starting lab script...")
;; ... rest of the script
```
## 8. Example: Rewriting a Small Part of `home-lab-tools.nix` (Conceptual)
Let's consider the `log` function and a simplified `deploy_machine` for local deployment.
**Current Bash:**
```bash
BLUE=''
NC='' # No Color
log() {
echo -e "''${BLUE}[lab]''${NC} $1"
}
deploy_machine() {
local machine="$1"
# ...
if [[ "$machine" == "congenital-optimist" ]]; then
log "Deploying $machine (mode: $mode) locally"
sudo nixos-rebuild $mode --flake "$HOMELAB_ROOT#$machine"
fi
# ...
}
```
**Conceptual Guile Scheme:**
```scheme
;; main-lab-script.scm
#!/usr/bin/env guile
!#
(use-modules (ice-9 popen)
(ice-9 rdelim)
(ice-9 pretty-print)
(ice-9 exceptions)
(srfi srfi-1)) ;; For list utilities like `string-join`
;; Configuration (could be in a separate module)
(define homelab-root "/home/geir/Home-lab")
;; Color Definitions
(define RED "")
(define GREEN "")
(define YELLOW "")
(define BLUE "")
(define NC "")
;; Logging functions
(define (log level-color level-name message)
(format #t "~a[~a]~a ~a
" level-color level-name NC message))
(define (info . messages)
(log BLUE "lab" (apply string-append (map (lambda (m) (if (string? m) m (format #f "~s" m))) messages))))
(define (success . messages)
(log GREEN "lab" (apply string-append (map (lambda (m) (if (string? m) m (format #f "~s" m))) messages))))
(define (warn . messages)
(log YELLOW "lab" (apply string-append (map (lambda (m) (if (string? m) m (format #f "~s" m))) messages))))
(define (err . messages)
(log RED "lab" (apply string-append (map (lambda (m) (if (string? m) m (format #f "~s" m))) messages)))
(exit 1)) ;; Exit on error
;; Function to run shell commands and handle output/errors
(define (run-shell-command . command-parts)
(let ((command-string (string-join command-parts " ")))
(info "Executing: " command-string)
(let ((pipe (open-pipe* command-string OPEN_READ)))
(let loop ((lines '()))
(let ((line (read-line pipe)))
(if (eof-object? line)
(begin
(close-pipe pipe)
(reverse lines)) ;; Return lines in order
(begin
(display line) (newline) ;; Display live output
(loop (cons line lines)))))))
;; TODO: Add proper error checking based on exit status of the command
;; For now, we assume success if open-pipe* doesn't fail.
;; A more robust solution would check `close-pipe` status or use `system*`.
))
;; Simplified deploy_machine
(define (deploy-machine machine mode)
(info "Deploying " machine " (mode: " mode ")")
(cond
((string=? machine "congenital-optimist")
(info "Deploying " machine " locally")
(catch #t
(lambda ()
(run-shell-command "sudo" "nixos-rebuild" mode "--flake" (string-append homelab-root "#" machine))
(success "Successfully deployed " machine))
(lambda (key . args)
(err "Failed to deploy " machine ". Error: " key " Args: " args))))
;; Add other machines here
(else
(err "Unknown machine: " machine))))
;; Main script logic (parsing arguments, calling functions)
(define (main args)
(if (< (length args) 3)
(begin
(err "Usage: <script> deploy <machine> [mode]")
(exit 1))
(let ((command (cadr args))
(machine (caddr args))
(mode (if (> (length args) 3) (cadddr args) "boot")))
(cond
((string=? command "deploy")
(deploy-machine machine mode))
;; Add other commands like "status", "update"
(else
(err "Unknown command: " command))))))
;; Run the main function with command-line arguments
;; (cdr args) to skip the script name itself
(main (program-arguments))
```
## 9. Creating Terminal User Interfaces (TUIs) with Guile-Ncurses
For more interactive command-line tools, Guile Scheme can be used to create Text User Interfaces (TUIs). The primary library for this is `guile-ncurses`.
**Guile-Ncurses** is a GNU project that provides Scheme bindings for the ncurses library, including its components for forms, panels, and menus. This allows you to build sophisticated text-based interfaces directly in Guile.
**Key Features:**
* **Windowing:** Create and manage multiple windows on the terminal.
* **Input Handling:** Process keyboard input, including special keys.
* **Text Attributes:** Control colors, bolding, underlining, and other text styles.
* **Forms, Panels, Menus:** Higher-level components for building complex interfaces.
**Getting Started with Guile-Ncurses:**
1. **Installation:** `guile-ncurses` would typically be installed via your system's package manager or built from source. If you are using NixOS, you would look for a Nix package for `guile-ncurses`.
```nix
# Example: Adding guile-ncurses to a Nix shell (package name might vary)
nix-shell -p guile guile-ncurses
```
2. **Using in Code:**
You would use the `(ncurses curses)` module (and others like `(ncurses form)`, `(ncurses menu)`, `(ncurses panel)`) in your Guile script.
```scheme
(use-modules (ncurses curses))
(define (tui-main stdscr)
;; Initialize ncurses
(cbreak!) ;; Line buffering disabled, Pass on ever char
(noecho!) ;; Don't echo() while we do getch
(keypad stdscr #t) ;; Enable Fx keys, arrow keys etc.
(addstr "Hello, Guile Ncurses TUI!")
(refresh)
(getch) ;; Wait for a key press
(endwin)) ;; End curses mode
;; Initialize and run the TUI
(initscr)
(tui-main stdscr)
```
**Resources:**
* **Guile-Ncurses Project Page:** [https://www.nongnu.org/guile-ncurses/](https://www.nongnu.org/guile-ncurses/)
* **Guile-Ncurses Manual:** [https://www.gnu.org/software/guile-ncurses/manual/](https://www.gnu.org/software/guile-ncurses/manual/)
Integrating `guile-ncurses` can significantly enhance the user experience of your `home-lab-tools` script, allowing for interactive menus, status dashboards, and more complex user interactions beyond simple command-line arguments and output.
## 10. Conclusion and Next Steps
Migrating `home-lab-tools` to Guile Scheme offers a path to a more maintainable, robust, and extensible solution. While there is a learning curve for Scheme, the long-term benefits for managing a complex set of administration tasks are significant.
**Next Steps:**
1. **Install Guile:** Ensure Guile is available in the development environment.
2. **Start Small:** Begin by porting one command or a set of utility functions (e.g., logging, SSH wrappers).
3. **Learn Guile Basics:** Familiarize with Scheme syntax, common procedures, and modules. The Guile Reference Manual is an excellent resource.
4. **Develop Incrementally:** Port functionality piece by piece, testing along the way.
5. **Explore Guile Libraries:** Investigate Guile libraries for argument parsing (e.g., `(gnu cmdline)`), file system operations, and other needs.
6. **Refactor and Organize:** Use Guile's module system to keep the codebase clean and organized.
This transition will require an initial investment in learning and development but promises a more powerful and sustainable tool for managing the home lab infrastructure.

View file

@ -0,0 +1,31 @@
{
lib,
buildNpmPackage,
fetchurl,
nodejs,
}:
buildNpmPackage rec {
pname = "task-master-ai";
version = "0.16.2";
src = fetchurl {
url = "https://github.com/eyaltoledano/claude-task-master/archive/refs/tags/v${version}.tar.gz";
hash = "sha256-hYXIvhXx1qJefnEbsllwm7TATPU8ihVV6XchaMjTACQ=";
};
npmDepsHash = "sha256-WjPFg/jYTbxrKNzTyqb6e0Z+PLPg6O2k8LBIELwozo8=";
dontNpmBuild = true;
# buildInputs = [ nodejs ]; # buildNpmPackage usually brings in nodejs
meta = with lib; {
description = "Claude Task Master AI - An intelligent task management and project breakdown tool";
homepage = "https://github.com/eyaltoledano/claude-task-master";
license = licenses.mit;
maintainers = [ ]; # Add your GitHub username if you want
platforms = platforms.all;
mainProgram = "task-master-ai";
};
}

View file

@ -6,6 +6,9 @@
# Home-lab administration command-line tool
lab = pkgs.callPackage ./home-lab-tools.nix { };
# Claude Task Master AI package
claude-task-master-ai = pkgs.callPackage ./claude-task-master-ai.nix { };
# Re-export commonly used packages with custom configurations
# (Basic CLI tools moved to base.nix)
}

View file

@ -0,0 +1,114 @@
# Packaging Claude Task Master AI for NixOS
This document outlines suggestions for packaging the "Claude Task Master AI" Node.js application as a Nix package. The typical installation method for this tool is `npm install -g task-master-ai`.
## 1. Creating the Nix Package
Nixpkgs provides helpers for packaging Node.js applications. The primary function for this is `buildNpmPackage`.
A Nix expression for this package can be created in your packages directory, for example, at `packages/claude-task-master-ai.nix`.
### Key Steps:
1. **Find the Source**: Determine the source of the `task-master-ai` package. This is usually the npm registry. You'll need the package name and version.
2. **Nix Expression (`default.nix` or `claude-task-master.nix`):** Create a Nix expression file.
3. **Use `buildNpmPackage`**: This function handles the download, build, and installation of npm packages.
4. **`npmDepsHash`**: You'll need to calculate a hash of the npm dependencies. This ensures reproducibility.
5. **Binaries**: Ensure that the executables provided by `task-master-ai` are correctly placed in the output's `bin` directory. `buildNpmPackage` usually handles this if the `package.json` of the application specifies `bin` entries.
### Example Nix Expression:
```nix
{ lib, buildNpmPackage, fetchFromGitHub, nodejs }: # Add other dependencies if needed
buildNpmPackage rec {
pname = "task-master-ai";
version = "INSERT_PACKAGE_VERSION_HERE"; # Replace with the actual version
src = fetchFromGitHub { # Or fetchurl if directly from npm/tarball
owner = "eyaltoledano"; # Replace if this is not the correct source
repo = "claude-task-master"; # Replace if this is not the correct source
rev = "v${version}"; # Or specific commit/tag
hash = "INSERT_SRC_HASH_HERE"; # lib.fakeSha256 for initial fetch, then replace
};
# If fetching directly from npm tarball:
# src = fetchurl {
# url = "https://registry.npmjs.org/task-master-ai/-/task-master-ai-${version}.tgz";
# sha256 = "INSERT_TARBALL_HASH_HERE"; # lib.fakeSha256 for initial fetch, then replace
# };
npmDepsHash = "INSERT_NPMDEPSHASH_HERE"; # Calculate this after the first build attempt
# buildInputs = [ nodejs ]; # buildNpmPackage usually brings in nodejs
meta = with lib; {
description = "Claude Task Master AI tool";
homepage = "https://github.com/eyaltoledano/claude-task-master"; # Or actual homepage
license = licenses.mit; # Check and replace with actual license
maintainers = [ maintainers.yourGithubUsername ]; # Your username
platforms = platforms.all;
};
}
```
### Obtaining `npmDepsHash`:
1. Initially, set `npmDepsHash = lib.fakeSha256;` or a placeholder like `"sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";`.
2. Attempt to build the package (e.g., `nix-build -A task-master-ai`).
3. The build will fail, but it will output the expected hash. Copy this hash into your Nix expression.
Alternatively, you can use `prefetch-npm-deps` if you have a `package-lock.json`:
```sh
# In a directory with package.json and package-lock.json for task-master-ai
nix-shell -p nodePackages.prefetch-npm-deps --run "prefetch-npm-deps package-lock.json"
```
Since `task-master-ai` is installed globally, you might need to fetch its source first to get the `package-lock.json`.
### Global Installation Aspect:
`buildNpmPackage` typically installs binaries specified in the `package.json`'s `bin` field into `$out/bin/`. This makes them available when the package is installed in a Nix profile. If `task-master-ai` is made available this way, VS Code can invoke it using `npx` as shown in the MCP server configuration, or potentially directly if it's added to the PATH.
## 2. Integrating with VS Code as an MCP Server
Instead of running `task-master-ai` as a system-wide NixOS service, it can be integrated directly into VS Code (or other compatible editors) as an MCP (Model Context Protocol) server. This allows your editor to communicate with the AI for task management capabilities.
The Nix package created in the previous step ensures that `task-master-ai` is available in your environment, typically invokable via `npx task-master-ai` or directly if the Nix package adds it to your PATH.
### VS Code `settings.json` Configuration:
You can configure VS Code to use `task-master-ai` as an MCP server by adding the following to your `settings.json` file:
```json
{
"mcpServers": {
"taskmaster-ai": {
"command": "npx",
"args": ["-y", "--package=task-master-ai", "task-master-ai"],
"env": {
"ANTHROPIC_API_KEY": "YOUR_ANTHROPIC_API_KEY_HERE",
"PERPLEXITY_API_KEY": "YOUR_PERPLEXITY_API_KEY_HERE",
"MODEL": "claude-3-7-sonnet-20250219",
"PERPLEXITY_MODEL": "sonar-pro",
"MAX_TOKENS": 64000,
"TEMPERATURE": 0.2,
"DEFAULT_SUBTASKS": 5,
"DEFAULT_PRIORITY": "medium"
}
}
}
}
```
**Key Points for MCP Configuration:**
* **`command` and `args`**: These specify how to run `task-master-ai`. Using `npx -y --package=task-master-ai task-master-ai` ensures that `npx` fetches and runs the specified version of `task-master-ai`. If your Nix package makes `task-master-ai` directly available in the PATH, you might simplify the command to just `task-master-ai` and remove the `args` that specify the package for `npx`.
* **`env`**: This section is crucial. You **must** replace placeholder API keys (`YOUR_ANTHROPIC_API_KEY_HERE`, `YOUR_PERPLEXITY_API_KEY_HERE`) with your actual keys.
* You can customize other environment variables like `MODEL`, `MAX_TOKENS`, etc., according to your needs and the capabilities of `task-master-ai`.
* Ensure the Nix package for `task-master-ai` (and `nodejs`/`npx`) is installed and accessible in the environment where VS Code runs.
## 3. Finding Package Information
* **NPM Registry**: Search for `task-master-ai` on [npmjs.com](https://www.npmjs.com/) to find its exact version, dependencies, and potentially its source repository. The roadmap indicates the source is `https://github.com/eyaltoledano/claude-task-master.git`.
* **GitHub Repository**: The roadmap points to `https://github.com/eyaltoledano/claude-task-master.git`. This is likely the best source for `package.json` and understanding how the tool works.
This guide provides a starting point. You'll need to adapt the examples based on the specifics of the `task-master-ai` tool.