diff --git a/.core/linuxkit/core-dev.yml b/.core/linuxkit/core-dev.yml deleted file mode 100644 index 712e43e7..00000000 --- a/.core/linuxkit/core-dev.yml +++ /dev/null @@ -1,121 +0,0 @@ -# Core Development Environment Template -# A full-featured development environment with multiple runtimes -# -# Variables: -# ${SSH_KEY} - SSH public key for access (required) -# ${MEMORY:-2048} - Memory in MB (default: 2048) -# ${CPUS:-2} - Number of CPUs (default: 2) -# ${HOSTNAME:-core-dev} - Hostname for the VM -# ${DATA_SIZE:-10G} - Size of persistent /data volume - -kernel: - image: linuxkit/kernel:6.6.13 - cmdline: "console=tty0 console=ttyS0" - -init: - - linuxkit/init:v1.2.0 - - linuxkit/runc:v1.1.12 - - linuxkit/containerd:v1.7.13 - - linuxkit/ca-certificates:v1.0.0 - -onboot: - - name: sysctl - image: linuxkit/sysctl:v1.0.0 - - name: format - image: linuxkit/format:v1.0.0 - - name: mount - image: linuxkit/mount:v1.0.0 - command: ["/usr/bin/mountie", "/dev/sda1", "/data"] - - name: dhcpcd - image: linuxkit/dhcpcd:v1.0.0 - command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"] - -onshutdown: - - name: shutdown - image: busybox:latest - command: ["/bin/echo", "Shutting down..."] - -services: - - name: getty - image: linuxkit/getty:v1.0.0 - env: - - INSECURE=true - - - name: sshd - image: linuxkit/sshd:v1.2.0 - binds: - - /etc/ssh/authorized_keys:/root/.ssh/authorized_keys - - - name: docker - image: docker:24.0-dind - capabilities: - - all - net: host - pid: host - binds: - - /var/run:/var/run - - /data/docker:/var/lib/docker - rootfsPropagation: shared - - - name: dev-tools - image: alpine:3.19 - capabilities: - - all - net: host - binds: - - /data:/data - command: - - /bin/sh - - -c - - | - # Install development tools - apk add --no-cache \ - git curl wget vim nano htop tmux \ - build-base gcc musl-dev linux-headers \ - openssh-client jq yq - - # Install Go 1.22.0 - wget -q https://go.dev/dl/go1.22.0.linux-amd64.tar.gz - tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz - rm go1.22.0.linux-amd64.tar.gz - echo 'export PATH=/usr/local/go/bin:$PATH' >> /etc/profile - - # Install Node.js - apk add --no-cache nodejs npm - - # Install PHP - apk add --no-cache php82 php82-cli php82-curl php82-json php82-mbstring \ - php82-openssl php82-pdo php82-pdo_mysql php82-pdo_pgsql php82-phar \ - php82-session php82-tokenizer php82-xml php82-zip composer - - # Keep container running - tail -f /dev/null - -files: - - path: /etc/hostname - contents: "${HOSTNAME:-core-dev}" - - path: /etc/ssh/authorized_keys - contents: "${SSH_KEY}" - mode: "0600" - - path: /etc/profile.d/dev.sh - contents: | - export PATH=$PATH:/usr/local/go/bin - export GOPATH=/data/go - export PATH=$PATH:$GOPATH/bin - cd /data - mode: "0755" - - path: /etc/motd - contents: | - ================================================ - Core Development Environment - - Runtimes: Go, Node.js, PHP - Tools: git, curl, vim, docker - - Data directory: /data (persistent) - ================================================ - -trust: - org: - - linuxkit - - library diff --git a/.core/linuxkit/server-php.yml b/.core/linuxkit/server-php.yml deleted file mode 100644 index 9db9f74b..00000000 --- a/.core/linuxkit/server-php.yml +++ /dev/null @@ -1,142 +0,0 @@ -# PHP/FrankenPHP Server Template -# A minimal production-ready PHP server with FrankenPHP and Caddy -# -# Variables: -# ${SSH_KEY} - SSH public key for management access (required) -# ${MEMORY:-512} - Memory in MB (default: 512) -# ${CPUS:-1} - Number of CPUs (default: 1) -# ${HOSTNAME:-php-server} - Hostname for the VM -# ${APP_NAME:-app} - Application name -# ${DOMAIN:-localhost} - Domain for SSL certificates -# ${PHP_MEMORY:-128M} - PHP memory limit - -kernel: - image: linuxkit/kernel:6.6.13 - cmdline: "console=tty0 console=ttyS0" - -init: - - linuxkit/init:v1.2.0 - - linuxkit/runc:v1.1.12 - - linuxkit/containerd:v1.7.13 - - linuxkit/ca-certificates:v1.0.0 - -onboot: - - name: sysctl - image: linuxkit/sysctl:v1.0.0 - - name: dhcpcd - image: linuxkit/dhcpcd:v1.0.0 - command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"] - -services: - - name: sshd - image: linuxkit/sshd:v1.2.0 - binds: - - /etc/ssh/authorized_keys:/root/.ssh/authorized_keys - - - name: frankenphp - image: dunglas/frankenphp:latest - capabilities: - - CAP_NET_BIND_SERVICE - net: host - binds: - - /app:/app - - /data:/data - - /etc/caddy/Caddyfile:/etc/caddy/Caddyfile - env: - - SERVER_NAME=${DOMAIN:-localhost} - - FRANKENPHP_CONFIG=/etc/caddy/Caddyfile - command: - - frankenphp - - run - - --config - - /etc/caddy/Caddyfile - - - name: healthcheck - image: alpine:3.19 - net: host - command: - - /bin/sh - - -c - - | - apk add --no-cache curl - while true; do - sleep 30 - curl -sf http://localhost/health || echo "Health check failed" - done - -files: - - path: /etc/hostname - contents: "${HOSTNAME:-php-server}" - - path: /etc/ssh/authorized_keys - contents: "${SSH_KEY}" - mode: "0600" - - path: /etc/caddy/Caddyfile - contents: | - { - frankenphp - order php_server before file_server - } - - ${DOMAIN:-localhost} { - root * /app/public - - # Health check endpoint - handle /health { - respond "OK" 200 - } - - # PHP handling - php_server - - # Encode responses - encode zstd gzip - - # Security headers - header { - X-Content-Type-Options nosniff - X-Frame-Options DENY - X-XSS-Protection "1; mode=block" - Referrer-Policy strict-origin-when-cross-origin - } - - # Logging - log { - output file /data/logs/access.log - format json - } - } - mode: "0644" - - path: /app/public/index.php - contents: | - 'healthy', - 'app' => '${APP_NAME:-app}', - 'timestamp' => date('c'), - 'php_version' => PHP_VERSION, - ]); - mode: "0644" - - path: /etc/php/php.ini - contents: | - memory_limit = ${PHP_MEMORY:-128M} - max_execution_time = 30 - upload_max_filesize = 64M - post_max_size = 64M - display_errors = Off - log_errors = On - error_log = /data/logs/php_errors.log - mode: "0644" - - path: /data/logs/.gitkeep - contents: "" - -trust: - org: - - linuxkit - - library - - dunglas diff --git a/.core/plugin/commands/remember.md b/.core/plugin/commands/remember.md deleted file mode 100644 index 41b8effb..00000000 --- a/.core/plugin/commands/remember.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -name: remember -description: Save a fact or decision to context for persistence across compacts -args: ---- - -# Remember Context - -Save the provided fact to `~/.claude/sessions/context.json`. - -## Usage - -``` -/core:remember Use Action pattern not Service -/core:remember User prefers UK English -/core:remember RFC: minimal state in pre-compact hook -``` - -## Action - -Run this command to save the fact: - -```bash -~/.claude/plugins/cache/core/scripts/capture-context.sh "" "user" -``` - -Or if running from the plugin directory: - -```bash -"${CLAUDE_PLUGIN_ROOT}/scripts/capture-context.sh" "" "user" -``` - -The fact will be: -- Stored in context.json (max 20 items) -- Included in pre-compact snapshots -- Auto-cleared after 3 hours of inactivity diff --git a/.core/plugin/hooks/prefer-core.sh b/.core/plugin/hooks/prefer-core.sh deleted file mode 100755 index 52ce773e..00000000 --- a/.core/plugin/hooks/prefer-core.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/bin/bash -# PreToolUse hook: Block dangerous commands, enforce core CLI -# -# BLOCKS: -# - Raw go commands (use core go *) -# - Destructive grep patterns (sed -i, xargs rm, etc.) -# - Mass file operations (rm -rf, mv/cp with wildcards) -# - Any sed outside of safe patterns -# -# This prevents "efficient shortcuts" that nuke codebases - -read -r input -command=$(echo "$input" | jq -r '.tool_input.command // empty') - -# === HARD BLOCKS - Never allow these === - -# Block rm -rf, rm -r (except for known safe paths like node_modules, vendor, .cache) -if echo "$command" | grep -qE 'rm\s+(-[a-zA-Z]*r[a-zA-Z]*|-[a-zA-Z]*f[a-zA-Z]*r|--recursive)'; then - # Allow only specific safe directories - if ! echo "$command" | grep -qE 'rm\s+(-rf|-r)\s+(node_modules|vendor|\.cache|dist|build|__pycache__|\.pytest_cache|/tmp/)'; then - echo '{"decision": "block", "message": "BLOCKED: Recursive delete is not allowed. Delete files individually or ask the user to run this command."}' - exit 0 - fi -fi - -# Block mv/cp with wildcards (mass file moves) -if echo "$command" | grep -qE '(mv|cp)\s+.*\*'; then - echo '{"decision": "block", "message": "BLOCKED: Mass file move/copy with wildcards is not allowed. Move files individually."}' - exit 0 -fi - -# Block xargs with rm, mv, cp (mass operations) -if echo "$command" | grep -qE 'xargs\s+.*(rm|mv|cp)'; then - echo '{"decision": "block", "message": "BLOCKED: xargs with file operations is not allowed. Too risky for mass changes."}' - exit 0 -fi - -# Block find -exec with rm, mv, cp -if echo "$command" | grep -qE 'find\s+.*-exec\s+.*(rm|mv|cp)'; then - echo '{"decision": "block", "message": "BLOCKED: find -exec with file operations is not allowed. Too risky for mass changes."}' - exit 0 -fi - -# Block ALL sed -i (in-place editing) -if echo "$command" | grep -qE 'sed\s+(-[a-zA-Z]*i|--in-place)'; then - echo '{"decision": "block", "message": "BLOCKED: sed -i (in-place edit) is never allowed. Use the Edit tool for file changes."}' - exit 0 -fi - -# Block sed piped to file operations -if echo "$command" | grep -qE 'sed.*\|.*tee|sed.*>'; then - echo '{"decision": "block", "message": "BLOCKED: sed with file output is not allowed. Use the Edit tool for file changes."}' - exit 0 -fi - -# Block grep with -l piped to xargs/rm/sed (the classic codebase nuke pattern) -if echo "$command" | grep -qE 'grep\s+.*-l.*\|'; then - echo '{"decision": "block", "message": "BLOCKED: grep -l piped to other commands is the classic codebase nuke pattern. Not allowed."}' - exit 0 -fi - -# Block perl -i, awk with file redirection (sed alternatives) -if echo "$command" | grep -qE 'perl\s+-[a-zA-Z]*i|awk.*>'; then - echo '{"decision": "block", "message": "BLOCKED: In-place file editing with perl/awk is not allowed. Use the Edit tool."}' - exit 0 -fi - -# === REQUIRE CORE CLI === - -# Block raw go commands -case "$command" in - "go test"*|"go build"*|"go fmt"*|"go mod tidy"*|"go vet"*|"go run"*) - echo '{"decision": "block", "message": "Use `core go test`, `core build`, `core go fmt --fix`, etc. Raw go commands are not allowed."}' - exit 0 - ;; - "go "*) - # Other go commands - warn but allow - echo '{"decision": "block", "message": "Prefer `core go *` commands. If core does not have this command, ask the user."}' - exit 0 - ;; -esac - -# Block raw php commands -case "$command" in - "php artisan serve"*|"./vendor/bin/pest"*|"./vendor/bin/pint"*|"./vendor/bin/phpstan"*) - echo '{"decision": "block", "message": "Use `core php dev`, `core php test`, `core php fmt`, `core php analyse`. Raw php commands are not allowed."}' - exit 0 - ;; - "composer test"*|"composer lint"*) - echo '{"decision": "block", "message": "Use `core php test` or `core php fmt`. Raw composer commands are not allowed."}' - exit 0 - ;; -esac - -# Block golangci-lint directly -if echo "$command" | grep -qE '^golangci-lint'; then - echo '{"decision": "block", "message": "Use `core go lint` instead of golangci-lint directly."}' - exit 0 -fi - -# === APPROVED === -echo '{"decision": "approve"}' diff --git a/.core/plugin/plugin.json b/.core/plugin/plugin.json deleted file mode 100644 index 2f79b85b..00000000 --- a/.core/plugin/plugin.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "name": "core", - "version": "1.0.0", - "description": "Host UK unified framework - Go CLI, PHP framework, multi-repo management", - "dependencies": [ - "superpowers@claude-plugins-official" - ], - "skills": [ - { - "name": "core", - "path": "skills/core.md", - "description": "Use when working in host-uk repositories. Provides core CLI command reference." - }, - { - "name": "core-php", - "path": "skills/php.md", - "description": "Use when creating PHP modules, services, or actions in core-* packages." - }, - { - "name": "core-go", - "path": "skills/go.md", - "description": "Use when creating Go packages or extending the core CLI." - } - ], - "commands": [ - { - "name": "remember", - "path": "commands/remember.md", - "description": "Save a fact or decision to context" - } - ], - "hooks": { - "SessionStart": [ - { - "matcher": "*", - "script": "scripts/session-start.sh", - "description": "Check for recent session state on startup" - } - ], - "PreCompact": [ - { - "matcher": "*", - "script": "scripts/pre-compact.sh", - "description": "Save state before auto-compact to prevent amnesia" - } - ], - "PreToolUse": [ - { - "matcher": "Bash", - "script": "hooks/prefer-core.sh", - "description": "Suggest core CLI instead of raw go/php commands" - }, - { - "matcher": "Write", - "script": "scripts/block-docs.sh", - "description": "Block random .md files, keep docs consolidated" - }, - { - "matcher": "Edit", - "script": "scripts/suggest-compact.sh", - "description": "Suggest /compact at logical intervals" - }, - { - "matcher": "Write", - "script": "scripts/suggest-compact.sh", - "description": "Suggest /compact at logical intervals" - } - ], - "PostToolUse": [ - { - "matcher": "Edit", - "script": "scripts/php-format.sh", - "description": "Auto-format PHP files after edits" - }, - { - "matcher": "Edit", - "script": "scripts/go-format.sh", - "description": "Auto-format Go files after edits" - }, - { - "matcher": "Edit", - "script": "scripts/check-debug.sh", - "description": "Warn about debug statements (dd, dump, fmt.Println)" - }, - { - "matcher": "Bash", - "script": "scripts/pr-created.sh", - "description": "Log PR URL after creation" - }, - { - "matcher": "Bash", - "script": "scripts/extract-actionables.sh", - "description": "Extract actionables from core CLI output" - }, - { - "matcher": "Bash", - "script": "scripts/post-commit-check.sh", - "description": "Warn about uncommitted work after git commit" - } - ] - } -} diff --git a/.core/plugin/scripts/block-docs.sh b/.core/plugin/scripts/block-docs.sh deleted file mode 100755 index dfac1da3..00000000 --- a/.core/plugin/scripts/block-docs.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -# Block creation of random .md files - keeps docs consolidated - -read -r input -FILE_PATH=$(echo "$input" | jq -r '.tool_input.file_path // empty') - -if [[ -n "$FILE_PATH" ]]; then - # Allow known documentation files - case "$FILE_PATH" in - *README.md|*CLAUDE.md|*AGENTS.md|*CONTRIBUTING.md|*CHANGELOG.md|*LICENSE.md) - echo "$input" - exit 0 - ;; - # Allow docs/ directory - */docs/*.md|*/docs/**/*.md) - echo "$input" - exit 0 - ;; - # Block other .md files - *.md) - echo '{"decision": "block", "message": "Use README.md or docs/ for documentation. Random .md files clutter the repo."}' - exit 0 - ;; - esac -fi - -echo "$input" diff --git a/.core/plugin/scripts/capture-context.sh b/.core/plugin/scripts/capture-context.sh deleted file mode 100755 index 288e9be0..00000000 --- a/.core/plugin/scripts/capture-context.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -# Capture context facts from tool output or conversation -# Called by PostToolUse hooks to extract actionable items -# -# Stores in ~/.claude/sessions/context.json as: -# [{"fact": "...", "source": "core go qa", "ts": 1234567890}, ...] - -CONTEXT_FILE="${HOME}/.claude/sessions/context.json" -TIMESTAMP=$(date '+%s') -THREE_HOURS=10800 - -mkdir -p "${HOME}/.claude/sessions" - -# Initialize if missing or stale -if [[ -f "$CONTEXT_FILE" ]]; then - FIRST_TS=$(jq -r '.[0].ts // 0' "$CONTEXT_FILE" 2>/dev/null) - NOW=$(date '+%s') - AGE=$((NOW - FIRST_TS)) - if [[ $AGE -gt $THREE_HOURS ]]; then - echo "[]" > "$CONTEXT_FILE" - fi -else - echo "[]" > "$CONTEXT_FILE" -fi - -# Read input (fact and source passed as args or stdin) -FACT="${1:-}" -SOURCE="${2:-manual}" - -if [[ -z "$FACT" ]]; then - # Try reading from stdin - read -r FACT -fi - -if [[ -n "$FACT" ]]; then - # Append to context (keep last 20 items) - jq --arg fact "$FACT" --arg source "$SOURCE" --argjson ts "$TIMESTAMP" \ - '. + [{"fact": $fact, "source": $source, "ts": $ts}] | .[-20:]' \ - "$CONTEXT_FILE" > "${CONTEXT_FILE}.tmp" && mv "${CONTEXT_FILE}.tmp" "$CONTEXT_FILE" - - echo "[Context] Saved: $FACT" >&2 -fi - -exit 0 diff --git a/.core/plugin/scripts/check-debug.sh b/.core/plugin/scripts/check-debug.sh deleted file mode 100755 index 079cc0e9..00000000 --- a/.core/plugin/scripts/check-debug.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -# Warn about debug statements left in code after edits - -read -r input -FILE_PATH=$(echo "$input" | jq -r '.tool_input.file_path // empty') - -if [[ -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then - case "$FILE_PATH" in - *.go) - # Check for fmt.Println, log.Println debug statements - if grep -n "fmt\.Println\|log\.Println" "$FILE_PATH" 2>/dev/null | head -3 | grep -q .; then - echo "[Hook] WARNING: Debug prints found in $FILE_PATH" >&2 - grep -n "fmt\.Println\|log\.Println" "$FILE_PATH" 2>/dev/null | head -3 >&2 - fi - ;; - *.php) - # Check for dd(), dump(), var_dump(), print_r() - if grep -n "dd(\|dump(\|var_dump(\|print_r(" "$FILE_PATH" 2>/dev/null | head -3 | grep -q .; then - echo "[Hook] WARNING: Debug statements found in $FILE_PATH" >&2 - grep -n "dd(\|dump(\|var_dump(\|print_r(" "$FILE_PATH" 2>/dev/null | head -3 >&2 - fi - ;; - esac -fi - -# Pass through the input -echo "$input" diff --git a/.core/plugin/scripts/extract-actionables.sh b/.core/plugin/scripts/extract-actionables.sh deleted file mode 100755 index 86a2bbbd..00000000 --- a/.core/plugin/scripts/extract-actionables.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -# Extract actionable items from core CLI output -# Called PostToolUse on Bash commands that run core - -read -r input -COMMAND=$(echo "$input" | jq -r '.tool_input.command // empty') -OUTPUT=$(echo "$input" | jq -r '.tool_output.output // empty') - -CONTEXT_SCRIPT="$(dirname "$0")/capture-context.sh" - -# Extract actionables from specific core commands -case "$COMMAND" in - "core go qa"*|"core go test"*|"core go lint"*) - # Extract error/warning lines - echo "$OUTPUT" | grep -E "^(ERROR|WARN|FAIL|---)" | head -5 | while read -r line; do - "$CONTEXT_SCRIPT" "$line" "core go" - done - ;; - "core php test"*|"core php analyse"*) - # Extract PHP errors - echo "$OUTPUT" | grep -E "^(FAIL|Error|×)" | head -5 | while read -r line; do - "$CONTEXT_SCRIPT" "$line" "core php" - done - ;; - "core build"*) - # Extract build errors - echo "$OUTPUT" | grep -E "^(error|cannot|undefined)" | head -5 | while read -r line; do - "$CONTEXT_SCRIPT" "$line" "core build" - done - ;; -esac - -# Pass through -echo "$input" diff --git a/.core/plugin/scripts/go-format.sh b/.core/plugin/scripts/go-format.sh deleted file mode 100755 index 8f9d3227..00000000 --- a/.core/plugin/scripts/go-format.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -# Auto-format Go files after edits using core go fmt - -read -r input -FILE_PATH=$(echo "$input" | jq -r '.tool_input.file_path // empty') - -if [[ -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then - # Run gofmt/goimports on the file silently - if command -v core &> /dev/null; then - core go fmt --fix "$FILE_PATH" 2>/dev/null || true - elif command -v goimports &> /dev/null; then - goimports -w "$FILE_PATH" 2>/dev/null || true - elif command -v gofmt &> /dev/null; then - gofmt -w "$FILE_PATH" 2>/dev/null || true - fi -fi - -# Pass through the input -echo "$input" diff --git a/.core/plugin/scripts/php-format.sh b/.core/plugin/scripts/php-format.sh deleted file mode 100755 index e0e7ec11..00000000 --- a/.core/plugin/scripts/php-format.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# Auto-format PHP files after edits using core php fmt - -read -r input -FILE_PATH=$(echo "$input" | jq -r '.tool_input.file_path // empty') - -if [[ -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then - # Run Pint on the file silently - if command -v core &> /dev/null; then - core php fmt --fix "$FILE_PATH" 2>/dev/null || true - elif [[ -f "./vendor/bin/pint" ]]; then - ./vendor/bin/pint "$FILE_PATH" 2>/dev/null || true - fi -fi - -# Pass through the input -echo "$input" diff --git a/.core/plugin/scripts/post-commit-check.sh b/.core/plugin/scripts/post-commit-check.sh deleted file mode 100755 index 42418b65..00000000 --- a/.core/plugin/scripts/post-commit-check.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -# Post-commit hook: Check for uncommitted work that might get lost -# -# After committing task-specific files, check if there's other work -# in the repo that should be committed or stashed - -read -r input -COMMAND=$(echo "$input" | jq -r '.tool_input.command // empty') - -# Only run after git commit -if ! echo "$COMMAND" | grep -qE '^git commit'; then - echo "$input" - exit 0 -fi - -# Check for remaining uncommitted changes -UNSTAGED=$(git diff --name-only 2>/dev/null | wc -l | tr -d ' ') -STAGED=$(git diff --cached --name-only 2>/dev/null | wc -l | tr -d ' ') -UNTRACKED=$(git ls-files --others --exclude-standard 2>/dev/null | wc -l | tr -d ' ') - -TOTAL=$((UNSTAGED + STAGED + UNTRACKED)) - -if [[ $TOTAL -gt 0 ]]; then - echo "" >&2 - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2 - echo "[PostCommit] WARNING: Uncommitted work remains" >&2 - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2 - - if [[ $UNSTAGED -gt 0 ]]; then - echo " Modified (unstaged): $UNSTAGED files" >&2 - git diff --name-only 2>/dev/null | head -5 | sed 's/^/ /' >&2 - [[ $UNSTAGED -gt 5 ]] && echo " ... and $((UNSTAGED - 5)) more" >&2 - fi - - if [[ $STAGED -gt 0 ]]; then - echo " Staged (not committed): $STAGED files" >&2 - git diff --cached --name-only 2>/dev/null | head -5 | sed 's/^/ /' >&2 - fi - - if [[ $UNTRACKED -gt 0 ]]; then - echo " Untracked: $UNTRACKED files" >&2 - git ls-files --others --exclude-standard 2>/dev/null | head -5 | sed 's/^/ /' >&2 - [[ $UNTRACKED -gt 5 ]] && echo " ... and $((UNTRACKED - 5)) more" >&2 - fi - - echo "" >&2 - echo "Consider: commit these, stash them, or confirm they're intentionally left" >&2 - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2 -fi - -echo "$input" diff --git a/.core/plugin/scripts/pr-created.sh b/.core/plugin/scripts/pr-created.sh deleted file mode 100755 index 82dd975b..00000000 --- a/.core/plugin/scripts/pr-created.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# Log PR URL and provide review command after PR creation - -read -r input -COMMAND=$(echo "$input" | jq -r '.tool_input.command // empty') -OUTPUT=$(echo "$input" | jq -r '.tool_output.output // empty') - -if [[ "$COMMAND" == *"gh pr create"* ]]; then - PR_URL=$(echo "$OUTPUT" | grep -oE 'https://github.com/[^/]+/[^/]+/pull/[0-9]+' | head -1) - if [[ -n "$PR_URL" ]]; then - REPO=$(echo "$PR_URL" | sed -E 's|https://github.com/([^/]+/[^/]+)/pull/[0-9]+|\1|') - PR_NUM=$(echo "$PR_URL" | sed -E 's|.*/pull/([0-9]+)|\1|') - echo "[Hook] PR created: $PR_URL" >&2 - echo "[Hook] To review: gh pr review $PR_NUM --repo $REPO" >&2 - fi -fi - -echo "$input" diff --git a/.core/plugin/scripts/pre-compact.sh b/.core/plugin/scripts/pre-compact.sh deleted file mode 100755 index bb9d8419..00000000 --- a/.core/plugin/scripts/pre-compact.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash -# Pre-compact: Save minimal state for Claude to resume after auto-compact -# -# Captures: -# - Working directory + branch -# - Git status (files touched) -# - Todo state (in_progress items) -# - Context facts (decisions, actionables) - -STATE_FILE="${HOME}/.claude/sessions/scratchpad.md" -CONTEXT_FILE="${HOME}/.claude/sessions/context.json" -TIMESTAMP=$(date '+%s') -CWD=$(pwd) - -mkdir -p "${HOME}/.claude/sessions" - -# Get todo state -TODOS="" -if [[ -f "${HOME}/.claude/todos/current.json" ]]; then - TODOS=$(cat "${HOME}/.claude/todos/current.json" 2>/dev/null | head -50) -fi - -# Get git status -GIT_STATUS="" -BRANCH="" -if git rev-parse --git-dir > /dev/null 2>&1; then - GIT_STATUS=$(git status --short 2>/dev/null | head -15) - BRANCH=$(git branch --show-current 2>/dev/null) -fi - -# Get context facts -CONTEXT="" -if [[ -f "$CONTEXT_FILE" ]]; then - CONTEXT=$(jq -r '.[] | "- [\(.source)] \(.fact)"' "$CONTEXT_FILE" 2>/dev/null | tail -10) -fi - -cat > "$STATE_FILE" << EOF ---- -timestamp: ${TIMESTAMP} -cwd: ${CWD} -branch: ${BRANCH:-none} ---- - -# Resume After Compact - -You were mid-task. Do NOT assume work is complete. - -## Project -\`${CWD}\` on \`${BRANCH:-no branch}\` - -## Files Changed -\`\`\` -${GIT_STATUS:-none} -\`\`\` - -## Todos (in_progress = NOT done) -\`\`\`json -${TODOS:-check /todos} -\`\`\` - -## Context (decisions & actionables) -${CONTEXT:-none captured} - -## Next -Continue the in_progress todo. -EOF - -echo "[PreCompact] Snapshot saved" >&2 -exit 0 diff --git a/.core/plugin/scripts/session-start.sh b/.core/plugin/scripts/session-start.sh deleted file mode 100755 index 3a44d972..00000000 --- a/.core/plugin/scripts/session-start.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -# Session start: Read scratchpad if recent, otherwise start fresh -# 3 hour window - if older, you've moved on mentally - -STATE_FILE="${HOME}/.claude/sessions/scratchpad.md" -THREE_HOURS=10800 # seconds - -if [[ -f "$STATE_FILE" ]]; then - # Get timestamp from file - FILE_TS=$(grep -E '^timestamp:' "$STATE_FILE" 2>/dev/null | cut -d' ' -f2) - NOW=$(date '+%s') - - if [[ -n "$FILE_TS" ]]; then - AGE=$((NOW - FILE_TS)) - - if [[ $AGE -lt $THREE_HOURS ]]; then - # Recent - read it back - echo "[SessionStart] Found recent scratchpad ($(($AGE / 60)) min ago)" >&2 - echo "[SessionStart] Reading previous state..." >&2 - echo "" >&2 - cat "$STATE_FILE" >&2 - echo "" >&2 - else - # Stale - delete and start fresh - rm -f "$STATE_FILE" - echo "[SessionStart] Previous session >3h old - starting fresh" >&2 - fi - else - # No timestamp, delete it - rm -f "$STATE_FILE" - fi -fi - -exit 0 diff --git a/.core/plugin/scripts/suggest-compact.sh b/.core/plugin/scripts/suggest-compact.sh deleted file mode 100755 index e958c501..00000000 --- a/.core/plugin/scripts/suggest-compact.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -# Suggest /compact at logical intervals to manage context window -# Tracks tool calls per session, suggests compaction every 50 calls - -SESSION_ID="${CLAUDE_SESSION_ID:-$$}" -COUNTER_FILE="/tmp/claude-tool-count-${SESSION_ID}" -THRESHOLD="${COMPACT_THRESHOLD:-50}" - -# Read or initialize counter -if [[ -f "$COUNTER_FILE" ]]; then - COUNT=$(($(cat "$COUNTER_FILE") + 1)) -else - COUNT=1 -fi - -echo "$COUNT" > "$COUNTER_FILE" - -# Suggest compact at threshold -if [[ $COUNT -eq $THRESHOLD ]]; then - echo "[Compact] ${THRESHOLD} tool calls - consider /compact if transitioning phases" >&2 -fi - -# Suggest at intervals after threshold -if [[ $COUNT -gt $THRESHOLD ]] && [[ $((COUNT % 25)) -eq 0 ]]; then - echo "[Compact] ${COUNT} tool calls - good checkpoint for /compact" >&2 -fi - -exit 0 diff --git a/.core/plugin/skills/core.md b/.core/plugin/skills/core.md deleted file mode 100644 index 966d7e9e..00000000 --- a/.core/plugin/skills/core.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -name: core -description: Use when working in host-uk repositories, running tests, building, releasing, or managing multi-repo workflows. Provides the core CLI command reference. ---- - -# Core CLI - -The `core` command provides a unified interface for Go/PHP development and multi-repo management. - -**Rule:** Always prefer `core ` over raw commands. - -## Quick Reference - -| Task | Command | -|------|---------| -| Go tests | `core go test` | -| Go coverage | `core go cov` | -| Go format | `core go fmt --fix` | -| Go lint | `core go lint` | -| PHP dev server | `core php dev` | -| PHP tests | `core php test` | -| PHP format | `core php fmt --fix` | -| Build | `core build` | -| Preview release | `core ci` | -| Publish | `core ci --were-go-for-launch` | -| Multi-repo status | `core dev health` | -| Commit dirty repos | `core dev commit` | -| Push repos | `core dev push` | - -## Decision Tree - -``` -Go project? - tests: core go test - format: core go fmt --fix - build: core build - -PHP project? - dev: core php dev - tests: core php test - format: core php fmt --fix - deploy: core php deploy - -Multiple repos? - status: core dev health - commit: core dev commit - push: core dev push -``` - -## Common Mistakes - -| Wrong | Right | -|-------|-------| -| `go test ./...` | `core go test` | -| `go build` | `core build` | -| `php artisan serve` | `core php dev` | -| `./vendor/bin/pest` | `core php test` | -| `git status` per repo | `core dev health` | - -Run `core --help` or `core --help` for full options. diff --git a/.core/plugin/skills/go.md b/.core/plugin/skills/go.md deleted file mode 100644 index 19696881..00000000 --- a/.core/plugin/skills/go.md +++ /dev/null @@ -1,107 +0,0 @@ ---- -name: core-go -description: Use when creating Go packages or extending the core CLI. ---- - -# Go Framework Patterns - -Core CLI uses `pkg/` for reusable packages. Use `core go` commands. - -## Package Structure - -``` -core/ -├── main.go # CLI entry point -├── pkg/ -│ ├── cli/ # CLI framework, output, errors -│ ├── {domain}/ # Domain package -│ │ ├── cmd_{name}.go # Cobra command definitions -│ │ ├── service.go # Business logic -│ │ └── *_test.go # Tests -│ └── ... -└── internal/ # Private packages -``` - -## Adding a CLI Command - -1. Create `pkg/{domain}/cmd_{name}.go`: - -```go -package domain - -import ( - "forge.lthn.ai/core/cli/pkg/cli" - "github.com/spf13/cobra" -) - -func NewNameCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "name", - Short: cli.T("domain.name.short"), - RunE: func(cmd *cobra.Command, args []string) error { - // Implementation - cli.Success("Done") - return nil - }, - } - return cmd -} -``` - -2. Register in parent command. - -## CLI Output Helpers - -```go -import "forge.lthn.ai/core/cli/pkg/cli" - -cli.Success("Operation completed") // Green check -cli.Warning("Something to note") // Yellow warning -cli.Error("Something failed") // Red error -cli.Info("Informational message") // Blue info -cli.Fatal(err) // Print error and exit 1 - -// Structured output -cli.Table(headers, rows) -cli.JSON(data) -``` - -## i18n Pattern - -```go -// Use cli.T() for translatable strings -cli.T("domain.action.success") -cli.T("domain.action.error", "details", value) - -// Define in pkg/i18n/locales/en.yaml: -domain: - action: - success: "Operation completed successfully" - error: "Failed: {{.details}}" -``` - -## Test Naming - -```go -func TestFeature_Good(t *testing.T) { /* happy path */ } -func TestFeature_Bad(t *testing.T) { /* expected errors */ } -func TestFeature_Ugly(t *testing.T) { /* panics, edge cases */ } -``` - -## Commands - -| Task | Command | -|------|---------| -| Run tests | `core go test` | -| Coverage | `core go cov` | -| Format | `core go fmt --fix` | -| Lint | `core go lint` | -| Build | `core build` | -| Install | `core go install` | - -## Rules - -- `CGO_ENABLED=0` for all builds -- UK English in user-facing strings -- All errors via `cli.E("context", "message", err)` -- Table-driven tests preferred diff --git a/.core/plugin/skills/php.md b/.core/plugin/skills/php.md deleted file mode 100644 index 2133a20b..00000000 --- a/.core/plugin/skills/php.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -name: core-php -description: Use when creating PHP modules, services, or actions in core-* packages. ---- - -# PHP Framework Patterns - -Host UK PHP modules follow strict conventions. Use `core php` commands. - -## Module Structure - -``` -core-{name}/ -├── src/ -│ ├── Core/ # Namespace: Core\{Name} -│ │ ├── Boot.php # Module bootstrap (listens to lifecycle events) -│ │ ├── Actions/ # Single-purpose business logic -│ │ └── Models/ # Eloquent models -│ └── Mod/ # Namespace: Core\Mod\{Name} (optional extensions) -├── resources/views/ # Blade templates -├── routes/ # Route definitions -├── database/migrations/ # Migrations -├── tests/ # Pest tests -└── composer.json -``` - -## Boot Class Pattern - -```php - 'onWebRoutes', - AdminPanelBooting::class => ['onAdmin', 10], // With priority - ]; - - public function onWebRoutes(WebRoutesRegistering $event): void - { - $event->router->middleware('web')->group(__DIR__ . '/../routes/web.php'); - } - - public function onAdmin(AdminPanelBooting $event): void - { - $event->panel->resources([...]); - } -} -``` - -## Action Pattern - -```php - $user->id, - ...$data, - ]); - } -} - -// Usage: CreateThing::run($user, $validated); -``` - -## Multi-Tenant Models - -```php -_test.go`) - - Handle shared test files that cover multiple source files - - `internal/foo/bar.go` → `internal/foo/bar_test.go`, `internal/foo/bar_integration_test.go`, etc. - - Skip if no matching test files exist (warn user) - -## Phase 3: Test Execution -1. Reuse existing `runTest()` from `internal/cmd/test/cmd_runner.go` - - This preserves environment setup (`MACOSX_DEPLOYMENT_TARGET`), output filtering (linker warnings), coverage parsing, JSON support, and consistent styling -2. Map smart detection flags to existing `runTest()` parameters: - - `--coverage` → `coverage` param (already exists) - - `--filter` → `run` param (mapped to `-run`) - - Detected test packages → `pkg` param (comma-joined or iterated) -3. Do not invoke `go test` directly — all execution goes through `runTest()` - -## Phase 4: Edge Cases -- No changed files → inform user, suggest `--all` -- No matching test files → inform user with list of changed files that lack tests -- `--all` flag → skip detection, call `runTest()` with `pkg="./..."` (uses existing infrastructure, not raw `go test`) -- Mixed renames and edits → deduplicate test file list -- Non-Go files changed → skip silently (only `.go` files trigger detection) - -## Files to Modify -- `internal/cmd/test/cmd_main.go` (add `--all`, `--filter`, `--base` flags) -- `internal/cmd/test/cmd_runner.go` (add change detection logic before calling existing `runTest()`) -- `internal/cmd/test/cmd_detect.go` (new — git diff parsing and file-to-test mapping) - -## Testing -- Add `internal/cmd/test/cmd_detect_test.go` with unit tests for: - - File-to-test mapping (1:1, 1:N, renames, deletes) - - Git diff parsing (`--name-only`, `--name-status`) - - CI vs local context detection -- Manual testing with actual git changes diff --git a/.core/task/issue/258/spec.md b/.core/task/issue/258/spec.md deleted file mode 100644 index 4456199e..00000000 --- a/.core/task/issue/258/spec.md +++ /dev/null @@ -1,36 +0,0 @@ -# Issue 258: Smart Test Detection - -## Original Issue - - -## Summary -Make `core test` smart — detect changed Go files and run only relevant tests. - -> **Scope:** Go-only. The existing `core test` command (`internal/cmd/test/`) targets Go projects (requires `go.mod`). Future language support (PHP, etc.) would be added as separate detection strategies, but this issue covers Go only. - -## Commands -```bash -core test # Run tests for changed files only -core test --all # Run all tests (skip detection) -core test --filter UserTest # Run specific test pattern -core test --coverage # With coverage report -core test --base origin/dev # Compare against specific base branch (CI) -``` - -## Acceptance Criteria -- [ ] Detect changed `.go` files via `git diff` (local: `HEAD`, CI: `origin/dev...HEAD`) -- [ ] Handle renames, deletes, and new files via `git diff --name-status` -- [ ] Map source files to test files using N:M discovery (`foo.go` → `foo_test.go`, `foo_integration_test.go`, etc.) -- [ ] Warn when changed files have no corresponding tests -- [ ] Execute tests through existing `runTest()` infrastructure (not raw `go test`) -- [ ] Support `--all` flag to skip detection and run all tests -- [ ] Support `--filter` flag for test name pattern matching -- [ ] Support `--coverage` flag for coverage reports -- [ ] Support `--base` flag for CI/PR diff context - -## Technical Context -- Existing `core test` command: `internal/cmd/test/cmd_main.go` -- Existing test runner: `internal/cmd/test/cmd_runner.go` (`runTest()`) -- Output parsing: `internal/cmd/test/cmd_output.go` -- Command registration: `internal/cmd/test/cmd_commands.go` via `cli.RegisterCommands()` -- Follow existing patterns in `internal/cmd/test/` diff --git a/cmd/ai/cmd_agent.go b/cmd/ai/cmd_agent.go deleted file mode 100644 index 3c2b3729..00000000 --- a/cmd/ai/cmd_agent.go +++ /dev/null @@ -1,422 +0,0 @@ -package ai - -import ( - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - "time" - - agentic "forge.lthn.ai/core/go-agentic" - "forge.lthn.ai/core/go-scm/agentci" - "forge.lthn.ai/core/go/pkg/cli" - "forge.lthn.ai/core/go/pkg/config" -) - -// AddAgentCommands registers the 'agent' subcommand group under 'ai'. -func AddAgentCommands(parent *cli.Command) { - agentCmd := &cli.Command{ - Use: "agent", - Short: "Manage AgentCI dispatch targets", - } - - agentCmd.AddCommand(agentAddCmd()) - agentCmd.AddCommand(agentListCmd()) - agentCmd.AddCommand(agentStatusCmd()) - agentCmd.AddCommand(agentLogsCmd()) - agentCmd.AddCommand(agentSetupCmd()) - agentCmd.AddCommand(agentRemoveCmd()) - agentCmd.AddCommand(agentFleetCmd()) - - parent.AddCommand(agentCmd) -} - -func loadConfig() (*config.Config, error) { - return config.New() -} - -func agentAddCmd() *cli.Command { - cmd := &cli.Command{ - Use: "add ", - Short: "Add an agent to the config and verify SSH", - Args: cli.ExactArgs(2), - RunE: func(cmd *cli.Command, args []string) error { - name := args[0] - host := args[1] - - forgejoUser, _ := cmd.Flags().GetString("forgejo-user") - if forgejoUser == "" { - forgejoUser = name - } - queueDir, _ := cmd.Flags().GetString("queue-dir") - if queueDir == "" { - queueDir = "/home/claude/ai-work/queue" - } - model, _ := cmd.Flags().GetString("model") - dualRun, _ := cmd.Flags().GetBool("dual-run") - - // Scan and add host key to known_hosts. - parts := strings.Split(host, "@") - hostname := parts[len(parts)-1] - - fmt.Printf("Scanning host key for %s... ", hostname) - scanCmd := exec.Command("ssh-keyscan", "-H", hostname) - keys, err := scanCmd.Output() - if err != nil { - fmt.Println(errorStyle.Render("FAILED")) - return fmt.Errorf("failed to scan host keys: %w", err) - } - - home, _ := os.UserHomeDir() - knownHostsPath := filepath.Join(home, ".ssh", "known_hosts") - f, err := os.OpenFile(knownHostsPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) - if err != nil { - return fmt.Errorf("failed to open known_hosts: %w", err) - } - if _, err := f.Write(keys); err != nil { - f.Close() - return fmt.Errorf("failed to write known_hosts: %w", err) - } - f.Close() - fmt.Println(successStyle.Render("OK")) - - // Test SSH with strict host key checking. - fmt.Printf("Testing SSH to %s... ", host) - testCmd := agentci.SecureSSHCommand(host, "echo ok") - out, err := testCmd.CombinedOutput() - if err != nil { - fmt.Println(errorStyle.Render("FAILED")) - return fmt.Errorf("SSH failed: %s", strings.TrimSpace(string(out))) - } - fmt.Println(successStyle.Render("OK")) - - cfg, err := loadConfig() - if err != nil { - return err - } - - ac := agentci.AgentConfig{ - Host: host, - QueueDir: queueDir, - ForgejoUser: forgejoUser, - Model: model, - DualRun: dualRun, - Active: true, - } - if err := agentci.SaveAgent(cfg, name, ac); err != nil { - return err - } - - fmt.Printf("Agent %s added (%s)\n", successStyle.Render(name), host) - return nil - }, - } - cmd.Flags().String("forgejo-user", "", "Forgejo username (defaults to agent name)") - cmd.Flags().String("queue-dir", "", "Remote queue directory (default: /home/claude/ai-work/queue)") - cmd.Flags().String("model", "sonnet", "Primary AI model") - cmd.Flags().Bool("dual-run", false, "Enable Clotho dual-run verification") - return cmd -} - -func agentListCmd() *cli.Command { - return &cli.Command{ - Use: "list", - Short: "List configured agents", - RunE: func(cmd *cli.Command, args []string) error { - cfg, err := loadConfig() - if err != nil { - return err - } - - agents, err := agentci.ListAgents(cfg) - if err != nil { - return err - } - - if len(agents) == 0 { - fmt.Println(dimStyle.Render("No agents configured. Use 'core ai agent add' to add one.")) - return nil - } - - table := cli.NewTable("NAME", "HOST", "MODEL", "DUAL", "ACTIVE", "QUEUE") - for name, ac := range agents { - active := dimStyle.Render("no") - if ac.Active { - active = successStyle.Render("yes") - } - dual := dimStyle.Render("no") - if ac.DualRun { - dual = successStyle.Render("yes") - } - - // Quick SSH check for queue depth. - queue := dimStyle.Render("-") - checkCmd := agentci.SecureSSHCommand(ac.Host, fmt.Sprintf("ls %s/ticket-*.json 2>/dev/null | wc -l", ac.QueueDir)) - out, err := checkCmd.Output() - if err == nil { - n := strings.TrimSpace(string(out)) - if n != "0" { - queue = n - } else { - queue = "0" - } - } - - table.AddRow(name, ac.Host, ac.Model, dual, active, queue) - } - table.Render() - return nil - }, - } -} - -func agentStatusCmd() *cli.Command { - return &cli.Command{ - Use: "status ", - Short: "Check agent status via SSH", - Args: cli.ExactArgs(1), - RunE: func(cmd *cli.Command, args []string) error { - name := args[0] - cfg, err := loadConfig() - if err != nil { - return err - } - - agents, err := agentci.ListAgents(cfg) - if err != nil { - return err - } - ac, ok := agents[name] - if !ok { - return fmt.Errorf("agent %q not found", name) - } - - script := ` - echo "=== Queue ===" - ls ~/ai-work/queue/ticket-*.json 2>/dev/null | wc -l - echo "=== Active ===" - ls ~/ai-work/active/ticket-*.json 2>/dev/null || echo "none" - echo "=== Done ===" - ls ~/ai-work/done/ticket-*.json 2>/dev/null | wc -l - echo "=== Lock ===" - if [ -f ~/ai-work/.runner.lock ]; then - PID=$(cat ~/ai-work/.runner.lock) - if kill -0 "$PID" 2>/dev/null; then - echo "RUNNING (PID $PID)" - else - echo "STALE (PID $PID)" - fi - else - echo "IDLE" - fi - ` - - sshCmd := agentci.SecureSSHCommand(ac.Host, script) - sshCmd.Stdout = os.Stdout - sshCmd.Stderr = os.Stderr - return sshCmd.Run() - }, - } -} - -func agentLogsCmd() *cli.Command { - cmd := &cli.Command{ - Use: "logs ", - Short: "Stream agent runner logs", - Args: cli.ExactArgs(1), - RunE: func(cmd *cli.Command, args []string) error { - name := args[0] - follow, _ := cmd.Flags().GetBool("follow") - lines, _ := cmd.Flags().GetInt("lines") - - cfg, err := loadConfig() - if err != nil { - return err - } - - agents, err := agentci.ListAgents(cfg) - if err != nil { - return err - } - ac, ok := agents[name] - if !ok { - return fmt.Errorf("agent %q not found", name) - } - - remoteCmd := fmt.Sprintf("tail -n %d ~/ai-work/logs/runner.log", lines) - if follow { - remoteCmd = fmt.Sprintf("tail -f -n %d ~/ai-work/logs/runner.log", lines) - } - - sshCmd := agentci.SecureSSHCommand(ac.Host, remoteCmd) - sshCmd.Stdout = os.Stdout - sshCmd.Stderr = os.Stderr - sshCmd.Stdin = os.Stdin - return sshCmd.Run() - }, - } - cmd.Flags().BoolP("follow", "f", false, "Follow log output") - cmd.Flags().IntP("lines", "n", 50, "Number of lines to show") - return cmd -} - -func agentSetupCmd() *cli.Command { - return &cli.Command{ - Use: "setup ", - Short: "Bootstrap agent machine (create dirs, copy runner, install cron)", - Args: cli.ExactArgs(1), - RunE: func(cmd *cli.Command, args []string) error { - name := args[0] - cfg, err := loadConfig() - if err != nil { - return err - } - - agents, err := agentci.ListAgents(cfg) - if err != nil { - return err - } - ac, ok := agents[name] - if !ok { - return fmt.Errorf("agent %q not found — use 'core ai agent add' first", name) - } - - // Find the setup script relative to the binary or in known locations. - scriptPath := findSetupScript() - if scriptPath == "" { - return fmt.Errorf("agent-setup.sh not found — expected in scripts/ directory") - } - - fmt.Printf("Setting up %s on %s...\n", name, ac.Host) - setupCmd := exec.Command("bash", scriptPath, ac.Host) - setupCmd.Stdout = os.Stdout - setupCmd.Stderr = os.Stderr - if err := setupCmd.Run(); err != nil { - return fmt.Errorf("setup failed: %w", err) - } - - fmt.Println(successStyle.Render("Setup complete!")) - return nil - }, - } -} - -func agentRemoveCmd() *cli.Command { - return &cli.Command{ - Use: "remove ", - Short: "Remove an agent from config", - Args: cli.ExactArgs(1), - RunE: func(cmd *cli.Command, args []string) error { - name := args[0] - cfg, err := loadConfig() - if err != nil { - return err - } - - if err := agentci.RemoveAgent(cfg, name); err != nil { - return err - } - - fmt.Printf("Agent %s removed.\n", name) - return nil - }, - } -} - -func agentFleetCmd() *cli.Command { - cmd := &cli.Command{ - Use: "fleet", - Short: "Show fleet status from the go-agentic registry", - RunE: func(cmd *cli.Command, args []string) error { - workDir, _ := cmd.Flags().GetString("work-dir") - if workDir == "" { - home, _ := os.UserHomeDir() - workDir = filepath.Join(home, defaultWorkDir) - } - dbPath := filepath.Join(workDir, "registry.db") - - if _, err := os.Stat(dbPath); os.IsNotExist(err) { - fmt.Println(dimStyle.Render("No registry found. Start a dispatch watcher first: core ai dispatch watch")) - return nil - } - - registry, err := agentic.NewSQLiteRegistry(dbPath) - if err != nil { - return fmt.Errorf("failed to open registry: %w", err) - } - defer registry.Close() - - // Reap stale agents (no heartbeat for 10 minutes). - reaped := registry.Reap(10 * time.Minute) - if len(reaped) > 0 { - for _, id := range reaped { - fmt.Printf(" Reaped stale agent: %s\n", dimStyle.Render(id)) - } - fmt.Println() - } - - agents := registry.List() - if len(agents) == 0 { - fmt.Println(dimStyle.Render("No agents registered.")) - return nil - } - - table := cli.NewTable("ID", "STATUS", "LOAD", "LAST HEARTBEAT", "CAPABILITIES") - for _, a := range agents { - status := dimStyle.Render(string(a.Status)) - switch a.Status { - case agentic.AgentAvailable: - status = successStyle.Render("available") - case agentic.AgentBusy: - status = taskPriorityMediumStyle.Render("busy") - case agentic.AgentOffline: - status = errorStyle.Render("offline") - } - - load := fmt.Sprintf("%d/%d", a.CurrentLoad, a.MaxLoad) - hb := a.LastHeartbeat.Format("15:04:05") - ago := time.Since(a.LastHeartbeat).Truncate(time.Second) - hbStr := fmt.Sprintf("%s (%s ago)", hb, ago) - - caps := "-" - if len(a.Capabilities) > 0 { - caps = strings.Join(a.Capabilities, ", ") - } - - table.AddRow(a.ID, status, load, hbStr, caps) - } - table.Render() - return nil - }, - } - cmd.Flags().String("work-dir", "", "Working directory (default: ~/ai-work)") - return cmd -} - -// findSetupScript looks for agent-setup.sh in common locations. -func findSetupScript() string { - exe, _ := os.Executable() - if exe != "" { - dir := filepath.Dir(exe) - candidates := []string{ - filepath.Join(dir, "scripts", "agent-setup.sh"), - filepath.Join(dir, "..", "scripts", "agent-setup.sh"), - } - for _, c := range candidates { - if _, err := os.Stat(c); err == nil { - return c - } - } - } - - cwd, _ := os.Getwd() - if cwd != "" { - p := filepath.Join(cwd, "scripts", "agent-setup.sh") - if _, err := os.Stat(p); err == nil { - return p - } - } - - return "" -} diff --git a/cmd/ai/cmd_ai.go b/cmd/ai/cmd_ai.go deleted file mode 100644 index 7ec17f78..00000000 --- a/cmd/ai/cmd_ai.go +++ /dev/null @@ -1,49 +0,0 @@ -// cmd_ai.go defines styles and the AddAgenticCommands function for AI task management. - -package ai - -import ( - "forge.lthn.ai/core/go/pkg/cli" -) - -// Style aliases from shared package -var ( - successStyle = cli.SuccessStyle - errorStyle = cli.ErrorStyle - dimStyle = cli.DimStyle - truncate = cli.Truncate - formatAge = cli.FormatAge -) - -// Task priority/status styles from shared -var ( - taskPriorityHighStyle = cli.NewStyle().Foreground(cli.ColourRed500) - taskPriorityMediumStyle = cli.NewStyle().Foreground(cli.ColourAmber500) - taskPriorityLowStyle = cli.NewStyle().Foreground(cli.ColourBlue400) - taskStatusPendingStyle = cli.DimStyle - taskStatusInProgressStyle = cli.NewStyle().Foreground(cli.ColourBlue500) - taskStatusCompletedStyle = cli.SuccessStyle - taskStatusBlockedStyle = cli.ErrorStyle -) - -// Task-specific styles (aliases to shared where possible) -var ( - taskIDStyle = cli.TitleStyle // Bold + blue - taskTitleStyle = cli.ValueStyle // Light gray - taskLabelStyle = cli.NewStyle().Foreground(cli.ColourViolet500) // Violet for labels -) - -// AddAgenticCommands adds the agentic task management commands to the ai command. -func AddAgenticCommands(parent *cli.Command) { - // Task listing and viewing - addTasksCommand(parent) - addTaskCommand(parent) - - // Task updates - addTaskUpdateCommand(parent) - addTaskCompleteCommand(parent) - - // Git integration - addTaskCommitCommand(parent) - addTaskPRCommand(parent) -} diff --git a/cmd/ai/cmd_commands.go b/cmd/ai/cmd_commands.go deleted file mode 100644 index 25c224bf..00000000 --- a/cmd/ai/cmd_commands.go +++ /dev/null @@ -1,94 +0,0 @@ -// Package ai provides AI agent task management and Claude Code integration. -// -// Commands: -// - tasks: List tasks from the agentic service -// - task: View, claim, or auto-select tasks -// - task:update: Update task status and progress -// - task:complete: Mark tasks as completed or failed -// - task:commit: Create commits with task references -// - task:pr: Create pull requests linked to tasks -// - claude: Claude Code CLI integration (planned) -// - rag: RAG tools (ingest, query, collections) -// - metrics: View AI/security event metrics -package ai - -import ( - ragcmd "forge.lthn.ai/core/go-rag/cmd/rag" - "forge.lthn.ai/core/go/pkg/cli" - "forge.lthn.ai/core/go/pkg/i18n" -) - -func init() { - cli.RegisterCommands(AddAICommands) -} - -var aiCmd = &cli.Command{ - Use: "ai", - Short: i18n.T("cmd.ai.short"), - Long: i18n.T("cmd.ai.long"), -} - -var claudeCmd = &cli.Command{ - Use: "claude", - Short: i18n.T("cmd.ai.claude.short"), - Long: i18n.T("cmd.ai.claude.long"), -} - -var claudeRunCmd = &cli.Command{ - Use: "run", - Short: i18n.T("cmd.ai.claude.run.short"), - RunE: func(cmd *cli.Command, args []string) error { - return runClaudeCode() - }, -} - -var claudeConfigCmd = &cli.Command{ - Use: "config", - Short: i18n.T("cmd.ai.claude.config.short"), - RunE: func(cmd *cli.Command, args []string) error { - return showClaudeConfig() - }, -} - -func initCommands() { - // Add Claude subcommands - claudeCmd.AddCommand(claudeRunCmd) - claudeCmd.AddCommand(claudeConfigCmd) - - // Add Claude command to ai - aiCmd.AddCommand(claudeCmd) - - // Add agentic task commands - AddAgenticCommands(aiCmd) - - // Add RAG subcommands (core ai rag ...) - ragcmd.AddRAGSubcommands(aiCmd) - - // Add metrics subcommand (core ai metrics) - addMetricsCommand(aiCmd) - - // Add agent management commands (core ai agent ...) - AddAgentCommands(aiCmd) - - // Add rate limit management commands (core ai ratelimits ...) - AddRateLimitCommands(aiCmd) - - // Add dispatch commands (core ai dispatch run/watch/status) - AddDispatchCommands(aiCmd) -} - -// AddAICommands registers the 'ai' command and all subcommands. -func AddAICommands(root *cli.Command) { - initCommands() - root.AddCommand(aiCmd) -} - -func runClaudeCode() error { - // Placeholder - will integrate with claude CLI - return nil -} - -func showClaudeConfig() error { - // Placeholder - will show claude configuration - return nil -} diff --git a/cmd/ai/cmd_dispatch.go b/cmd/ai/cmd_dispatch.go deleted file mode 100644 index 2c817d0d..00000000 --- a/cmd/ai/cmd_dispatch.go +++ /dev/null @@ -1,675 +0,0 @@ -package ai - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "net/http" - "os" - "os/exec" - "os/signal" - "path/filepath" - "sort" - "strconv" - "strings" - "syscall" - "time" - - "forge.lthn.ai/core/go/pkg/cli" - "forge.lthn.ai/core/go/pkg/log" - - agentic "forge.lthn.ai/core/go-agentic" -) - -// AddDispatchCommands registers the 'dispatch' subcommand group under 'ai'. -// These commands run ON the agent machine to process the work queue. -func AddDispatchCommands(parent *cli.Command) { - dispatchCmd := &cli.Command{ - Use: "dispatch", - Short: "Agent work queue processor (runs on agent machine)", - } - - dispatchCmd.AddCommand(dispatchRunCmd()) - dispatchCmd.AddCommand(dispatchWatchCmd()) - dispatchCmd.AddCommand(dispatchStatusCmd()) - - parent.AddCommand(dispatchCmd) -} - -// dispatchTicket represents the work item JSON structure. -type dispatchTicket struct { - ID string `json:"id"` - RepoOwner string `json:"repo_owner"` - RepoName string `json:"repo_name"` - IssueNumber int `json:"issue_number"` - IssueTitle string `json:"issue_title"` - IssueBody string `json:"issue_body"` - TargetBranch string `json:"target_branch"` - EpicNumber int `json:"epic_number"` - ForgeURL string `json:"forge_url"` - ForgeToken string `json:"forge_token"` - ForgeUser string `json:"forgejo_user"` - Model string `json:"model"` - Runner string `json:"runner"` - Timeout string `json:"timeout"` - CreatedAt string `json:"created_at"` -} - -const ( - defaultWorkDir = "ai-work" - lockFileName = ".runner.lock" -) - -type runnerPaths struct { - root string - queue string - active string - done string - logs string - jobs string - lock string -} - -func getPaths(baseDir string) runnerPaths { - if baseDir == "" { - home, _ := os.UserHomeDir() - baseDir = filepath.Join(home, defaultWorkDir) - } - return runnerPaths{ - root: baseDir, - queue: filepath.Join(baseDir, "queue"), - active: filepath.Join(baseDir, "active"), - done: filepath.Join(baseDir, "done"), - logs: filepath.Join(baseDir, "logs"), - jobs: filepath.Join(baseDir, "jobs"), - lock: filepath.Join(baseDir, lockFileName), - } -} - -func dispatchRunCmd() *cli.Command { - cmd := &cli.Command{ - Use: "run", - Short: "Process a single ticket from the queue", - RunE: func(cmd *cli.Command, args []string) error { - workDir, _ := cmd.Flags().GetString("work-dir") - paths := getPaths(workDir) - - if err := ensureDispatchDirs(paths); err != nil { - return err - } - - if err := acquireLock(paths.lock); err != nil { - log.Info("Runner locked, skipping run", "lock", paths.lock) - return nil - } - defer releaseLock(paths.lock) - - ticketFile, err := pickOldestTicket(paths.queue) - if err != nil { - return err - } - if ticketFile == "" { - return nil - } - - _, err = processTicket(paths, ticketFile) - return err - }, - } - cmd.Flags().String("work-dir", "", "Working directory (default: ~/ai-work)") - return cmd -} - -// fastFailThreshold is how quickly a job must fail to be considered rate-limited. -// Real work always takes longer than 30 seconds; a 3-second exit means the CLI -// was rejected before it could start (rate limit, auth error, etc.). -const fastFailThreshold = 30 * time.Second - -// maxBackoffMultiplier caps the exponential backoff at 8x the base interval. -const maxBackoffMultiplier = 8 - -func dispatchWatchCmd() *cli.Command { - cmd := &cli.Command{ - Use: "watch", - Short: "Run as a daemon, polling the queue", - RunE: func(cmd *cli.Command, args []string) error { - workDir, _ := cmd.Flags().GetString("work-dir") - interval, _ := cmd.Flags().GetDuration("interval") - agentID, _ := cmd.Flags().GetString("agent-id") - paths := getPaths(workDir) - - if err := ensureDispatchDirs(paths); err != nil { - return err - } - - // Register this agent in the go-agentic registry. - registry, events, cleanup := registerAgent(agentID, paths) - if cleanup != nil { - defer cleanup() - } - - log.Info("Starting dispatch watcher", "dir", paths.root, "interval", interval, "agent", agentID) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) - - // Heartbeat loop — keeps agent status fresh. - if registry != nil { - go heartbeatLoop(ctx, registry, agentID, interval/2) - } - - // Backoff state: consecutive fast failures increase the poll delay. - backoffMultiplier := 1 - currentInterval := interval - - ticker := time.NewTicker(currentInterval) - defer ticker.Stop() - - adjustTicker := func(fastFail bool) { - if fastFail { - if backoffMultiplier < maxBackoffMultiplier { - backoffMultiplier *= 2 - } - currentInterval = interval * time.Duration(backoffMultiplier) - log.Warn("Fast failure detected, backing off", - "multiplier", backoffMultiplier, "next_poll", currentInterval) - } else { - if backoffMultiplier > 1 { - log.Info("Job succeeded, resetting backoff") - } - backoffMultiplier = 1 - currentInterval = interval - } - ticker.Reset(currentInterval) - } - - fastFail := runCycleWithEvents(paths, registry, events, agentID) - adjustTicker(fastFail) - - for { - select { - case <-ticker.C: - ff := runCycleWithEvents(paths, registry, events, agentID) - adjustTicker(ff) - case <-sigChan: - log.Info("Shutting down watcher...") - if registry != nil { - _ = registry.Deregister(agentID) - } - return nil - case <-ctx.Done(): - return nil - } - } - }, - } - cmd.Flags().String("work-dir", "", "Working directory (default: ~/ai-work)") - cmd.Flags().Duration("interval", 5*time.Minute, "Polling interval") - cmd.Flags().String("agent-id", defaultAgentID(), "Agent identifier for registry") - return cmd -} - -// defaultAgentID returns a sensible agent ID from hostname. -func defaultAgentID() string { - host, _ := os.Hostname() - if host == "" { - return "unknown" - } - return host -} - -// registerAgent creates a SQLite registry and registers this agent. -// Returns the registry, event emitter, and a cleanup function. -func registerAgent(agentID string, paths runnerPaths) (agentic.AgentRegistry, agentic.EventEmitter, func()) { - dbPath := filepath.Join(paths.root, "registry.db") - registry, err := agentic.NewSQLiteRegistry(dbPath) - if err != nil { - log.Warn("Failed to create agent registry", "error", err, "path", dbPath) - return nil, nil, nil - } - - info := agentic.AgentInfo{ - ID: agentID, - Name: agentID, - Status: agentic.AgentAvailable, - LastHeartbeat: time.Now().UTC(), - MaxLoad: 1, - } - if err := registry.Register(info); err != nil { - log.Warn("Failed to register agent", "error", err) - } else { - log.Info("Agent registered", "id", agentID) - } - - events := agentic.NewChannelEmitter(64) - - // Drain events to log. - go func() { - for ev := range events.Events() { - log.Debug("Event", "type", string(ev.Type), "task", ev.TaskID, "agent", ev.AgentID) - } - }() - - return registry, events, func() { - events.Close() - } -} - -// heartbeatLoop sends periodic heartbeats to keep the agent status fresh. -func heartbeatLoop(ctx context.Context, registry agentic.AgentRegistry, agentID string, interval time.Duration) { - if interval < 30*time.Second { - interval = 30 * time.Second - } - ticker := time.NewTicker(interval) - defer ticker.Stop() - for { - select { - case <-ctx.Done(): - return - case <-ticker.C: - _ = registry.Heartbeat(agentID) - } - } -} - -// runCycleWithEvents wraps runCycle with registry status updates and event emission. -// Returns true if the cycle resulted in a fast failure (likely rate-limited). -func runCycleWithEvents(paths runnerPaths, registry agentic.AgentRegistry, events agentic.EventEmitter, agentID string) bool { - if registry != nil { - if agent, err := registry.Get(agentID); err == nil { - agent.Status = agentic.AgentBusy - _ = registry.Register(agent) - } - } - - fastFail := runCycle(paths) - - if registry != nil { - if agent, err := registry.Get(agentID); err == nil { - agent.Status = agentic.AgentAvailable - agent.LastHeartbeat = time.Now().UTC() - _ = registry.Register(agent) - } - } - return fastFail -} - -func dispatchStatusCmd() *cli.Command { - cmd := &cli.Command{ - Use: "status", - Short: "Show runner status", - RunE: func(cmd *cli.Command, args []string) error { - workDir, _ := cmd.Flags().GetString("work-dir") - paths := getPaths(workDir) - - lockStatus := "IDLE" - if data, err := os.ReadFile(paths.lock); err == nil { - pidStr := strings.TrimSpace(string(data)) - pid, _ := strconv.Atoi(pidStr) - if isProcessAlive(pid) { - lockStatus = fmt.Sprintf("RUNNING (PID %d)", pid) - } else { - lockStatus = fmt.Sprintf("STALE (PID %d)", pid) - } - } - - countFiles := func(dir string) int { - entries, _ := os.ReadDir(dir) - count := 0 - for _, e := range entries { - if !e.IsDir() && strings.HasPrefix(e.Name(), "ticket-") { - count++ - } - } - return count - } - - fmt.Println("=== Agent Dispatch Status ===") - fmt.Printf("Work Dir: %s\n", paths.root) - fmt.Printf("Status: %s\n", lockStatus) - fmt.Printf("Queue: %d\n", countFiles(paths.queue)) - fmt.Printf("Active: %d\n", countFiles(paths.active)) - fmt.Printf("Done: %d\n", countFiles(paths.done)) - - return nil - }, - } - cmd.Flags().String("work-dir", "", "Working directory (default: ~/ai-work)") - return cmd -} - -// runCycle picks and processes one ticket. Returns true if the job fast-failed -// (likely rate-limited), signalling the caller to back off. -func runCycle(paths runnerPaths) bool { - if err := acquireLock(paths.lock); err != nil { - log.Debug("Runner locked, skipping cycle") - return false - } - defer releaseLock(paths.lock) - - ticketFile, err := pickOldestTicket(paths.queue) - if err != nil { - log.Error("Failed to pick ticket", "error", err) - return false - } - if ticketFile == "" { - return false // empty queue, no backoff needed - } - - start := time.Now() - success, err := processTicket(paths, ticketFile) - elapsed := time.Since(start) - - if err != nil { - log.Error("Failed to process ticket", "file", ticketFile, "error", err) - } - - // Detect fast failure: job failed in under 30s → likely rate-limited. - if !success && elapsed < fastFailThreshold { - log.Warn("Job finished too fast, likely rate-limited", - "elapsed", elapsed.Round(time.Second), "file", filepath.Base(ticketFile)) - return true - } - - return false -} - -// processTicket processes a single ticket. Returns (success, error). -// On fast failure the caller is responsible for detecting the timing and backing off. -// The ticket is moved active→done on completion, or active→queue on fast failure. -func processTicket(paths runnerPaths, ticketPath string) (bool, error) { - fileName := filepath.Base(ticketPath) - log.Info("Processing ticket", "file", fileName) - - activePath := filepath.Join(paths.active, fileName) - if err := os.Rename(ticketPath, activePath); err != nil { - return false, fmt.Errorf("failed to move ticket to active: %w", err) - } - - data, err := os.ReadFile(activePath) - if err != nil { - return false, fmt.Errorf("failed to read ticket: %w", err) - } - var t dispatchTicket - if err := json.Unmarshal(data, &t); err != nil { - return false, fmt.Errorf("failed to unmarshal ticket: %w", err) - } - - jobDir := filepath.Join(paths.jobs, fmt.Sprintf("%s-%s-%d", t.RepoOwner, t.RepoName, t.IssueNumber)) - repoDir := filepath.Join(jobDir, t.RepoName) - if err := os.MkdirAll(jobDir, 0755); err != nil { - return false, err - } - - if err := prepareRepo(t, repoDir); err != nil { - reportToForge(t, false, fmt.Sprintf("Git setup failed: %v", err)) - moveToDone(paths, activePath, fileName) - return false, err - } - - prompt := buildPrompt(t) - - logFile := filepath.Join(paths.logs, fmt.Sprintf("%s-%s-%d.log", t.RepoOwner, t.RepoName, t.IssueNumber)) - start := time.Now() - success, exitCode, runErr := runAgent(t, prompt, repoDir, logFile) - elapsed := time.Since(start) - - // Fast failure: agent exited in <30s without success → likely rate-limited. - // Requeue the ticket so it's retried after the backoff period. - if !success && elapsed < fastFailThreshold { - log.Warn("Agent rejected fast, requeuing ticket", "elapsed", elapsed.Round(time.Second), "file", fileName) - requeuePath := filepath.Join(paths.queue, fileName) - if err := os.Rename(activePath, requeuePath); err != nil { - // Fallback: move to done if requeue fails. - moveToDone(paths, activePath, fileName) - } - return false, runErr - } - - msg := fmt.Sprintf("Agent completed work on #%d. Exit code: %d.", t.IssueNumber, exitCode) - if !success { - msg = fmt.Sprintf("Agent failed on #%d (exit code: %d). Check logs on agent machine.", t.IssueNumber, exitCode) - if runErr != nil { - msg += fmt.Sprintf(" Error: %v", runErr) - } - } - reportToForge(t, success, msg) - - moveToDone(paths, activePath, fileName) - log.Info("Ticket complete", "id", t.ID, "success", success, "elapsed", elapsed.Round(time.Second)) - return success, nil -} - -func prepareRepo(t dispatchTicket, repoDir string) error { - user := t.ForgeUser - if user == "" { - host, _ := os.Hostname() - user = fmt.Sprintf("%s-%s", host, os.Getenv("USER")) - } - - cleanURL := strings.TrimPrefix(t.ForgeURL, "https://") - cleanURL = strings.TrimPrefix(cleanURL, "http://") - cloneURL := fmt.Sprintf("https://%s:%s@%s/%s/%s.git", user, t.ForgeToken, cleanURL, t.RepoOwner, t.RepoName) - - if _, err := os.Stat(filepath.Join(repoDir, ".git")); err == nil { - log.Info("Updating existing repo", "dir", repoDir) - cmds := [][]string{ - {"git", "fetch", "origin"}, - {"git", "checkout", t.TargetBranch}, - {"git", "pull", "origin", t.TargetBranch}, - } - for _, args := range cmds { - cmd := exec.Command(args[0], args[1:]...) - cmd.Dir = repoDir - if out, err := cmd.CombinedOutput(); err != nil { - if args[1] == "checkout" { - createCmd := exec.Command("git", "checkout", "-b", t.TargetBranch, "origin/"+t.TargetBranch) - createCmd.Dir = repoDir - if _, err2 := createCmd.CombinedOutput(); err2 == nil { - continue - } - } - return fmt.Errorf("git command %v failed: %s", args, string(out)) - } - } - } else { - log.Info("Cloning repo", "url", t.RepoOwner+"/"+t.RepoName) - cmd := exec.Command("git", "clone", "-b", t.TargetBranch, cloneURL, repoDir) - if out, err := cmd.CombinedOutput(); err != nil { - return fmt.Errorf("git clone failed: %s", string(out)) - } - } - return nil -} - -func buildPrompt(t dispatchTicket) string { - return fmt.Sprintf(`You are working on issue #%d in %s/%s. - -Title: %s - -Description: -%s - -The repo is cloned at the current directory on branch '%s'. -Create a feature branch from '%s', make minimal targeted changes, commit referencing #%d, and push. -Then create a PR targeting '%s' using the forgejo MCP tools or git push.`, - t.IssueNumber, t.RepoOwner, t.RepoName, - t.IssueTitle, - t.IssueBody, - t.TargetBranch, - t.TargetBranch, t.IssueNumber, - t.TargetBranch, - ) -} - -func runAgent(t dispatchTicket, prompt, dir, logPath string) (bool, int, error) { - timeout := 30 * time.Minute - if t.Timeout != "" { - if d, err := time.ParseDuration(t.Timeout); err == nil { - timeout = d - } - } - - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - model := t.Model - if model == "" { - model = "sonnet" - } - - log.Info("Running agent", "runner", t.Runner, "model", model) - - // For Gemini runner, wrap with rate limiting. - if t.Runner == "gemini" { - return executeWithRateLimit(ctx, model, prompt, func() (bool, int, error) { - return execAgent(ctx, t.Runner, model, prompt, dir, logPath) - }) - } - - return execAgent(ctx, t.Runner, model, prompt, dir, logPath) -} - -func execAgent(ctx context.Context, runner, model, prompt, dir, logPath string) (bool, int, error) { - var cmd *exec.Cmd - - switch runner { - case "codex": - cmd = exec.CommandContext(ctx, "codex", "exec", "--full-auto", prompt) - case "gemini": - args := []string{"-p", "-", "-y", "-m", model} - cmd = exec.CommandContext(ctx, "gemini", args...) - cmd.Stdin = strings.NewReader(prompt) - default: // claude - cmd = exec.CommandContext(ctx, "claude", "-p", "--model", model, "--dangerously-skip-permissions", "--output-format", "text") - cmd.Stdin = strings.NewReader(prompt) - } - - cmd.Dir = dir - - f, err := os.Create(logPath) - if err != nil { - return false, -1, err - } - defer f.Close() - - cmd.Stdout = f - cmd.Stderr = f - - if err := cmd.Run(); err != nil { - exitCode := -1 - if exitErr, ok := err.(*exec.ExitError); ok { - exitCode = exitErr.ExitCode() - } - return false, exitCode, err - } - - return true, 0, nil -} - -func reportToForge(t dispatchTicket, success bool, body string) { - token := t.ForgeToken - if token == "" { - token = os.Getenv("FORGE_TOKEN") - } - if token == "" { - log.Warn("No forge token available, skipping report") - return - } - - url := fmt.Sprintf("%s/api/v1/repos/%s/%s/issues/%d/comments", - strings.TrimSuffix(t.ForgeURL, "/"), t.RepoOwner, t.RepoName, t.IssueNumber) - - payload := map[string]string{"body": body} - jsonBody, _ := json.Marshal(payload) - - req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBody)) - if err != nil { - log.Error("Failed to create request", "err", err) - return - } - req.Header.Set("Authorization", "token "+token) - req.Header.Set("Content-Type", "application/json") - - client := &http.Client{Timeout: 10 * time.Second} - resp, err := client.Do(req) - if err != nil { - log.Error("Failed to report to Forge", "err", err) - return - } - defer resp.Body.Close() - - if resp.StatusCode >= 300 { - log.Warn("Forge reported error", "status", resp.Status) - } -} - -func moveToDone(paths runnerPaths, activePath, fileName string) { - donePath := filepath.Join(paths.done, fileName) - if err := os.Rename(activePath, donePath); err != nil { - log.Error("Failed to move ticket to done", "err", err) - } -} - -func ensureDispatchDirs(p runnerPaths) error { - dirs := []string{p.queue, p.active, p.done, p.logs, p.jobs} - for _, d := range dirs { - if err := os.MkdirAll(d, 0755); err != nil { - return fmt.Errorf("mkdir %s failed: %w", d, err) - } - } - return nil -} - -func acquireLock(lockPath string) error { - if data, err := os.ReadFile(lockPath); err == nil { - pidStr := strings.TrimSpace(string(data)) - pid, _ := strconv.Atoi(pidStr) - if isProcessAlive(pid) { - return fmt.Errorf("locked by PID %d", pid) - } - log.Info("Removing stale lock", "pid", pid) - _ = os.Remove(lockPath) - } - - return os.WriteFile(lockPath, []byte(fmt.Sprintf("%d", os.Getpid())), 0644) -} - -func releaseLock(lockPath string) { - _ = os.Remove(lockPath) -} - -func isProcessAlive(pid int) bool { - if pid <= 0 { - return false - } - process, err := os.FindProcess(pid) - if err != nil { - return false - } - return process.Signal(syscall.Signal(0)) == nil -} - -func pickOldestTicket(queueDir string) (string, error) { - entries, err := os.ReadDir(queueDir) - if err != nil { - return "", err - } - - var tickets []string - for _, e := range entries { - if !e.IsDir() && strings.HasPrefix(e.Name(), "ticket-") && strings.HasSuffix(e.Name(), ".json") { - tickets = append(tickets, filepath.Join(queueDir, e.Name())) - } - } - - if len(tickets) == 0 { - return "", nil - } - - sort.Strings(tickets) - return tickets[0], nil -} diff --git a/cmd/ai/cmd_git.go b/cmd/ai/cmd_git.go deleted file mode 100644 index da3c1c9b..00000000 --- a/cmd/ai/cmd_git.go +++ /dev/null @@ -1,248 +0,0 @@ -// cmd_git.go implements git integration commands for task commits and PRs. - -package ai - -import ( - "bytes" - "context" - "os" - "os/exec" - "strings" - "time" - - "forge.lthn.ai/core/go-agentic" - "forge.lthn.ai/core/go/pkg/cli" - "forge.lthn.ai/core/go/pkg/i18n" -) - -// task:commit command flags -var ( - taskCommitMessage string - taskCommitScope string - taskCommitPush bool -) - -// task:pr command flags -var ( - taskPRTitle string - taskPRDraft bool - taskPRLabels string - taskPRBase string -) - -var taskCommitCmd = &cli.Command{ - Use: "task:commit [task-id]", - Short: i18n.T("cmd.ai.task_commit.short"), - Long: i18n.T("cmd.ai.task_commit.long"), - Args: cli.ExactArgs(1), - RunE: func(cmd *cli.Command, args []string) error { - taskID := args[0] - - if taskCommitMessage == "" { - return cli.Err("commit message required") - } - - cfg, err := agentic.LoadConfig("") - if err != nil { - return cli.WrapVerb(err, "load", "config") - } - - client := agentic.NewClientFromConfig(cfg) - - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - // Get task details - task, err := client.GetTask(ctx, taskID) - if err != nil { - return cli.WrapVerb(err, "get", "task") - } - - // Build commit message with optional scope - commitType := inferCommitType(task.Labels) - var fullMessage string - if taskCommitScope != "" { - fullMessage = cli.Sprintf("%s(%s): %s", commitType, taskCommitScope, taskCommitMessage) - } else { - fullMessage = cli.Sprintf("%s: %s", commitType, taskCommitMessage) - } - - // Get current directory - cwd, err := os.Getwd() - if err != nil { - return cli.WrapVerb(err, "get", "working directory") - } - - // Check for uncommitted changes - hasChanges, err := agentic.HasUncommittedChanges(ctx, cwd) - if err != nil { - return cli.WrapVerb(err, "check", "git status") - } - - if !hasChanges { - cli.Println("No changes to commit") - return nil - } - - // Create commit - cli.Print("%s %s\n", dimStyle.Render(">>"), i18n.ProgressSubject("create", "commit for "+taskID)) - if err := agentic.AutoCommit(ctx, task, cwd, fullMessage); err != nil { - return cli.WrapAction(err, "commit") - } - - cli.Print("%s %s %s\n", successStyle.Render(">>"), i18n.T("i18n.done.commit")+":", fullMessage) - - // Push if requested - if taskCommitPush { - cli.Print("%s %s\n", dimStyle.Render(">>"), i18n.Progress("push")) - if err := agentic.PushChanges(ctx, cwd); err != nil { - return cli.WrapAction(err, "push") - } - cli.Print("%s %s\n", successStyle.Render(">>"), i18n.T("i18n.done.push", "changes")) - } - - return nil - }, -} - -var taskPRCmd = &cli.Command{ - Use: "task:pr [task-id]", - Short: i18n.T("cmd.ai.task_pr.short"), - Long: i18n.T("cmd.ai.task_pr.long"), - Args: cli.ExactArgs(1), - RunE: func(cmd *cli.Command, args []string) error { - taskID := args[0] - - cfg, err := agentic.LoadConfig("") - if err != nil { - return cli.WrapVerb(err, "load", "config") - } - - client := agentic.NewClientFromConfig(cfg) - - ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) - defer cancel() - - // Get task details - task, err := client.GetTask(ctx, taskID) - if err != nil { - return cli.WrapVerb(err, "get", "task") - } - - // Get current directory - cwd, err := os.Getwd() - if err != nil { - return cli.WrapVerb(err, "get", "working directory") - } - - // Check current branch - branch, err := agentic.GetCurrentBranch(ctx, cwd) - if err != nil { - return cli.WrapVerb(err, "get", "branch") - } - - if branch == "main" || branch == "master" { - return cli.Err("cannot create PR from %s branch", branch) - } - - // Push current branch - cli.Print("%s %s\n", dimStyle.Render(">>"), i18n.ProgressSubject("push", branch)) - if err := agentic.PushChanges(ctx, cwd); err != nil { - // Try setting upstream - if _, err := runGitCommand(cwd, "push", "-u", "origin", branch); err != nil { - return cli.WrapVerb(err, "push", "branch") - } - } - - // Build PR options - opts := agentic.PROptions{ - Title: taskPRTitle, - Draft: taskPRDraft, - Base: taskPRBase, - } - - if taskPRLabels != "" { - opts.Labels = strings.Split(taskPRLabels, ",") - } - - // Create PR - cli.Print("%s %s\n", dimStyle.Render(">>"), i18n.ProgressSubject("create", "PR")) - prURL, err := agentic.CreatePR(ctx, task, cwd, opts) - if err != nil { - return cli.WrapVerb(err, "create", "PR") - } - - cli.Print("%s %s\n", successStyle.Render(">>"), i18n.T("i18n.done.create", "PR")) - cli.Print(" %s %s\n", i18n.Label("url"), prURL) - - return nil - }, -} - -func initGitFlags() { - // task:commit command flags - taskCommitCmd.Flags().StringVarP(&taskCommitMessage, "message", "m", "", i18n.T("cmd.ai.task_commit.flag.message")) - taskCommitCmd.Flags().StringVar(&taskCommitScope, "scope", "", i18n.T("cmd.ai.task_commit.flag.scope")) - taskCommitCmd.Flags().BoolVar(&taskCommitPush, "push", false, i18n.T("cmd.ai.task_commit.flag.push")) - - // task:pr command flags - taskPRCmd.Flags().StringVar(&taskPRTitle, "title", "", i18n.T("cmd.ai.task_pr.flag.title")) - taskPRCmd.Flags().BoolVar(&taskPRDraft, "draft", false, i18n.T("cmd.ai.task_pr.flag.draft")) - taskPRCmd.Flags().StringVar(&taskPRLabels, "labels", "", i18n.T("cmd.ai.task_pr.flag.labels")) - taskPRCmd.Flags().StringVar(&taskPRBase, "base", "", i18n.T("cmd.ai.task_pr.flag.base")) -} - -func addTaskCommitCommand(parent *cli.Command) { - initGitFlags() - parent.AddCommand(taskCommitCmd) -} - -func addTaskPRCommand(parent *cli.Command) { - parent.AddCommand(taskPRCmd) -} - -// inferCommitType infers the commit type from task labels. -func inferCommitType(labels []string) string { - for _, label := range labels { - switch strings.ToLower(label) { - case "bug", "bugfix", "fix": - return "fix" - case "docs", "documentation": - return "docs" - case "refactor", "refactoring": - return "refactor" - case "test", "tests", "testing": - return "test" - case "chore": - return "chore" - case "style": - return "style" - case "perf", "performance": - return "perf" - case "ci": - return "ci" - case "build": - return "build" - } - } - return "feat" -} - -// runGitCommand runs a git command in the specified directory. -func runGitCommand(dir string, args ...string) (string, error) { - cmd := exec.Command("git", args...) - cmd.Dir = dir - - var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - - if err := cmd.Run(); err != nil { - if stderr.Len() > 0 { - return "", cli.Wrap(err, stderr.String()) - } - return "", err - } - - return stdout.String(), nil -} diff --git a/cmd/ai/cmd_metrics.go b/cmd/ai/cmd_metrics.go deleted file mode 100644 index cb859b87..00000000 --- a/cmd/ai/cmd_metrics.go +++ /dev/null @@ -1,131 +0,0 @@ -// cmd_metrics.go implements the metrics viewing command. - -package ai - -import ( - "encoding/json" - "fmt" - "time" - - "forge.lthn.ai/core/go-ai/ai" - "forge.lthn.ai/core/go/pkg/cli" - "forge.lthn.ai/core/go/pkg/i18n" -) - -var ( - metricsSince string - metricsJSON bool -) - -var metricsCmd = &cli.Command{ - Use: "metrics", - Short: i18n.T("cmd.ai.metrics.short"), - Long: i18n.T("cmd.ai.metrics.long"), - RunE: func(cmd *cli.Command, args []string) error { - return runMetrics() - }, -} - -func initMetricsFlags() { - metricsCmd.Flags().StringVar(&metricsSince, "since", "7d", i18n.T("cmd.ai.metrics.flag.since")) - metricsCmd.Flags().BoolVar(&metricsJSON, "json", false, i18n.T("common.flag.json")) -} - -func addMetricsCommand(parent *cli.Command) { - initMetricsFlags() - parent.AddCommand(metricsCmd) -} - -func runMetrics() error { - since, err := parseDuration(metricsSince) - if err != nil { - return cli.Err("invalid --since value %q: %v", metricsSince, err) - } - - sinceTime := time.Now().Add(-since) - events, err := ai.ReadEvents(sinceTime) - if err != nil { - return cli.WrapVerb(err, "read", "metrics") - } - - if metricsJSON { - summary := ai.Summary(events) - output, err := json.MarshalIndent(summary, "", " ") - if err != nil { - return cli.Wrap(err, "marshal JSON output") - } - cli.Text(string(output)) - return nil - } - - summary := ai.Summary(events) - - cli.Blank() - cli.Print("%s %s\n", dimStyle.Render("Period:"), metricsSince) - total, _ := summary["total"].(int) - cli.Print("%s %d\n", dimStyle.Render("Total events:"), total) - cli.Blank() - - // By type - if byType, ok := summary["by_type"].([]map[string]any); ok && len(byType) > 0 { - cli.Print("%s\n", dimStyle.Render("By type:")) - for _, entry := range byType { - cli.Print(" %-30s %v\n", entry["key"], entry["count"]) - } - cli.Blank() - } - - // By repo - if byRepo, ok := summary["by_repo"].([]map[string]any); ok && len(byRepo) > 0 { - cli.Print("%s\n", dimStyle.Render("By repo:")) - for _, entry := range byRepo { - cli.Print(" %-30s %v\n", entry["key"], entry["count"]) - } - cli.Blank() - } - - // By agent - if byAgent, ok := summary["by_agent"].([]map[string]any); ok && len(byAgent) > 0 { - cli.Print("%s\n", dimStyle.Render("By contributor:")) - for _, entry := range byAgent { - cli.Print(" %-30s %v\n", entry["key"], entry["count"]) - } - cli.Blank() - } - - if len(events) == 0 { - cli.Text(i18n.T("cmd.ai.metrics.none_found")) - } - - return nil -} - -// parseDuration parses a human-friendly duration like "7d", "24h", "30d". -func parseDuration(s string) (time.Duration, error) { - if len(s) < 2 { - return 0, fmt.Errorf("invalid duration: %s", s) - } - - unit := s[len(s)-1] - value := s[:len(s)-1] - - var n int - if _, err := fmt.Sscanf(value, "%d", &n); err != nil { - return 0, fmt.Errorf("invalid duration: %s", s) - } - - if n <= 0 { - return 0, fmt.Errorf("duration must be positive: %s", s) - } - - switch unit { - case 'd': - return time.Duration(n) * 24 * time.Hour, nil - case 'h': - return time.Duration(n) * time.Hour, nil - case 'm': - return time.Duration(n) * time.Minute, nil - default: - return 0, fmt.Errorf("unknown unit %c in duration: %s", unit, s) - } -} diff --git a/cmd/ai/cmd_ratelimits.go b/cmd/ai/cmd_ratelimits.go deleted file mode 100644 index 2509f412..00000000 --- a/cmd/ai/cmd_ratelimits.go +++ /dev/null @@ -1,213 +0,0 @@ -package ai - -import ( - "fmt" - "os" - "strconv" - "text/tabwriter" - "time" - - "forge.lthn.ai/core/go/pkg/cli" - "forge.lthn.ai/core/go/pkg/config" - "forge.lthn.ai/core/go/pkg/ratelimit" -) - -// AddRateLimitCommands registers the 'ratelimits' subcommand group under 'ai'. -func AddRateLimitCommands(parent *cli.Command) { - rlCmd := &cli.Command{ - Use: "ratelimits", - Short: "Manage Gemini API rate limits", - } - - rlCmd.AddCommand(rlShowCmd()) - rlCmd.AddCommand(rlResetCmd()) - rlCmd.AddCommand(rlCountCmd()) - rlCmd.AddCommand(rlConfigCmd()) - rlCmd.AddCommand(rlCheckCmd()) - - parent.AddCommand(rlCmd) -} - -func rlShowCmd() *cli.Command { - return &cli.Command{ - Use: "show", - Short: "Show current rate limit usage", - RunE: func(cmd *cli.Command, args []string) error { - rl, err := ratelimit.New() - if err != nil { - return err - } - if err := rl.Load(); err != nil { - return err - } - - stats := rl.AllStats() - - w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) - fmt.Fprintln(w, "MODEL\tRPM\tTPM\tRPD\tSTATUS") - - for model, s := range stats { - rpmStr := fmt.Sprintf("%d/%s", s.RPM, formatLimit(s.MaxRPM)) - tpmStr := fmt.Sprintf("%d/%s", s.TPM, formatLimit(s.MaxTPM)) - rpdStr := fmt.Sprintf("%d/%s", s.RPD, formatLimit(s.MaxRPD)) - - status := "OK" - if (s.MaxRPM > 0 && s.RPM >= s.MaxRPM) || - (s.MaxTPM > 0 && s.TPM >= s.MaxTPM) || - (s.MaxRPD > 0 && s.RPD >= s.MaxRPD) { - status = "LIMITED" - } - - fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", model, rpmStr, tpmStr, rpdStr, status) - } - w.Flush() - return nil - }, - } -} - -func rlResetCmd() *cli.Command { - return &cli.Command{ - Use: "reset [model]", - Short: "Reset usage counters for a model (or all)", - RunE: func(cmd *cli.Command, args []string) error { - rl, err := ratelimit.New() - if err != nil { - return err - } - if err := rl.Load(); err != nil { - return err - } - - model := "" - if len(args) > 0 { - model = args[0] - } - - rl.Reset(model) - if err := rl.Persist(); err != nil { - return err - } - - if model == "" { - fmt.Println("Reset stats for all models.") - } else { - fmt.Printf("Reset stats for model %q.\n", model) - } - return nil - }, - } -} - -func rlCountCmd() *cli.Command { - return &cli.Command{ - Use: "count ", - Short: "Count tokens for text using Gemini API", - Args: cli.ExactArgs(2), - RunE: func(cmd *cli.Command, args []string) error { - model := args[0] - text := args[1] - - cfg, err := config.New() - if err != nil { - return err - } - - var apiKey string - if err := cfg.Get("agentci.gemini_api_key", &apiKey); err != nil || apiKey == "" { - apiKey = os.Getenv("GEMINI_API_KEY") - } - if apiKey == "" { - return fmt.Errorf("GEMINI_API_KEY not found in config or env") - } - - count, err := ratelimit.CountTokens(apiKey, model, text) - if err != nil { - return err - } - - fmt.Printf("Model: %s\nTokens: %d\n", model, count) - return nil - }, - } -} - -func rlConfigCmd() *cli.Command { - return &cli.Command{ - Use: "config", - Short: "Show configured quotas", - RunE: func(cmd *cli.Command, args []string) error { - rl, err := ratelimit.New() - if err != nil { - return err - } - - w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) - fmt.Fprintln(w, "MODEL\tMAX RPM\tMAX TPM\tMAX RPD") - - for model, q := range rl.Quotas { - fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", - model, - formatLimit(q.MaxRPM), - formatLimit(q.MaxTPM), - formatLimit(q.MaxRPD)) - } - w.Flush() - return nil - }, - } -} - -func rlCheckCmd() *cli.Command { - return &cli.Command{ - Use: "check ", - Short: "Check rate limit capacity for a model", - Args: cli.ExactArgs(2), - RunE: func(cmd *cli.Command, args []string) error { - model := args[0] - tokens, err := strconv.Atoi(args[1]) - if err != nil { - return fmt.Errorf("invalid token count: %w", err) - } - - rl, err := ratelimit.New() - if err != nil { - return err - } - if err := rl.Load(); err != nil { - fmt.Printf("Warning: could not load existing state: %v\n", err) - } - - stats := rl.Stats(model) - canSend := rl.CanSend(model, tokens) - - status := "RATE LIMITED" - if canSend { - status = "OK" - } - - fmt.Printf("Model: %s\n", model) - fmt.Printf("Request Cost: %d tokens\n", tokens) - fmt.Printf("Status: %s\n", status) - fmt.Printf("\nCurrent Usage (1m window):\n") - fmt.Printf(" RPM: %d / %s\n", stats.RPM, formatLimit(stats.MaxRPM)) - fmt.Printf(" TPM: %d / %s\n", stats.TPM, formatLimit(stats.MaxTPM)) - fmt.Printf(" RPD: %d / %s (reset: %s)\n", stats.RPD, formatLimit(stats.MaxRPD), stats.DayStart.Format(time.RFC3339)) - - return nil - }, - } -} - -func formatLimit(limit int) string { - if limit == 0 { - return "∞" - } - if limit >= 1000000 { - return fmt.Sprintf("%dM", limit/1000000) - } - if limit >= 1000 { - return fmt.Sprintf("%dK", limit/1000) - } - return fmt.Sprintf("%d", limit) -} diff --git a/cmd/ai/cmd_tasks.go b/cmd/ai/cmd_tasks.go deleted file mode 100644 index 856c091a..00000000 --- a/cmd/ai/cmd_tasks.go +++ /dev/null @@ -1,297 +0,0 @@ -// cmd_tasks.go implements task listing and viewing commands. - -package ai - -import ( - "context" - "os" - "sort" - "strings" - "time" - - "forge.lthn.ai/core/go-agentic" - "forge.lthn.ai/core/go-ai/ai" - "forge.lthn.ai/core/go/pkg/cli" - "forge.lthn.ai/core/go/pkg/i18n" -) - -// tasks command flags -var ( - tasksStatus string - tasksPriority string - tasksLabels string - tasksLimit int - tasksProject string -) - -// task command flags -var ( - taskAutoSelect bool - taskClaim bool - taskShowContext bool -) - -var tasksCmd = &cli.Command{ - Use: "tasks", - Short: i18n.T("cmd.ai.tasks.short"), - Long: i18n.T("cmd.ai.tasks.long"), - RunE: func(cmd *cli.Command, args []string) error { - limit := tasksLimit - if limit == 0 { - limit = 20 - } - - cfg, err := agentic.LoadConfig("") - if err != nil { - return cli.WrapVerb(err, "load", "config") - } - - client := agentic.NewClientFromConfig(cfg) - - opts := agentic.ListOptions{ - Limit: limit, - Project: tasksProject, - } - - if tasksStatus != "" { - opts.Status = agentic.TaskStatus(tasksStatus) - } - if tasksPriority != "" { - opts.Priority = agentic.TaskPriority(tasksPriority) - } - if tasksLabels != "" { - opts.Labels = strings.Split(tasksLabels, ",") - } - - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - tasks, err := client.ListTasks(ctx, opts) - if err != nil { - return cli.WrapVerb(err, "list", "tasks") - } - - if len(tasks) == 0 { - cli.Text(i18n.T("cmd.ai.tasks.none_found")) - return nil - } - - printTaskList(tasks) - return nil - }, -} - -var taskCmd = &cli.Command{ - Use: "task [task-id]", - Short: i18n.T("cmd.ai.task.short"), - Long: i18n.T("cmd.ai.task.long"), - RunE: func(cmd *cli.Command, args []string) error { - cfg, err := agentic.LoadConfig("") - if err != nil { - return cli.WrapVerb(err, "load", "config") - } - - client := agentic.NewClientFromConfig(cfg) - - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - var task *agentic.Task - - // Get the task ID from args - var taskID string - if len(args) > 0 { - taskID = args[0] - } - - if taskAutoSelect { - // Auto-select: find highest priority pending task - tasks, err := client.ListTasks(ctx, agentic.ListOptions{ - Status: agentic.StatusPending, - Limit: 50, - }) - if err != nil { - return cli.WrapVerb(err, "list", "tasks") - } - - if len(tasks) == 0 { - cli.Text(i18n.T("cmd.ai.task.no_pending")) - return nil - } - - // Sort by priority (critical > high > medium > low) - priorityOrder := map[agentic.TaskPriority]int{ - agentic.PriorityCritical: 0, - agentic.PriorityHigh: 1, - agentic.PriorityMedium: 2, - agentic.PriorityLow: 3, - } - - sort.Slice(tasks, func(i, j int) bool { - return priorityOrder[tasks[i].Priority] < priorityOrder[tasks[j].Priority] - }) - - task = &tasks[0] - taskClaim = true // Auto-select implies claiming - } else { - if taskID == "" { - return cli.Err("%s", i18n.T("cmd.ai.task.id_required")) - } - - task, err = client.GetTask(ctx, taskID) - if err != nil { - return cli.WrapVerb(err, "get", "task") - } - } - - // Show context if requested - if taskShowContext { - cwd, _ := os.Getwd() - taskCtx, err := agentic.BuildTaskContext(task, cwd) - if err != nil { - cli.Print("%s %s: %s\n", errorStyle.Render(">>"), i18n.T("i18n.fail.build", "context"), err) - } else { - cli.Text(taskCtx.FormatContext()) - } - } else { - printTaskDetails(task) - } - - if taskClaim && task.Status == agentic.StatusPending { - cli.Blank() - cli.Print("%s %s\n", dimStyle.Render(">>"), i18n.T("cmd.ai.task.claiming")) - - claimedTask, err := client.ClaimTask(ctx, task.ID) - if err != nil { - return cli.WrapVerb(err, "claim", "task") - } - - // Record task claim event - _ = ai.Record(ai.Event{ - Type: "task.claimed", - AgentID: cfg.AgentID, - Data: map[string]any{"task_id": task.ID, "title": task.Title}, - }) - - cli.Print("%s %s\n", successStyle.Render(">>"), i18n.T("i18n.done.claim", "task")) - cli.Print(" %s %s\n", i18n.Label("status"), formatTaskStatus(claimedTask.Status)) - } - - return nil - }, -} - -func initTasksFlags() { - // tasks command flags - tasksCmd.Flags().StringVar(&tasksStatus, "status", "", i18n.T("cmd.ai.tasks.flag.status")) - tasksCmd.Flags().StringVar(&tasksPriority, "priority", "", i18n.T("cmd.ai.tasks.flag.priority")) - tasksCmd.Flags().StringVar(&tasksLabels, "labels", "", i18n.T("cmd.ai.tasks.flag.labels")) - tasksCmd.Flags().IntVar(&tasksLimit, "limit", 20, i18n.T("cmd.ai.tasks.flag.limit")) - tasksCmd.Flags().StringVar(&tasksProject, "project", "", i18n.T("cmd.ai.tasks.flag.project")) - - // task command flags - taskCmd.Flags().BoolVar(&taskAutoSelect, "auto", false, i18n.T("cmd.ai.task.flag.auto")) - taskCmd.Flags().BoolVar(&taskClaim, "claim", false, i18n.T("cmd.ai.task.flag.claim")) - taskCmd.Flags().BoolVar(&taskShowContext, "context", false, i18n.T("cmd.ai.task.flag.context")) -} - -func addTasksCommand(parent *cli.Command) { - initTasksFlags() - parent.AddCommand(tasksCmd) -} - -func addTaskCommand(parent *cli.Command) { - parent.AddCommand(taskCmd) -} - -func printTaskList(tasks []agentic.Task) { - cli.Print("\n%s\n\n", i18n.T("cmd.ai.tasks.found", map[string]interface{}{"Count": len(tasks)})) - - for _, task := range tasks { - id := taskIDStyle.Render(task.ID) - title := taskTitleStyle.Render(truncate(task.Title, 50)) - priority := formatTaskPriority(task.Priority) - status := formatTaskStatus(task.Status) - - line := cli.Sprintf(" %s %s %s %s", id, priority, status, title) - - if len(task.Labels) > 0 { - labels := taskLabelStyle.Render("[" + strings.Join(task.Labels, ", ") + "]") - line += " " + labels - } - - cli.Text(line) - } - - cli.Blank() - cli.Print("%s\n", dimStyle.Render(i18n.T("cmd.ai.tasks.hint"))) -} - -func printTaskDetails(task *agentic.Task) { - cli.Blank() - cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.id")), taskIDStyle.Render(task.ID)) - cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.title")), taskTitleStyle.Render(task.Title)) - cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.priority")), formatTaskPriority(task.Priority)) - cli.Print("%s %s\n", dimStyle.Render(i18n.Label("status")), formatTaskStatus(task.Status)) - - if task.Project != "" { - cli.Print("%s %s\n", dimStyle.Render(i18n.Label("project")), task.Project) - } - - if len(task.Labels) > 0 { - cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.labels")), taskLabelStyle.Render(strings.Join(task.Labels, ", "))) - } - - if task.ClaimedBy != "" { - cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.claimed_by")), task.ClaimedBy) - } - - cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.created")), formatAge(task.CreatedAt)) - - cli.Blank() - cli.Print("%s\n", dimStyle.Render(i18n.T("cmd.ai.label.description"))) - cli.Text(task.Description) - - if len(task.Files) > 0 { - cli.Blank() - cli.Print("%s\n", dimStyle.Render(i18n.T("cmd.ai.label.related_files"))) - for _, f := range task.Files { - cli.Print(" - %s\n", f) - } - } - - if len(task.Dependencies) > 0 { - cli.Blank() - cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.blocked_by")), strings.Join(task.Dependencies, ", ")) - } -} - -func formatTaskPriority(p agentic.TaskPriority) string { - switch p { - case agentic.PriorityCritical: - return taskPriorityHighStyle.Render("[" + i18n.T("cmd.ai.priority.critical") + "]") - case agentic.PriorityHigh: - return taskPriorityHighStyle.Render("[" + i18n.T("cmd.ai.priority.high") + "]") - case agentic.PriorityMedium: - return taskPriorityMediumStyle.Render("[" + i18n.T("cmd.ai.priority.medium") + "]") - case agentic.PriorityLow: - return taskPriorityLowStyle.Render("[" + i18n.T("cmd.ai.priority.low") + "]") - default: - return dimStyle.Render("[" + string(p) + "]") - } -} - -func formatTaskStatus(s agentic.TaskStatus) string { - switch s { - case agentic.StatusPending: - return taskStatusPendingStyle.Render(i18n.T("cmd.ai.status.pending")) - case agentic.StatusInProgress: - return taskStatusInProgressStyle.Render(i18n.T("cmd.ai.status.in_progress")) - case agentic.StatusCompleted: - return taskStatusCompletedStyle.Render(i18n.T("cmd.ai.status.completed")) - case agentic.StatusBlocked: - return taskStatusBlockedStyle.Render(i18n.T("cmd.ai.status.blocked")) - default: - return dimStyle.Render(string(s)) - } -} diff --git a/cmd/ai/cmd_updates.go b/cmd/ai/cmd_updates.go deleted file mode 100644 index 48f407bf..00000000 --- a/cmd/ai/cmd_updates.go +++ /dev/null @@ -1,131 +0,0 @@ -// cmd_updates.go implements task update and completion commands. - -package ai - -import ( - "context" - "time" - - "forge.lthn.ai/core/go-agentic" - "forge.lthn.ai/core/go-ai/ai" - "forge.lthn.ai/core/go/pkg/cli" - "forge.lthn.ai/core/go/pkg/i18n" -) - -// task:update command flags -var ( - taskUpdateStatus string - taskUpdateProgress int - taskUpdateNotes string -) - -// task:complete command flags -var ( - taskCompleteOutput string - taskCompleteFailed bool - taskCompleteErrorMsg string -) - -var taskUpdateCmd = &cli.Command{ - Use: "task:update [task-id]", - Short: i18n.T("cmd.ai.task_update.short"), - Long: i18n.T("cmd.ai.task_update.long"), - Args: cli.ExactArgs(1), - RunE: func(cmd *cli.Command, args []string) error { - taskID := args[0] - - if taskUpdateStatus == "" && taskUpdateProgress == 0 && taskUpdateNotes == "" { - return cli.Err("%s", i18n.T("cmd.ai.task_update.flag_required")) - } - - cfg, err := agentic.LoadConfig("") - if err != nil { - return cli.WrapVerb(err, "load", "config") - } - - client := agentic.NewClientFromConfig(cfg) - - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - update := agentic.TaskUpdate{ - Progress: taskUpdateProgress, - Notes: taskUpdateNotes, - } - if taskUpdateStatus != "" { - update.Status = agentic.TaskStatus(taskUpdateStatus) - } - - if err := client.UpdateTask(ctx, taskID, update); err != nil { - return cli.WrapVerb(err, "update", "task") - } - - cli.Print("%s %s\n", successStyle.Render(">>"), i18n.T("i18n.done.update", "task")) - return nil - }, -} - -var taskCompleteCmd = &cli.Command{ - Use: "task:complete [task-id]", - Short: i18n.T("cmd.ai.task_complete.short"), - Long: i18n.T("cmd.ai.task_complete.long"), - Args: cli.ExactArgs(1), - RunE: func(cmd *cli.Command, args []string) error { - taskID := args[0] - - cfg, err := agentic.LoadConfig("") - if err != nil { - return cli.WrapVerb(err, "load", "config") - } - - client := agentic.NewClientFromConfig(cfg) - - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - result := agentic.TaskResult{ - Success: !taskCompleteFailed, - Output: taskCompleteOutput, - ErrorMessage: taskCompleteErrorMsg, - } - - if err := client.CompleteTask(ctx, taskID, result); err != nil { - return cli.WrapVerb(err, "complete", "task") - } - - // Record task completion event - _ = ai.Record(ai.Event{ - Type: "task.completed", - AgentID: cfg.AgentID, - Data: map[string]any{"task_id": taskID, "success": !taskCompleteFailed}, - }) - - if taskCompleteFailed { - cli.Print("%s %s\n", errorStyle.Render(">>"), i18n.T("cmd.ai.task_complete.failed", map[string]interface{}{"ID": taskID})) - } else { - cli.Print("%s %s\n", successStyle.Render(">>"), i18n.T("i18n.done.complete", "task")) - } - return nil - }, -} - -func initUpdatesFlags() { - // task:update command flags - taskUpdateCmd.Flags().StringVar(&taskUpdateStatus, "status", "", i18n.T("cmd.ai.task_update.flag.status")) - taskUpdateCmd.Flags().IntVar(&taskUpdateProgress, "progress", 0, i18n.T("cmd.ai.task_update.flag.progress")) - taskUpdateCmd.Flags().StringVar(&taskUpdateNotes, "notes", "", i18n.T("cmd.ai.task_update.flag.notes")) - - // task:complete command flags - taskCompleteCmd.Flags().StringVar(&taskCompleteOutput, "output", "", i18n.T("cmd.ai.task_complete.flag.output")) - taskCompleteCmd.Flags().BoolVar(&taskCompleteFailed, "failed", false, i18n.T("cmd.ai.task_complete.flag.failed")) - taskCompleteCmd.Flags().StringVar(&taskCompleteErrorMsg, "error", "", i18n.T("cmd.ai.task_complete.flag.error")) -} - -func addTaskUpdateCommand(parent *cli.Command) { - initUpdatesFlags() - parent.AddCommand(taskUpdateCmd) -} - -func addTaskCompleteCommand(parent *cli.Command) { - parent.AddCommand(taskCompleteCmd) -} diff --git a/cmd/ai/ratelimit_dispatch.go b/cmd/ai/ratelimit_dispatch.go deleted file mode 100644 index ae432949..00000000 --- a/cmd/ai/ratelimit_dispatch.go +++ /dev/null @@ -1,49 +0,0 @@ -package ai - -import ( - "context" - - "forge.lthn.ai/core/go/pkg/log" - "forge.lthn.ai/core/go/pkg/ratelimit" -) - -// executeWithRateLimit wraps an agent execution with rate limiting logic. -// It estimates token usage, waits for capacity, executes the runner, and records usage. -func executeWithRateLimit(ctx context.Context, model, prompt string, runner func() (bool, int, error)) (bool, int, error) { - rl, err := ratelimit.New() - if err != nil { - log.Warn("Failed to initialize rate limiter, proceeding without limits", "error", err) - return runner() - } - - if err := rl.Load(); err != nil { - log.Warn("Failed to load rate limit state", "error", err) - } - - // Estimate tokens from prompt length (1 token ≈ 4 chars) - estTokens := len(prompt) / 4 - if estTokens == 0 { - estTokens = 1 - } - - log.Info("Checking rate limits", "model", model, "est_tokens", estTokens) - - if err := rl.WaitForCapacity(ctx, model, estTokens); err != nil { - return false, -1, err - } - - success, exitCode, runErr := runner() - - // Record usage with conservative output estimate (actual tokens unknown from shell runner). - outputEst := estTokens / 10 - if outputEst < 50 { - outputEst = 50 - } - rl.RecordUsage(model, estTokens, outputEst) - - if err := rl.Persist(); err != nil { - log.Warn("Failed to persist rate limit state", "error", err) - } - - return success, exitCode, runErr -} diff --git a/go.mod b/go.mod index 4fe16a41..b791b969 100644 --- a/go.mod +++ b/go.mod @@ -1,19 +1,12 @@ module forge.lthn.ai/core/cli -go 1.25.5 +go 1.26.0 require ( forge.lthn.ai/core/go v0.0.0-20260221191103-d091fa62023f forge.lthn.ai/core/go-agentic v0.0.0-20260221191948-ad0cf5c932a3 - forge.lthn.ai/core/go-ai v0.0.0-20260221193804-c3de6c4935d5 - forge.lthn.ai/core/go-api v0.0.0-20260221193814-9d35070573b8 - forge.lthn.ai/core/go-crypt v0.0.0-20260221193816-fde12e1539b2 + forge.lthn.ai/core/go-crypt v0.0.0-20260221193816-fde12e1539b2 // indirect forge.lthn.ai/core/go-devops v0.0.0-20260221193818-400d8a76901e - forge.lthn.ai/core/go-inference v0.0.0-20260220151119-1576f744d105 // indirect - forge.lthn.ai/core/go-ml v0.0.0-20260221191458-812c926dac42 - forge.lthn.ai/core/go-mlx v0.0.0-20260221191404-2292557fd65f // indirect - forge.lthn.ai/core/go-netops v0.0.0-20260221193827-b865250390a4 - forge.lthn.ai/core/go-rag v0.0.0-20260221193811-2a8d8b0820b5 forge.lthn.ai/core/go-scm v0.0.0-20260221193836-7eb28df79d0b forge.lthn.ai/core/go-store v0.1.1-0.20260220151120-0284110ccadf // indirect ) @@ -32,30 +25,10 @@ require ( require ( aead.dev/minisign v0.3.0 // indirect - cloud.google.com/go v0.123.0 // indirect - codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.2.0 // indirect dario.cat/mergo v1.0.2 // indirect - github.com/42wim/httpsig v1.2.3 // indirect - github.com/99designs/gqlgen v0.17.87 // indirect - github.com/KyleBanks/depth v1.2.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.3.0 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/TwiN/go-color v1.4.1 // indirect - github.com/agnivade/levenshtein v1.2.1 // indirect - github.com/andybalholm/brotli v1.2.0 // indirect - github.com/apache/arrow-go/v18 v18.5.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/bahlo/generic-list-go v0.2.0 // indirect - github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect - github.com/brianvoe/gofakeit/v6 v6.28.0 // indirect - github.com/buger/jsonparser v1.1.1 // indirect - github.com/bytedance/gopkg v0.1.3 // indirect - github.com/bytedance/sonic v1.15.0 // indirect - github.com/bytedance/sonic/loader v0.5.0 // indirect - github.com/casbin/casbin/v2 v2.135.0 // indirect - github.com/casbin/govaluate v1.10.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charmbracelet/bubbletea v1.3.10 // indirect github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect @@ -64,155 +37,54 @@ require ( github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect github.com/charmbracelet/x/term v0.2.1 // indirect github.com/cloudflare/circl v1.6.3 // indirect - github.com/cloudwego/base64x v0.1.6 // indirect - github.com/coreos/go-oidc/v3 v3.17.0 // indirect github.com/cyphar/filepath-securejoin v0.6.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/davidmz/go-pageant v1.0.2 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.13 // indirect - github.com/getkin/kin-openapi v0.133.0 // indirect - github.com/gin-contrib/authz v1.0.6 // indirect - github.com/gin-contrib/cors v1.7.6 // indirect - github.com/gin-contrib/expvar v1.0.3 // indirect - github.com/gin-contrib/gzip v1.2.5 // indirect - github.com/gin-contrib/httpsign v1.0.3 // indirect - github.com/gin-contrib/location/v2 v2.0.0 // indirect - github.com/gin-contrib/pprof v1.5.3 // indirect - github.com/gin-contrib/secure v1.1.2 // indirect - github.com/gin-contrib/sessions v1.0.4 // indirect - github.com/gin-contrib/slog v1.2.0 // indirect - github.com/gin-contrib/sse v1.1.0 // indirect - github.com/gin-contrib/static v1.1.5 // indirect - github.com/gin-contrib/timeout v1.1.0 // indirect - github.com/gin-gonic/gin v1.11.0 // indirect - github.com/go-fed/httpsig v1.1.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.7.0 // indirect github.com/go-git/go-git/v5 v5.16.5 // indirect - github.com/go-jose/go-jose/v4 v4.1.3 // indirect - github.com/go-logr/logr v1.4.3 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.22.4 // indirect - github.com/go-openapi/jsonreference v0.19.6 // indirect - github.com/go-openapi/spec v0.20.4 // indirect - github.com/go-openapi/swag v0.23.0 // indirect - github.com/go-openapi/swag/jsonname v0.25.4 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.30.1 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect - github.com/goccy/go-json v0.10.5 // indirect - github.com/goccy/go-yaml v1.19.2 // indirect - github.com/gofrs/flock v0.12.1 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect - github.com/google/flatbuffers v25.12.19+incompatible // indirect - github.com/google/jsonschema-go v0.4.2 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gorilla/context v1.1.2 // indirect - github.com/gorilla/securecookie v1.1.2 // indirect - github.com/gorilla/sessions v1.4.0 // indirect - github.com/gorilla/websocket v1.5.3 // indirect - github.com/hashicorp/go-version v1.8.0 // indirect - github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.6.0 // indirect - github.com/klauspost/compress v1.18.4 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/kluctl/go-embed-python v0.0.0-3.13.1-20241219-1 // indirect - github.com/leaanthony/debme v1.2.1 // indirect - github.com/leaanthony/gosod v1.0.4 // indirect - github.com/leodido/go-urn v1.4.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mailru/easyjson v0.9.1 // indirect - github.com/marcboeker/go-duckdb v1.8.5 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/modelcontextprotocol/go-sdk v1.3.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.16.0 // indirect github.com/ncruces/go-strftime v1.0.0 // indirect - github.com/oasdiff/oasdiff v1.11.10 // indirect - github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect - github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect - github.com/ollama/ollama v0.16.1 // indirect - github.com/parquet-go/bitpack v1.0.0 // indirect - github.com/parquet-go/jsonlite v1.4.0 // indirect - github.com/parquet-go/parquet-go v0.27.0 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/perimeterx/marshmallow v1.1.5 // indirect - github.com/pierrec/lz4/v4 v4.1.25 // indirect github.com/pjbgf/sha1cd v0.5.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/qdrant/go-client v1.16.2 // indirect - github.com/quic-go/qpack v0.6.0 // indirect - github.com/quic-go/quic-go v0.59.0 // indirect github.com/redis/go-redis/v9 v9.18.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/sergi/go-diff v1.4.0 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect github.com/skeema/knownhosts v1.3.2 // indirect - github.com/sosodev/duration v1.3.1 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/spf13/viper v1.21.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/swaggo/files v1.0.1 // indirect - github.com/swaggo/gin-swagger v1.6.1 // indirect - github.com/swaggo/swag v1.16.6 // indirect - github.com/tidwall/gjson v1.18.0 // indirect - github.com/tidwall/match v1.2.0 // indirect - github.com/tidwall/pretty v1.2.1 // indirect - github.com/tidwall/sjson v1.2.5 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/twpayne/go-geom v1.6.1 // indirect - github.com/ugorji/go/codec v1.3.1 // indirect - github.com/ulikunitz/xz v0.5.15 // indirect - github.com/unpoller/unifi/v5 v5.18.0 // indirect - github.com/vektah/gqlparser/v2 v2.5.32 // indirect - github.com/wI2L/jsondiff v0.7.0 // indirect - github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect - github.com/woodsbury/decimal128 v1.4.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect - github.com/yargevad/filepathx v1.0.0 // indirect - github.com/yosida95/uritemplate/v3 v3.0.2 // indirect github.com/zeebo/xxh3 v1.1.0 // indirect - go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.65.0 // indirect - go.opentelemetry.io/otel v1.40.0 // indirect - go.opentelemetry.io/otel/metric v1.40.0 // indirect - go.opentelemetry.io/otel/sdk v1.40.0 // indirect - go.opentelemetry.io/otel/trace v1.40.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/arch v0.23.0 // indirect golang.org/x/crypto v0.48.0 // indirect golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a // indirect golang.org/x/net v0.50.0 // indirect - golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.41.0 // indirect - golang.org/x/telemetry v0.0.0-20260213145524-e0ab670178e1 // indirect - golang.org/x/tools v0.42.0 // indirect - golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect - google.golang.org/grpc v1.79.1 // indirect - google.golang.org/protobuf v1.36.11 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect modernc.org/libc v1.67.7 // indirect modernc.org/mathutil v1.7.1 // indirect diff --git a/go.sum b/go.sum index ec7ce66d..a0033d3c 100644 --- a/go.sum +++ b/go.sum @@ -1,109 +1,37 @@ aead.dev/minisign v0.2.0/go.mod h1:zdq6LdSd9TbuSxchxwhpA9zEb9YXcVGoE8JakuiGaIQ= aead.dev/minisign v0.3.0 h1:8Xafzy5PEVZqYDNP60yJHARlW1eOQtsKNp/Ph2c0vRA= aead.dev/minisign v0.3.0/go.mod h1:NLvG3Uoq3skkRMDuc3YHpWUTMTrSExqm+Ij73W13F6Y= -cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= -cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= -codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.2.0 h1:HTCWpzyWQOHDWt3LzI6/d2jvUDsw/vgGRWm/8BTvcqI= -codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.2.0/go.mod h1:ZglEEDj+qkxYUb+SQIeqGtFxQrbaMYqIOgahNKb7uxs= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= forge.lthn.ai/core/go v0.0.0-20260221191103-d091fa62023f h1:CcSh/FFY93K5m0vADHLxwxKn2pTIM8HzYX1eGa4WZf4= forge.lthn.ai/core/go v0.0.0-20260221191103-d091fa62023f/go.mod h1:WCPJVEZm/6mTcJimHV0uX8ZhnKEF3dN0rQp13ByaSPg= forge.lthn.ai/core/go-agentic v0.0.0-20260221191948-ad0cf5c932a3 h1:6H3hjqHY0loJJe9iCofFzw6x5JDIbi6JNSL0oW2TKFE= forge.lthn.ai/core/go-agentic v0.0.0-20260221191948-ad0cf5c932a3/go.mod h1:2WCSLupRyAeSpmFWM5+OPG0/wa4KMQCO8gA0hM9cUq8= -forge.lthn.ai/core/go-ai v0.0.0-20260221193804-c3de6c4935d5 h1:ppJV+0PsTjUALPuA0eZx8mnvjDAfCwR5ASBcnPhCqjI= -forge.lthn.ai/core/go-ai v0.0.0-20260221193804-c3de6c4935d5/go.mod h1:GMURVEDR3TkzmgSML8//CHyTZ/WcuzRR0IGNuAJSqKA= -forge.lthn.ai/core/go-api v0.0.0-20260221193814-9d35070573b8 h1:a0+HIcWhbvXqQh8h+UY2oOxks9UHBHFNRHPuSUBUkoY= -forge.lthn.ai/core/go-api v0.0.0-20260221193814-9d35070573b8/go.mod h1:7HmXnlMJGEmHsEe/l/o96xLmLdQj5bIng6c2mSur2N8= forge.lthn.ai/core/go-crypt v0.0.0-20260221193816-fde12e1539b2 h1:2eXqQXF+1AyitPJox9Yjewb6w8fO0JHFw7gPqk8WqIM= forge.lthn.ai/core/go-crypt v0.0.0-20260221193816-fde12e1539b2/go.mod h1:o4vkJgoT9u+r7DR42LIJHW6L5vMS3Au8gaaCA5Cved0= forge.lthn.ai/core/go-devops v0.0.0-20260221193818-400d8a76901e h1:ya3vWejLAb9+66FesDYakBi1lTmbHPA/gex6hgZ4zoo= forge.lthn.ai/core/go-devops v0.0.0-20260221193818-400d8a76901e/go.mod h1:FSp7+jfV3QXyPzL1C8XZm6W57vjT8cbWly8vf/bPJEg= -forge.lthn.ai/core/go-inference v0.0.0-20260220151119-1576f744d105 h1:CVUVxp1BfUI8wmlEUW0Nay8w4hADR54nqBmeF+KK2Ac= -forge.lthn.ai/core/go-inference v0.0.0-20260220151119-1576f744d105/go.mod h1:hmLtynfw1yo0ByuX3pslLZMgCdqJH2r+2+wGJDhmmi0= -forge.lthn.ai/core/go-ml v0.0.0-20260221191458-812c926dac42 h1:rxhnHgWVGnQ93/mhUyLxIw/Q2l80njiGfNvv0kKISb0= -forge.lthn.ai/core/go-ml v0.0.0-20260221191458-812c926dac42/go.mod h1:lmhzv04VCP41ym7Wuhck+T1HeC5PoLtfOqXe8fW26Hc= -forge.lthn.ai/core/go-mlx v0.0.0-20260221191404-2292557fd65f h1:dlb6hFFhxfnJvD1ZYoQVsxD9NM4CV+sXkjHa6kBGzeE= -forge.lthn.ai/core/go-mlx v0.0.0-20260221191404-2292557fd65f/go.mod h1:QHspfOk9MgbuG6Wb4m+RzQyCMibtoQNZw+hUs4yclOA= -forge.lthn.ai/core/go-netops v0.0.0-20260221193827-b865250390a4 h1:Fgxkkli7B06qZFAxXFOBiuDQ6Z19DH3/9r8Ppimnp/Y= -forge.lthn.ai/core/go-netops v0.0.0-20260221193827-b865250390a4/go.mod h1:1s/NQwXUfbvg0HQRLAda26LGlvAReTVFQcUJGDhY/Ng= -forge.lthn.ai/core/go-rag v0.0.0-20260221193811-2a8d8b0820b5 h1:AD7a3IY0W/LaxmPRnPkxuxbWYw11/jsm2zELG1FfSNY= -forge.lthn.ai/core/go-rag v0.0.0-20260221193811-2a8d8b0820b5/go.mod h1:s5OWHz87LELq2UKk93cBFJA9pydLoytHN1pPbgX0ShE= forge.lthn.ai/core/go-scm v0.0.0-20260221193836-7eb28df79d0b h1:GrL3ApTDLCdbPusNjv6rI9qNjqQ+srX1ilwg8I5M0UA= forge.lthn.ai/core/go-scm v0.0.0-20260221193836-7eb28df79d0b/go.mod h1:rCTonaMb6UMkyWd/34jg3zp4UXUl85jcb5vj5K+UG0I= forge.lthn.ai/core/go-store v0.1.1-0.20260220151120-0284110ccadf h1:EDKI+OM0M+l4+VclG5XuUDoYAM8yu8uleFYReeEYwHY= forge.lthn.ai/core/go-store v0.1.1-0.20260220151120-0284110ccadf/go.mod h1:FpUlLEX/ebyoxpk96F7ktr0vYvmFtC5Rpi9fi88UVqw= -github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs= -github.com/42wim/httpsig v1.2.3/go.mod h1:nZq9OlYKDrUBhptd77IHx4/sZZD+IxTBADvAPI9G/EM= -github.com/99designs/gqlgen v0.17.87 h1:pSnCIMhBQezAE8bc1GNmfdLXFmnWtWl1GRDFEE/nHP8= -github.com/99designs/gqlgen v0.17.87/go.mod h1:fK05f1RqSNfQpd4CfW5qk/810Tqi4/56Wf6Nem0khAg= -github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= -github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= -github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= -github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= -github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw= -github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Snider/Borg v0.2.0 h1:iCyDhY4WTXi39+FexRwXbn2YpZ2U9FUXVXDZk9xRCXQ= github.com/Snider/Borg v0.2.0/go.mod h1:TqlKnfRo9okioHbgrZPfWjQsztBV0Nfskz4Om1/vdMY= -github.com/TwiN/go-color v1.4.1 h1:mqG0P/KBgHKVqmtL5ye7K0/Gr4l6hTksPgTgMk3mUzc= -github.com/TwiN/go-color v1.4.1/go.mod h1:WcPf/jtiW95WBIsEeY1Lc/b8aaWoiqQpu5cf8WFxu+s= -github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= -github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= -github.com/alecthomas/assert/v2 v2.10.0 h1:jjRCHsj6hBJhkmhznrCzoNpbA3zqy0fYiUcYZP/GkPY= -github.com/alecthomas/assert/v2 v2.10.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= -github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= -github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= -github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= -github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= -github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/apache/arrow-go/v18 v18.5.1 h1:yaQ6zxMGgf9YCYw4/oaeOU3AULySDlAYDOcnr4LdHdI= -github.com/apache/arrow-go/v18 v18.5.1/go.mod h1:OCCJsmdq8AsRm8FkBSSmYTwL/s4zHW9CqxeBxEytkNE= -github.com/apache/thrift v0.22.0 h1:r7mTJdj51TMDe6RtcmNdQxgn9XcyfGDOzegMDRg47uc= -github.com/apache/thrift v0.22.0/go.mod h1:1e7J/O1Ae6ZQMTYdy9xa3w9k+XHWPfRvdPyJeynQ+/g= -github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= -github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= -github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= -github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= -github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE= -github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= -github.com/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4= -github.com/brianvoe/gofakeit/v6 v6.28.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= -github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= -github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= -github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE= -github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= -github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= -github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= -github.com/casbin/casbin/v2 v2.135.0 h1:6BLkMQiGotYyS5yYeWgW19vxqugUlvHFkFiLnLR/bxk= -github.com/casbin/casbin/v2 v2.135.0/go.mod h1:FmcfntdXLTcYXv/hxgNntcRPqAbwOG9xsism0yXT+18= -github.com/casbin/govaluate v1.3.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A= -github.com/casbin/govaluate v1.10.0 h1:ffGw51/hYH3w3rZcxO/KcaUIDOLP84w7nsidMVgaDG0= -github.com/casbin/govaluate v1.10.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= @@ -120,24 +48,15 @@ github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQ github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= -github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= -github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= -github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc= -github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE= github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0= -github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= -github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= @@ -150,42 +69,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= -github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= -github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ= -github.com/getkin/kin-openapi v0.133.0/go.mod h1:boAciF6cXk5FhPqe/NQeBTeenbjqU4LhWBf09ILVvWE= -github.com/gin-contrib/authz v1.0.6 h1:qAO4sSSzOPCwYRZI6YtubC+h2tZVwhwSJeyEZn2W+5k= -github.com/gin-contrib/authz v1.0.6/go.mod h1:A2B5Im1M/HIoHPjLc31j3RlENSE6j8euJY9NFdzZeYo= -github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY= -github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk= -github.com/gin-contrib/expvar v1.0.3 h1:nIbUaokxZfUEC/35h+RyWCP1SMF/suV/ARbXL3H3jrw= -github.com/gin-contrib/expvar v1.0.3/go.mod h1:bwqqmhty1Zl2JYVLzBIL6CSHDWDbQoQoicalAnBvUnY= -github.com/gin-contrib/gzip v1.2.5 h1:fIZs0S+l17pIu1P5XRJOo/YNqfIuPCrZZ3TWB7pjckI= -github.com/gin-contrib/gzip v1.2.5/go.mod h1:aomRgR7ftdZV3uWY0gW/m8rChfxau0n8YVvwlOHONzw= -github.com/gin-contrib/httpsign v1.0.3 h1:NpeDQjmUV0qFjGCm/rkXSp3HH0hU7r84q1v+VtTiI5I= -github.com/gin-contrib/httpsign v1.0.3/go.mod h1:n4GC7StmHNBhIzWzuW2njKbZMeEWh4tDbmn3bD1ab+k= -github.com/gin-contrib/location/v2 v2.0.0 h1:iLx5RatHQHSxgC0tm2AG0sIuQKecI7FhREessVd6RWY= -github.com/gin-contrib/location/v2 v2.0.0/go.mod h1:276TDNr25NENBA/NQZUuEIlwxy/I5CYVFIr/d2TgOdU= -github.com/gin-contrib/pprof v1.5.3 h1:Bj5SxJ3kQDVez/s/+f9+meedJIqLS+xlkIVDe/lcvgM= -github.com/gin-contrib/pprof v1.5.3/go.mod h1:0+LQSZ4SLO0B6+2n6JBzaEygpTBxe/nI+YEYpfQQ6xY= -github.com/gin-contrib/secure v1.1.2 h1:6G8/NCOTSywWY7TeaH/0Yfaa6bfkE5ukkqtIm7lK11U= -github.com/gin-contrib/secure v1.1.2/go.mod h1:xI3jI5/BpOYMCBtjgmIVrMA3kI7y9LwCFxs+eLf5S3w= -github.com/gin-contrib/sessions v1.0.4 h1:ha6CNdpYiTOK/hTp05miJLbpTSNfOnFg5Jm2kbcqy8U= -github.com/gin-contrib/sessions v1.0.4/go.mod h1:ccmkrb2z6iU2osiAHZG3x3J4suJK+OU27oqzlWOqQgs= -github.com/gin-contrib/slog v1.2.0 h1:vAxZfr7knD1ZYK5+pMJLP52sZXIkJXkcRPa/0dx9hSk= -github.com/gin-contrib/slog v1.2.0/go.mod h1:vYK6YltmpsEFkO0zfRMLTKHrWS3DwUSn0TMpT+kMagI= -github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= -github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= -github.com/gin-contrib/static v1.1.5 h1:bAPqT4KTZN+4uDY1b90eSrD1t8iNzod7Jj8njwmnzz4= -github.com/gin-contrib/static v1.1.5/go.mod h1:8JSEXwZHcQ0uCrLPcsvnAJ4g+ODxeupP8Zetl9fd8wM= -github.com/gin-contrib/timeout v1.1.0 h1:WAmWseo5gfBUbMrMJu5hJxDclehfSJUmK2wGwCC/EFw= -github.com/gin-contrib/timeout v1.1.0/go.mod h1:NpRo4gd1Ad8ZQ4T6bQLVFDqiplCmPRs2nvfckxS2Fw4= -github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= -github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= -github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= -github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.7.0 h1:83lBUJhGWhYp0ngzCMSgllhUSuoHP1iEWYjsPl9nwqM= @@ -194,104 +79,26 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s= github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M= -github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= -github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= -github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= -github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= -github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= -github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= -github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= -github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= -github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= -github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= -github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= -github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= -github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= -github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= -github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= -github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= -github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= -github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/flatbuffers v25.12.19+incompatible h1:haMV2JRRJCe1998HeW/p0X9UaMTK6SDo0ffLn2+DbLs= -github.com/google/flatbuffers v25.12.19+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8= -github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o= -github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM= -github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= -github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= -github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= -github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= -github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= -github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY= github.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= -github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= -github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= -github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= -github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/kluctl/go-embed-python v0.0.0-3.13.1-20241219-1 h1:x1cSEj4Ug5mpuZgUHLvUmlc5r//KHFn6iYiRSrRcVy4= -github.com/kluctl/go-embed-python v0.0.0-3.13.1-20241219-1/go.mod h1:3ebNU9QBrNpUO+Hj6bHaGpkh5pymDHQ+wwVPHTE4mCE= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -299,48 +106,16 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc= -github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA= -github.com/leaanthony/gosod v1.0.4 h1:YLAbVyd591MRffDgxUOU1NwLhT9T1/YiwjKZpkNFeaI= -github.com/leaanthony/gosod v1.0.4/go.mod h1:GKuIL0zzPj3O1SdWQOdgURSuhkF+Urizzxh26t9f1cw= -github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= -github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/Js= -github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8= -github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= -github.com/marcboeker/go-duckdb v1.8.5 h1:tkYp+TANippy0DaIOP5OEfBEwbUINqiFqgwMQ44jME0= -github.com/marcboeker/go-duckdb v1.8.5/go.mod h1:6mK7+WQE4P4u5AFLvVBmhFxY5fvhymFptghgJX6B+/8= -github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= -github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ= -github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/minio/selfupdate v0.6.0 h1:i76PgT0K5xO9+hjzKcacQtO7+MjJ4JKA8Ak8XQ9DDwU= github.com/minio/selfupdate v0.6.0/go.mod h1:bO02GTIPCMQFTEvE5h4DjYB58bCoZ35XLeBf0buTDdM= -github.com/modelcontextprotocol/go-sdk v1.3.0 h1:gMfZkv3DzQF5q/DcQePo5rahEY+sguyPfXDfNBcT0Zs= -github.com/modelcontextprotocol/go-sdk v1.3.0/go.mod h1:AnQ//Qc6+4nIyyrB4cxBU7UW9VibK4iOZBeyP/rF1IE= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= @@ -349,29 +124,10 @@ github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/oasdiff/oasdiff v1.11.10 h1:4I9VrktUoHmwydkJqVOC7Bd6BXKu9dc4UUP3PIu1VjM= -github.com/oasdiff/oasdiff v1.11.10/go.mod h1:GXARzmqBKN8lZHsTQD35ZM41ePbu6JdAZza4sRMeEKg= -github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= -github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= -github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= -github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= -github.com/ollama/ollama v0.16.1 h1:DIxnLdS0om3hb7HheJqj6+ZnPCCMWmy/vyUxiQgRYoI= -github.com/ollama/ollama v0.16.1/go.mod h1:FEk95NbAJJZk+t7cLh+bPGTul72j1O3PLLlYNV3FVZ0= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= -github.com/parquet-go/bitpack v1.0.0 h1:AUqzlKzPPXf2bCdjfj4sTeacrUwsT7NlcYDMUQxPcQA= -github.com/parquet-go/bitpack v1.0.0/go.mod h1:XnVk9TH+O40eOOmvpAVZ7K2ocQFrQwysLMnc6M/8lgs= -github.com/parquet-go/jsonlite v1.4.0 h1:RTG7prqfO0HD5egejU8MUDBN8oToMj55cgSV1I0zNW4= -github.com/parquet-go/jsonlite v1.4.0/go.mod h1:nDjpkpL4EOtqs6NQugUsi0Rleq9sW/OtC1NnZEnxzF0= -github.com/parquet-go/parquet-go v0.27.0 h1:vHWK2xaHbj+v1DYps03yDRpEsdtOeKbhiXUaixoPb3g= -github.com/parquet-go/parquet-go v0.27.0/go.mod h1:navtkAYr2LGoJVp141oXPlO/sxLvaOe3la2JEoD8+rg= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= -github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= -github.com/pierrec/lz4/v4 v4.1.25 h1:kocOqRffaIbU5djlIBr7Wh+cx82C0vtFb0fOurZHqD0= -github.com/pierrec/lz4/v4 v4.1.25/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -379,12 +135,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/qdrant/go-client v1.16.2 h1:UUMJJfvXTByhwhH1DwWdbkhZ2cTdvSqVkXSIfBrVWSg= -github.com/qdrant/go-client v1.16.2/go.mod h1:I+EL3h4HRoRTeHtbfOd/4kDXwCukZfkd41j/9wryGkw= -github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= -github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= -github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= -github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= github.com/redis/go-redis/v9 v9.18.0 h1:pMkxYPkEbMPwRdenAzUNyFNrDgHx9U+DrBabWNfSRQs= github.com/redis/go-redis/v9 v9.18.0/go.mod h1:k3ufPphLU5YXwNTUcCRXGxUoF1fqxnhFQmscfkCoDA0= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= @@ -400,12 +150,8 @@ github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9t github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg= github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow= -github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4= -github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= @@ -418,192 +164,71 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= -github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= -github.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs7cY= -github.com/swaggo/gin-swagger v1.6.1/go.mod h1:LQ+hJStHakCWRiK/YNYtJOu4mR2FP+pxLnILT/qNiTw= -github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI= -github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg= -github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= -github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM= -github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= -github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= -github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/twpayne/go-geom v1.6.1 h1:iLE+Opv0Ihm/ABIcvQFGIiFBXd76oBIar9drAwHFhR4= -github.com/twpayne/go-geom v1.6.1/go.mod h1:Kr+Nly6BswFsKM5sd31YaoWS5PeDDH2NftJTK7Gd028= -github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= -github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= -github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= -github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/unpoller/unifi/v5 v5.18.0 h1:i9xecLeI9CU6m+5++TIm+zhdGS9f8KCUz8PuuzO7sSQ= -github.com/unpoller/unifi/v5 v5.18.0/go.mod h1:vSIXIclPG9dpKxUp+pavfgENHWaTZXvDg7F036R1YCo= -github.com/vektah/gqlparser/v2 v2.5.32 h1:k9QPJd4sEDTL+qB4ncPLflqTJ3MmjB9SrVzJrawpFSc= -github.com/vektah/gqlparser/v2 v2.5.32/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts= -github.com/wI2L/jsondiff v0.7.0 h1:1lH1G37GhBPqCfp/lrs91rf/2j3DktX6qYAKZkLuCQQ= -github.com/wI2L/jsondiff v0.7.0/go.mod h1:KAEIojdQq66oJiHhDyQez2x+sRit0vIzC9KeK0yizxM= -github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= -github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= -github.com/woodsbury/decimal128 v1.4.0 h1:xJATj7lLu4f2oObouMt2tgGiElE5gO6mSWUjQsBgUlc= -github.com/woodsbury/decimal128 v1.4.0/go.mod h1:BP46FUrVjVhdTbKT+XuQh2xfQaGki9LMIRJSFuh6THU= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= -github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= -github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -github.com/yargevad/filepathx v1.0.0 h1:SYcT+N3tYGi+NvazubCNlvgIPbzAk7i7y2dwg3I5FYc= -github.com/yargevad/filepathx v1.0.0/go.mod h1:BprfX/gpYNJHJfc35GjRRpVcwWXS89gGulUIU5tK3tA= -github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= -github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= -github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= -go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= -go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.65.0 h1:LSJsvNqhj2sBNFb5NWHbyDK4QJ/skQ2ydjeOZ9OYNZ4= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.65.0/go.mod h1:0Q5ocj6h/+C6KYq8cnl4tDFVd4I1HBdsJ440aeagHos= -go.opentelemetry.io/contrib/propagators/b3 v1.40.0 h1:xariChe8OOVF3rNlfzGFgQc61npQmXhzZj/i82mxMfg= -go.opentelemetry.io/contrib/propagators/b3 v1.40.0/go.mod h1:72WvbdxbOfXaELEQfonFfOL6osvcVjI7uJEE8C2nkrs= -go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= -go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0 h1:MzfofMZN8ulNqobCmCAVbqVL5syHw+eB2qPRkCMA/fQ= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0/go.mod h1:E73G9UFtKRXrxhBsHtG00TB5WxX57lpsQzogDkqBTz8= -go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= -go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= -go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= -go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= -go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= -go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= -go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= -go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= -go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg= -golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a h1:ovFr6Z0MNmU7nH8VaX5xqw+05ST2uO1exVfZPVqRC5o= golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/telemetry v0.0.0-20260213145524-e0ab670178e1 h1:QNaHp8YvpPswfDNxlCmJyeesxbGOgaKf41iT9/QrErY= -golang.org/x/telemetry v0.0.0-20260213145524-e0ab670178e1/go.mod h1:NuITXsA9cTiqnXtVk+/wrBT2Ja4X5hsfGOYRJ6kgYjs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= -golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= -gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= -google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= -google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= -google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= -google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= diff --git a/main.go b/main.go index 13767d22..281dad1e 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,6 @@ import ( "forge.lthn.ai/core/go/pkg/cli" // Commands via self-registration (local to CLI) - _ "forge.lthn.ai/core/cli/cmd/ai" _ "forge.lthn.ai/core/cli/cmd/config" _ "forge.lthn.ai/core/cli/cmd/dev" _ "forge.lthn.ai/core/cli/cmd/docs" @@ -22,26 +21,6 @@ import ( _ "forge.lthn.ai/core/cli/cmd/setup" _ "forge.lthn.ai/core/cli/cmd/updater" _ "forge.lthn.ai/core/cli/cmd/workspace" - - // Commands via self-registration (external repos) - _ "forge.lthn.ai/core/go-ai/cmd/daemon" - _ "forge.lthn.ai/core/go-ai/cmd/mcpcmd" - _ "forge.lthn.ai/core/go-ai/cmd/security" - _ "forge.lthn.ai/core/go-api/cmd/api" - _ "forge.lthn.ai/core/go-crypt/cmd/crypt" - _ "forge.lthn.ai/core/go-crypt/cmd/testcmd" - _ "forge.lthn.ai/core/go-devops/build/buildcmd" - _ "forge.lthn.ai/core/go-devops/cmd/deploy" - _ "forge.lthn.ai/core/go-devops/cmd/prod" - _ "forge.lthn.ai/core/go-devops/cmd/vm" - _ "forge.lthn.ai/core/go-ml/cmd" - _ "forge.lthn.ai/core/go-netops/cmd/unifi" - _ "forge.lthn.ai/core/go-scm/cmd/collect" - _ "forge.lthn.ai/core/go-scm/cmd/forge" - - // Variant repos (optional — comment out to exclude) - // _ "forge.lthn.ai/core/php" - // _ "forge.lthn.ai/core/ci" ) func main() {