Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Pre-commit hooks: formatter + linter for the TS frontend and Python backends.
# Install once with: pre-commit install
# Run on all files : pre-commit run --all-files
#
# Python (service/, home-agent/) is handled by Ruff (matches the Ruff CI check).
# WebUI is handled by ESLint + Prettier via the existing npm scripts (matches the
# "Lint TS" CI check). The WebUI hooks lint/format the whole WebUI workspace because
# ESLint/Prettier resolve their flat config from WebUI/, not the repo root.
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.20
hooks:
# Linter (auto-fix what it safely can)
- id: ruff
name: ruff lint (Python)
args: [--fix]
files: ^(service|home-agent)/.*\.py$
# Formatter
- id: ruff-format
name: ruff format (Python)
files: ^(service|home-agent)/.*\.py$

- repo: local
hooks:
# Linter (ESLint with auto-fix)
- id: eslint
name: eslint (WebUI)
language: system
entry: bash -c 'cd WebUI && npm run lint:eslint'
files: ^WebUI/.*\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|vue)$
pass_filenames: false
# Formatter (Prettier)
- id: prettier
name: prettier (WebUI)
language: system
entry: bash -c 'cd WebUI && npm run format'
Comment on lines +29 to +36

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant config and package scripts.
git ls-files .pre-commit-config.yaml WebUI/package.json README.md readme.md | sed 's#^`#FILE` #'
printf '\n--- .pre-commit-config.yaml ---\n'
cat -n .pre-commit-config.yaml | sed -n '20,45p'
printf '\n--- WebUI/package.json ---\n'
cat -n WebUI/package.json | sed -n '1,220p'
printf '\n--- README/readme mentions Windows ---\n'
rg -n -i 'windows' README.md readme.md || true

Repository: TNG/AI-Playground

Length of output: 7686


Make the WebUI hooks shell-free. bash -c adds an unnecessary Git Bash dependency here; npm --prefix WebUI run lint:eslint and npm --prefix WebUI run format avoid breaking Windows setups that don’t have bash on PATH.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.pre-commit-config.yaml around lines 29 - 36, The WebUI pre-commit hooks are
still invoking bash via the lint and prettier entries, which breaks environments
without Bash. Update the hook definitions to use npm’s prefix-based invocation
instead of the current bash -c wrapper in the lint:eslint and format entries so
WebUI runs directly from the repo root and remains Windows-compatible.

files: ^WebUI/.*\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|vue|json|css|scss|html|md|ya?ml)$
pass_filenames: false
97 changes: 97 additions & 0 deletions WebUI/build/scripts/ensure-precommit-hook.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env node
/**
* ensure-precommit-hook.mjs
*
* Install the git pre-commit hook defined by <repo-root>/.pre-commit-config.yaml
* (Ruff for the Python backends, ESLint + Prettier for the WebUI). Run as part of
* `npm run setup` so a fresh clone is ready to commit without a manual step.
*
* Best effort and idempotent:
* - No-op if this is not a git checkout (e.g. a source tarball).
* - No-op if the hook is already installed.
* - Uses `pre-commit` from PATH, else falls back to `uvx pre-commit`
* (uv ships with this repo's toolchain). If neither is available it prints
* how to install it and exits 0 — never blocks setup.
*/
import { spawnSync } from 'node:child_process'
import { existsSync, readFileSync } from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'

const webuiDir = path.resolve(fileURLToPath(new URL('../../', import.meta.url)))
const repoRoot = path.resolve(webuiDir, '..')

function log(msg) {
console.log(`[ensure-precommit-hook] ${msg}`)
}

// Resolve a path relative to the git dir (handles worktrees and `.git` files).
function gitPath(relative) {
const res = spawnSync('git', ['rev-parse', '--git-path', relative], {
cwd: repoRoot,
encoding: 'utf-8',
})
if (res.status !== 0) return undefined
const out = res.stdout.trim()
return path.isAbsolute(out) ? out : path.join(repoRoot, out)
}

// Skip cleanly when this is not a git working tree.
if (
spawnSync('git', ['rev-parse', '--is-inside-work-tree'], { cwd: repoRoot, stdio: 'ignore' })
.status !== 0
) {
log('Not a git checkout — skipping pre-commit hook install.')
process.exit(0)
}

const configPath = path.join(repoRoot, '.pre-commit-config.yaml')
if (!existsSync(configPath)) {
log(`No .pre-commit-config.yaml at ${configPath} — skipping.`)
process.exit(0)
}

// Already installed? The shim pre-commit writes carries a recognizable marker.
const hookPath = gitPath('hooks/pre-commit')
if (hookPath && existsSync(hookPath)) {
try {
if (readFileSync(hookPath, 'utf-8').includes('File generated by pre-commit')) {
log('pre-commit hook already installed — nothing to do.')
process.exit(0)
}
} catch {
/* fall through and (re)install */
}
}

function commandAvailable(cmd, args = ['--version']) {
return spawnSync(cmd, args, { stdio: 'ignore' }).status === 0
}

// Prefer a `pre-commit` on PATH; otherwise run it through uv's `uvx`.
let runner
if (commandAvailable('pre-commit')) {
runner = ['pre-commit']
} else if (commandAvailable('uvx')) {
runner = ['uvx', 'pre-commit']
} else {
log('`pre-commit` is not installed and `uvx` is unavailable, so the hook was not installed.')
log('Install it once with either of:')
log(' uv tool install pre-commit')
log(' pipx install pre-commit')
log('then re-run `npm run setup` (or `pre-commit install`).')
process.exit(0)
Comment on lines +71 to +83

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

git ls-files WebUI/build/scripts/ensure-precommit-hook.mjs WebUI/package.json WebUI -co --exclude-standard | sed -n '1,200p'

Repository: TNG/AI-Playground

Length of output: 7936


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== ensure-precommit-hook.mjs ==\n'
cat -n WebUI/build/scripts/ensure-precommit-hook.mjs | sed -n '1,220p'

printf '\n== package.json snippets ==\n'
cat -n WebUI/package.json | sed -n '1,260p'

Repository: TNG/AI-Playground

Length of output: 9602


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== ensure-precommit-hook.mjs (1-240) ==\n'
sed -n '1,240p' WebUI/build/scripts/ensure-precommit-hook.mjs

printf '\n== package.json relevant scripts ==\n'
python3 - <<'PY'
import json
from pathlib import Path
p = Path("WebUI/package.json")
data = json.loads(p.read_text())
for k, v in data.get("scripts", {}).items():
    if any(tok in k.lower() for tok in ["setup", "hook", "uv", "precommit", "fetch", "install"]):
        print(f"{k}: {v}")
PY

printf '\n== search for uv/pre-commit installation wiring ==\n'
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' '\buvx?\b|pre-commit|fetch-external-resources|install-hooks|toolchain' WebUI -g '!WebUI/build/scripts/ensure-precommit-hook.mjs'

Repository: TNG/AI-Playground

Length of output: 14736


🏁 Script executed:

#!/bin/bash
set -euo pipefail

find WebUI -maxdepth 4 \( -name '*uv*' -o -name '*precommit*' -o -name '*hook*' \) | sed -n '1,200p'

Repository: TNG/AI-Playground

Length of output: 295


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import json, pathlib
pkg = json.loads(pathlib.Path("WebUI/package.json").read_text())
print("scripts:")
for k in sorted(pkg.get("scripts", {})):
    if any(s in k.lower() for s in ["setup", "hook", "fetch", "uv", "install"]):
        print(f"  {k}: {pkg['scripts'][k]}")
PY

printf '\n== files mentioning fetch-external-resources or install-hooks ==\n'
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' 'fetch-external-resources|install-hooks|ensure-precommit-hook|uvx|pre-commit' WebUI

Repository: TNG/AI-Playground

Length of output: 1710


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Narrowly inspect the installer and setup path
sed -n '1,220p' WebUI/build/scripts/ensure-precommit-hook.mjs
printf '\n----\n'
sed -n '1,220p' WebUI/package.json

Repository: TNG/AI-Playground

Length of output: 7992


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== fetch-external-resources.mts ==\n'
sed -n '1,260p' WebUI/build/scripts/fetch-external-resources.mts

printf '\n== uv.ts (top section) ==\n'
sed -n '1,120p' WebUI/electron/subprocesses/uvBasedBackends/uv.ts

printf '\n== aipgRoot.ts (resource names section) ==\n'
sed -n '1,80p' WebUI/electron/aipgRoot.ts

Repository: TNG/AI-Playground

Length of output: 15762


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== fetch-external-resources.mts ==\n'
sed -n '1,260p' WebUI/build/scripts/fetch-external-resources.mts

printf '\n== build-paths.mts ==\n'
sed -n '1,260p' WebUI/build/scripts/build-paths.mts

printf '\n== uv.ts top ==\n'
sed -n '1,80p' WebUI/electron/subprocesses/uvBasedBackends/uv.ts

printf '\n== aipgRoot.ts top ==\n'
sed -n '1,80p' WebUI/electron/aipgRoot.ts

Repository: TNG/AI-Playground

Length of output: 19937


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== uv resource handling in fetch-external-resources.mts ==\n'
rg -n -A4 -B4 'uvx|uv\.exe|resource.*uv|renameSync|extractAllTo|chmodSync|mv |cpSync|copy' WebUI/build/scripts/fetch-external-resources.mts

printf '\n== PATH wiring / environment setup ==\n'
rg -n -A3 -B3 'PATH=|process\.env\.PATH|append.*PATH|prepend.*PATH|build/resources|uvx\.exe|uv\.exe' WebUI

Repository: TNG/AI-Playground

Length of output: 14491


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== fetch script around the UV resource section ==\n'
sed -n '240,420p' WebUI/build/scripts/fetch-external-resources.mts

printf '\n== any PATH edits involving build/resources ==\n'
rg -n -A3 -B3 'process\.env\.PATH|PATH[^\n]*build/resources|build/resources[^\n]*PATH|prepend.*PATH|append.*PATH' WebUI

Repository: TNG/AI-Playground

Length of output: 11066


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== fetch-external-resources.mts (UV section) ==\n'
sed -n '260,420p' WebUI/build/scripts/fetch-external-resources.mts

printf '\n== search for PATH wiring in setup/install scripts ==\n'
rg -n -A3 -B3 'PATH|build/resources|uvx|uv\.exe|ensure-electron|install-hooks' WebUI/build WebUI/electron WebUI/package.json

Repository: TNG/AI-Playground

Length of output: 47137


Use the repo-managed uv.exe before the PATH-only fallback. Right now setup downloads uv, but this block still only probes global pre-commit/uvx, so hook installation is skipped on a fresh clone unless those tools are already installed. Resolve build/resources/uv.exe first, then fall back to PATH.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@WebUI/build/scripts/ensure-precommit-hook.mjs` around lines 71 - 83, The hook
selection logic in ensure-precommit-hook.mjs currently checks only global
pre-commit and uvx, which can skip installation on a fresh clone. Update the
runner resolution in the block that sets runner to first use the repo-managed
build/resources/uv.exe (the uv downloaded by setup), then fall back to
PATH-based pre-commit and uvx. Keep the existing install-failure messaging path
unchanged if none of the runners are available.

}

log(`Installing git pre-commit hook with: ${runner.join(' ')} install`)
const res = spawnSync(runner[0], [...runner.slice(1), 'install'], {
cwd: repoRoot,
stdio: 'inherit',
})
if (res.status !== 0) {
log('Could not install the pre-commit hook (continuing anyway).')
log('You can install it manually later with `pre-commit install`.')
process.exit(0)
}

log('pre-commit hook installed.')
Loading
Loading