mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-11 21:57:14 +03:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3950a8221d | ||
|
|
fc5dcefce2 | ||
|
|
5c67bf725d | ||
|
|
67d018fe05 | ||
|
|
3bdb9ff06c | ||
|
|
cd12f8660e | ||
|
|
43088d8fbf | ||
|
|
a8ce5962d3 | ||
|
|
7d6c5bdd92 | ||
|
|
73552b79c5 | ||
|
|
73a6f51a54 |
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
license: Apache 2.0
|
||||
allowed-tools:
|
||||
- Bash(npx impeccable *)
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
metadata:
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
---
|
||||
|
||||
This skill gives you the tools and permission to create design that earns to be called out-of-distribution craft: Whereas before, your design work would have been safe, timid and measured, you now approach every design task as an award-winning design director with impeccable understanding for what makes exceptional design work: production-grade code, peak creativity, a clear POV, deep understanding of the needs of the client and users, and exceptional craft.
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
{
|
||||
"name": "impeccable",
|
||||
"description": "Design fluency for frontend development. 1 skill with 23 commands (/impeccable polish, /impeccable audit, /impeccable critique, etc.) and curated anti-pattern detection.",
|
||||
"version": "4.2.3",
|
||||
"version": "4.3.1",
|
||||
"author": {
|
||||
"name": "Paul Bakaus",
|
||||
"email": "paul@paulbakaus.com"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "impeccable",
|
||||
"description": "Design fluency for frontend development. 1 skill with 23 commands (/impeccable polish, /impeccable audit, /impeccable critique, etc.) and curated anti-pattern detection.",
|
||||
"version": "4.2.3",
|
||||
"version": "4.3.1",
|
||||
"author": {
|
||||
"name": "Paul Bakaus",
|
||||
"email": "paul@paulbakaus.com"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
user-invocable: true
|
||||
argument-hint: "[shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]"
|
||||
license: Apache 2.0
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
license: Apache 2.0
|
||||
---
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
user-invocable: true
|
||||
license: Apache 2.0
|
||||
---
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
---
|
||||
|
||||
This skill gives you the tools and permission to create design that earns to be called out-of-distribution craft: Whereas before, your design work would have been safe, timid and measured, you now approach every design task as an award-winning design director with impeccable understanding for what makes exceptional design work: production-grade code, peak creativity, a clear POV, deep understanding of the needs of the client and users, and exceptional craft.
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
user-invocable: true
|
||||
argument-hint: "[shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]"
|
||||
license: Apache 2.0
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
user-invocable: true
|
||||
argument-hint: "[shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]"
|
||||
license: Apache 2.0
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
license: Apache 2.0
|
||||
---
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
license: Apache 2.0
|
||||
---
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
user-invocable: true
|
||||
argument-hint: "[shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]"
|
||||
license: Apache 2.0
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
license: Apache 2.0
|
||||
allowed-tools:
|
||||
- Bash(npx impeccable *)
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
user-invocable: true
|
||||
argument-hint: "[shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]"
|
||||
license: Apache 2.0
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
user-invocable: true
|
||||
argument-hint: "[shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]"
|
||||
license: Apache 2.0
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
user-invocable: true
|
||||
argument-hint: "[shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]"
|
||||
license: Apache 2.0
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
user-invocable: true
|
||||
argument-hint: "[shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]"
|
||||
license: Apache 2.0
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
license: Apache 2.0
|
||||
---
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
user-invocable: true
|
||||
license: Apache 2.0
|
||||
allowed-tools:
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
Generated
+16
-16
@@ -525,7 +525,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "impeccable"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"impeccable-browser",
|
||||
@@ -544,7 +544,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "impeccable-browser"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"impeccable-core",
|
||||
@@ -558,7 +558,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "impeccable-bundle"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"impeccable-core",
|
||||
@@ -567,14 +567,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "impeccable-common"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impeccable-comp"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"image",
|
||||
"once_cell",
|
||||
@@ -586,7 +586,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "impeccable-comp-verbs"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"impeccable-common",
|
||||
"impeccable-comp",
|
||||
@@ -599,7 +599,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "impeccable-context"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"impeccable-common",
|
||||
@@ -618,7 +618,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "impeccable-core"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"impeccable-core",
|
||||
"impeccable-foundation",
|
||||
@@ -630,7 +630,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "impeccable-detect"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"impeccable-common",
|
||||
"impeccable-core",
|
||||
@@ -642,7 +642,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "impeccable-foundation"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"cssparser",
|
||||
"once_cell",
|
||||
@@ -655,7 +655,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "impeccable-hook"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"impeccable-common",
|
||||
"impeccable-context",
|
||||
@@ -669,7 +669,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "impeccable-html"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"cssparser",
|
||||
"ego-tree",
|
||||
@@ -690,7 +690,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "impeccable-live"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"getrandom 0.2.17",
|
||||
"impeccable-common",
|
||||
@@ -708,7 +708,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "impeccable-skills"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"impeccable-common",
|
||||
"impeccable-context",
|
||||
@@ -727,7 +727,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "impeccable-wasm"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"impeccable-core",
|
||||
"impeccable-detect",
|
||||
@@ -1758,7 +1758,7 @@ checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc"
|
||||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"impeccable-bundle",
|
||||
]
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ resolver = "2"
|
||||
members = ["crates/*"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
publish = false
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
"zod": "^4.3.6",
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@impeccable/cli-darwin-arm64": "0.1.4",
|
||||
"@impeccable/cli-darwin-x64": "0.1.4",
|
||||
"@impeccable/cli-linux-arm64": "0.1.4",
|
||||
"@impeccable/cli-linux-x64": "0.1.4",
|
||||
"@impeccable/cli-windows-x64": "0.1.4",
|
||||
"@impeccable/cli-darwin-arm64": "0.1.5",
|
||||
"@impeccable/cli-darwin-x64": "0.1.5",
|
||||
"@impeccable/cli-linux-arm64": "0.1.5",
|
||||
"@impeccable/cli-linux-x64": "0.1.5",
|
||||
"@impeccable/cli-windows-x64": "0.1.5",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -72,15 +72,15 @@
|
||||
|
||||
"@hono/node-server": ["@hono/node-server@1.19.14", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw=="],
|
||||
|
||||
"@impeccable/cli-darwin-arm64": ["@impeccable/cli-darwin-arm64@0.1.4", "", { "os": "darwin", "cpu": "arm64", "bin": { "impeccable-darwin-arm64": "bin/impeccable" } }, "sha512-NNG8NLjQuNfyXwRBA72Jo2z4H8yd60qhkKF7a25pFR/+DKckH5V6TRIqNfgOaxlOWmY6RTUvPvGR4xSJDWLMGA=="],
|
||||
"@impeccable/cli-darwin-arm64": ["@impeccable/cli-darwin-arm64@0.1.5", "", { "os": "darwin", "cpu": "arm64", "bin": { "impeccable-darwin-arm64": "bin/impeccable" } }, "sha512-RvSrbsvzHsyfVaL693mMiWWLauZXas00F5VrNoaqOUvOm478HJUIUnl77H393F9k9LWZNiNFLwIinRnGJ92naA=="],
|
||||
|
||||
"@impeccable/cli-darwin-x64": ["@impeccable/cli-darwin-x64@0.1.4", "", { "os": "darwin", "cpu": "x64", "bin": { "impeccable-darwin-x64": "bin/impeccable" } }, "sha512-oGVjCvm/3DjpxDfMJcKVC+JFbZkTK3vnolI2AY5Uqkv82qjGJOgMQTp6XQIuNQEyrHB8Gv2CWj2XRMvnFpNR0Q=="],
|
||||
"@impeccable/cli-darwin-x64": ["@impeccable/cli-darwin-x64@0.1.5", "", { "os": "darwin", "cpu": "x64", "bin": { "impeccable-darwin-x64": "bin/impeccable" } }, "sha512-wYfhNhVtgosKIjxxWdJ3X/KydASdDIAAdmng8shKUgfiCkY9Tld0psuhVJOdkKoJaRajagp7a2f/tCNN1xdBZA=="],
|
||||
|
||||
"@impeccable/cli-linux-arm64": ["@impeccable/cli-linux-arm64@0.1.4", "", { "os": "linux", "cpu": "arm64", "bin": { "impeccable-linux-arm64": "bin/impeccable" } }, "sha512-t67/u941EhnjibHrMwDWrvImaLBUw7T5RMmnRv4FDmb2xPO56Cusi4S2d7P11+wkNMGYWe8xKhn9eelsvvGRsw=="],
|
||||
"@impeccable/cli-linux-arm64": ["@impeccable/cli-linux-arm64@0.1.5", "", { "os": "linux", "cpu": "arm64", "bin": { "impeccable-linux-arm64": "bin/impeccable" } }, "sha512-gjWJbOa7/wAECkIOac/x6mq43lDGaGwH87/ErsnrFwk/s5hgtlBF0sPePeB30e/lu1VzGMJcXVUEA8EULv4cCg=="],
|
||||
|
||||
"@impeccable/cli-linux-x64": ["@impeccable/cli-linux-x64@0.1.4", "", { "os": "linux", "cpu": "x64", "bin": { "impeccable-linux-x64": "bin/impeccable" } }, "sha512-0jkLoaVW1m5yVbtJeybSJjgp7p+LMw1EuP0yzosQq5uiSYjUArVDtLHPrBvDJHPVquPLtMV9+X2maDJJkgyvkg=="],
|
||||
"@impeccable/cli-linux-x64": ["@impeccable/cli-linux-x64@0.1.5", "", { "os": "linux", "cpu": "x64", "bin": { "impeccable-linux-x64": "bin/impeccable" } }, "sha512-JZU7kd7s4DOz9w2gwbbpI1u3YLhrcAUNZg/awjZcQFHJwpizv/MhPVUjHKYuChrPn+Oxlpw7KU8adKY1YrSq8g=="],
|
||||
|
||||
"@impeccable/cli-windows-x64": ["@impeccable/cli-windows-x64@0.1.4", "", { "os": "win32", "cpu": "x64", "bin": { "impeccable-windows-x64": "bin/impeccable.exe" } }, "sha512-aBBEEYcIlQ//pZEtLHNozSZJCgMZpQlpX1GVwyu9B9hM5PJA4Y9i/0g5jEyQ2Cbrjsfz9kOF6FalQk+AIrXC0Q=="],
|
||||
"@impeccable/cli-windows-x64": ["@impeccable/cli-windows-x64@0.1.5", "", { "os": "win32", "cpu": "x64", "bin": { "impeccable-windows-x64": "bin/impeccable.exe" } }, "sha512-kVZFjQIjb84mF2klxTspxE1OilalO/OmYDQnLkCJeDJCQLr6+L6HvbghJ1TChq87uyMu4GQNDPr9VV+h+Z+tCQ=="],
|
||||
|
||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
||||
|
||||
|
||||
@@ -30,9 +30,39 @@ static LAUNCHER_HOOK_MARKER: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new(r#"skills/impeccable/scripts/impeccable(?:\.cmd|\.exe)?["']?\s+hook(?:-before-edit|-probe|-after-edit|-stop)?(?:\s|$|["'&|;)])"#).unwrap()
|
||||
});
|
||||
|
||||
/// User-scope Windows commands embed a JSON-quoted path whose backslashes are
|
||||
/// doubled in the command string (#784). #604's single `\`→`/` replace is not
|
||||
/// enough: `\\` becomes `//`, which breaks `skills/impeccable` matching.
|
||||
/// A leading `//` after a quote (or at the start of the string) is a UNC
|
||||
/// prefix and stays two slashes, so doctor still probes `//server/share/...`.
|
||||
fn normalize_hook_separators(command: &str) -> String {
|
||||
let mut out = String::with_capacity(command.len());
|
||||
let mut chars = command.chars().peekable();
|
||||
let mut prev: Option<char> = None;
|
||||
while let Some(ch) = chars.next() {
|
||||
if ch != '\\' && ch != '/' {
|
||||
out.push(ch);
|
||||
prev = Some(ch);
|
||||
continue;
|
||||
}
|
||||
let mut n = 1usize;
|
||||
while matches!(chars.peek(), Some('\\' | '/')) {
|
||||
chars.next();
|
||||
n += 1;
|
||||
}
|
||||
out.push('/');
|
||||
if n >= 2 && matches!(prev, None | Some('"' | '\'')) {
|
||||
out.push('/');
|
||||
}
|
||||
prev = Some('/');
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// True when `command` invokes an Impeccable hook in either generation's spelling.
|
||||
pub fn is_impeccable_hook_command(command: &str) -> bool {
|
||||
LEGACY_HOOK_SCRIPT_MARKERS.iter().any(|m| command.contains(m)) || LAUNCHER_HOOK_MARKER.is_match(command)
|
||||
let command = normalize_hook_separators(command);
|
||||
LEGACY_HOOK_SCRIPT_MARKERS.iter().any(|m| command.contains(m)) || LAUNCHER_HOOK_MARKER.is_match(&command)
|
||||
}
|
||||
|
||||
/// True when `command` invokes an Impeccable hook in the launcher generation
|
||||
@@ -40,7 +70,7 @@ pub fn is_impeccable_hook_command(command: &str) -> bool {
|
||||
/// one: install/update use this to decide which manifests still need
|
||||
/// migrating to the launcher form.
|
||||
pub fn is_launcher_hook_command(command: &str) -> bool {
|
||||
LAUNCHER_HOOK_MARKER.is_match(command)
|
||||
LAUNCHER_HOOK_MARKER.is_match(&normalize_hook_separators(command))
|
||||
}
|
||||
|
||||
/// The launcher-era markers `context` and `doctor` treat as the design hook
|
||||
@@ -54,9 +84,10 @@ static LAUNCHER_DESIGN_HOOK: Lazy<Regex> = Lazy::new(|| {
|
||||
/// `hook-before-edit`) in either spelling; the JS `context.mjs` scan and
|
||||
/// `staleness-deep` HOOK_SCRIPT_MARKERS both meant exactly these two.
|
||||
pub fn is_design_hook_command(command: &str) -> bool {
|
||||
let command = normalize_hook_separators(command);
|
||||
command.contains("skills/impeccable/scripts/hook.mjs")
|
||||
|| command.contains("skills/impeccable/scripts/hook-before-edit.mjs")
|
||||
|| LAUNCHER_DESIGN_HOOK.is_match(command)
|
||||
|| LAUNCHER_DESIGN_HOOK.is_match(&command)
|
||||
}
|
||||
|
||||
/// True when `command` runs the design hook (`hook` / `hook-before-edit`) in
|
||||
@@ -66,7 +97,7 @@ pub fn is_design_hook_command(command: &str) -> bool {
|
||||
/// so the hook is dead and `MANUAL_DETECTOR_REQUIRED` must fire until an
|
||||
/// install/update repairs it.
|
||||
pub fn is_launcher_design_hook_command(command: &str) -> bool {
|
||||
LAUNCHER_DESIGN_HOOK.is_match(command)
|
||||
LAUNCHER_DESIGN_HOOK.is_match(&normalize_hook_separators(command))
|
||||
}
|
||||
|
||||
/// The shell token that names the hook program inside `command`, for
|
||||
@@ -74,7 +105,11 @@ pub fn is_launcher_design_hook_command(command: &str) -> bool {
|
||||
/// path in the binary form. `None` when the command carries no marker or
|
||||
/// the token cannot be isolated (a `'\''` escape sequence, for instance).
|
||||
pub fn hook_program_token(command: &str) -> Option<String> {
|
||||
if !is_design_hook_command(command) {
|
||||
if command.contains("'\\''") {
|
||||
return None;
|
||||
}
|
||||
let command = normalize_hook_separators(command);
|
||||
if !is_design_hook_command(&command) {
|
||||
return None;
|
||||
}
|
||||
static QUOTED: Lazy<Regex> = Lazy::new(|| {
|
||||
@@ -86,16 +121,13 @@ pub fn hook_program_token(command: &str) -> Option<String> {
|
||||
static BARE: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new(r#"([^\s"'|&;()]*skills/impeccable/scripts/(?:hook(?:-before-edit)?\.mjs|impeccable(?:\.cmd|\.exe)?))"#).unwrap()
|
||||
});
|
||||
if let Some(m) = QUOTED.captures(command) {
|
||||
if let Some(m) = QUOTED.captures(&command) {
|
||||
return Some(m[1].to_string());
|
||||
}
|
||||
if command.contains("'\\''") {
|
||||
return None;
|
||||
}
|
||||
if let Some(m) = SINGLE.captures(command) {
|
||||
if let Some(m) = SINGLE.captures(&command) {
|
||||
return Some(m[1].to_string());
|
||||
}
|
||||
BARE.captures(command).map(|m| m[1].to_string())
|
||||
BARE.captures(&command).map(|m| m[1].to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -183,4 +215,45 @@ mod tests {
|
||||
assert_eq!(hook_program_token("'/x/it'\\''s/.claude/skills/impeccable/scripts/impeccable' hook"), None);
|
||||
assert_eq!(hook_program_token("echo hi"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recognizes_json_escaped_windows_launcher_path() {
|
||||
let launcher = r"C:\Users\alice\.claude\skills\impeccable\scripts\impeccable";
|
||||
let quoted = serde_json::to_string(launcher).unwrap();
|
||||
let cmd = format!("[ ! -f {quoted} ] || {quoted} hook");
|
||||
assert!(is_impeccable_hook_command(&cmd), "{cmd}");
|
||||
assert!(is_launcher_hook_command(&cmd), "{cmd}");
|
||||
assert!(is_design_hook_command(&cmd), "{cmd}");
|
||||
assert_eq!(
|
||||
hook_program_token(&cmd).as_deref(),
|
||||
Some("C:/Users/alice/.claude/skills/impeccable/scripts/impeccable")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recognizes_single_backslash_windows_path() {
|
||||
let cmd = r#"[ ! -f "C:\Users\alice\.claude\skills\impeccable\scripts\impeccable" ] || "C:\Users\alice\.claude\skills\impeccable\scripts\impeccable" hook"#;
|
||||
assert!(is_impeccable_hook_command(cmd), "{cmd}");
|
||||
assert!(is_launcher_hook_command(cmd), "{cmd}");
|
||||
assert!(is_design_hook_command(cmd), "{cmd}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preserves_unc_prefix_in_program_token() {
|
||||
let launcher = r"\\server\share\.claude\skills\impeccable\scripts\impeccable";
|
||||
let quoted = serde_json::to_string(launcher).unwrap();
|
||||
let json_escaped = format!("[ ! -f {quoted} ] || {quoted} hook");
|
||||
assert!(is_impeccable_hook_command(&json_escaped), "{json_escaped}");
|
||||
assert_eq!(
|
||||
hook_program_token(&json_escaped).as_deref(),
|
||||
Some("//server/share/.claude/skills/impeccable/scripts/impeccable")
|
||||
);
|
||||
|
||||
let single = r#"[ ! -f "\\server\share\.claude\skills\impeccable\scripts\impeccable" ] || "\\server\share\.claude\skills\impeccable\scripts\impeccable" hook"#;
|
||||
assert!(is_impeccable_hook_command(single), "{single}");
|
||||
assert_eq!(
|
||||
hook_program_token(single).as_deref(),
|
||||
Some("//server/share/.claude/skills/impeccable/scripts/impeccable")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1350,6 +1350,7 @@ pub fn collect_browser_findings(dom: &dyn Dom, config: &BrowserConfig) -> Collec
|
||||
let mut findings: Vec<BrowserFinding> = Vec::new();
|
||||
findings.extend(hits(ec::check_element_borders_dom(dom, el)));
|
||||
findings.extend(hits(ec::check_element_pseudo_stripe_dom(dom, el)));
|
||||
findings.extend(hits(ec::check_element_stripe_child_dom(dom, el)));
|
||||
findings.extend(hits(ec::check_element_colors_dom(dom, el)));
|
||||
findings.extend(hits(ec::check_element_motion_dom(dom, el)));
|
||||
findings.extend(hits(ec::check_element_glow_dom(dom, el)));
|
||||
|
||||
@@ -20,8 +20,9 @@ use crate::checks::measures::{
|
||||
};
|
||||
use crate::checks::rules::{
|
||||
check_borders, check_colors, check_glow, check_hero_eyebrow, check_icon_tile,
|
||||
check_italic_serif, check_motion, is_emoji_only_text, BorderOpts, ColorOpts, GlowOpts,
|
||||
HeroEyebrowOpts, IconTileOpts, ItalicSerifOpts, MotionOpts, RuleHit, Sides, HEADING_TAGS,
|
||||
check_italic_serif, check_motion, check_stripe_child, is_emoji_only_text, BorderOpts,
|
||||
ColorOpts, GlowOpts, HeroEyebrowOpts, IconTileOpts, ItalicSerifOpts, MotionOpts, RuleHit,
|
||||
Sides, HEADING_TAGS,
|
||||
};
|
||||
use crate::checks::text_rules::{
|
||||
CURSOR_FIRST_VIEWPORT_PX, CURSOR_GLYPH_RE, POSITIONED_CHILD_INTERACTIVE_SELECTOR,
|
||||
@@ -367,6 +368,57 @@ pub fn check_element_pseudo_stripe_dom(dom: &dyn Dom, el: ElId) -> Vec<RuleHit>
|
||||
findings
|
||||
}
|
||||
|
||||
const STRIPE_CHILD_SKIP: &str = "nav, blockquote, pre, table, button, a, select, progress, meter, [role=\"progressbar\"], [role=\"slider\"], [role=\"scrollbar\"], [role=\"separator\"], [role=\"tablist\"]";
|
||||
|
||||
/// JS: checks.mjs#checkElementStripeChildDOM(el)
|
||||
pub fn check_element_stripe_child_dom(dom: &dyn Dom, el: ElId) -> Vec<RuleHit> {
|
||||
let tag = tag_lower(dom, el);
|
||||
if tag != "div" && tag != "span" {
|
||||
return Vec::new();
|
||||
}
|
||||
let Some(host) = dom.parent(el) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let host_tag = tag_lower(dom, host);
|
||||
if host_tag == "body" || host_tag == "html" {
|
||||
return Vec::new();
|
||||
}
|
||||
if !dom.children(el).is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
if !js::trim(&collapse_ws(&dom.text_content(el))).is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
if closest_or_none(dom, el, STRIPE_CHILD_SKIP).is_some() {
|
||||
return Vec::new();
|
||||
}
|
||||
if !is_rendered_for_browser_rule(dom, el) {
|
||||
return Vec::new();
|
||||
}
|
||||
if is_tab_context_element(dom, el) || is_status_context_element(dom, el) {
|
||||
return Vec::new();
|
||||
}
|
||||
let host_rect = dom.rect(host);
|
||||
if host_rect.width < 40.0 || host_rect.height < 20.0 {
|
||||
return Vec::new();
|
||||
}
|
||||
let child_rect = dom.rect(el);
|
||||
if child_rect.height < host_rect.height - 44.0 || child_rect.height < host_rect.height * 0.5 {
|
||||
return Vec::new();
|
||||
}
|
||||
let hugs = |v: f64| v.is_finite() && v.abs() <= 3.0;
|
||||
let edge = if hugs(child_rect.left - host_rect.left) {
|
||||
Some("left")
|
||||
} else if hugs(host_rect.right - child_rect.right) {
|
||||
Some("right")
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let width = child_rect.width;
|
||||
let bg = parse_rgb_or_any(&dom.style(el, "backgroundColor"));
|
||||
check_stripe_child(&class_selector(dom, el), width, edge, bg)
|
||||
}
|
||||
|
||||
/// JS: checks.mjs#readPseudoSurfaceDOM(el, rect)
|
||||
pub fn read_pseudo_surface_dom(dom: &dyn Dom, el: ElId, rect: &Rect) -> Option<Rgba> {
|
||||
for which in PSEUDOS {
|
||||
@@ -1378,6 +1430,44 @@ mod tests {
|
||||
assert!(check_element_pseudo_stripe_dom(&d, card).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stripe_child_flags_left_edge_and_skips_neutral_text_and_tab_context() {
|
||||
let (mut d, body) = page();
|
||||
let host = d.add(Some(body), "div");
|
||||
visible(&mut d, host);
|
||||
d.set_attr(host, "class", "card");
|
||||
d.set_rect(host, 0.0, 0.0, 300.0, 100.0);
|
||||
let stripe = d.add(Some(host), "div");
|
||||
visible(&mut d, stripe);
|
||||
d.set_rect(stripe, 0.0, 0.0, 4.0, 100.0);
|
||||
d.set_styles(
|
||||
stripe,
|
||||
&[
|
||||
("backgroundColor", "rgb(245, 158, 11)"),
|
||||
("width", "4px"),
|
||||
("height", "100px"),
|
||||
],
|
||||
);
|
||||
let hits = check_element_stripe_child_dom(&d, stripe);
|
||||
assert_eq!(hits.len(), 1);
|
||||
assert_eq!(hits[0].id, "side-tab");
|
||||
assert_eq!(hits[0].snippet, "div — 4px stripe child (left)");
|
||||
d.set_styles(stripe, &[("backgroundColor", "rgb(120, 120, 120)")]);
|
||||
assert!(check_element_stripe_child_dom(&d, stripe).is_empty());
|
||||
let stripe_text = d.add(Some(host), "div");
|
||||
visible(&mut d, stripe_text);
|
||||
d.set_rect(stripe_text, 4.0, 0.0, 4.0, 100.0);
|
||||
d.set_styles(
|
||||
stripe_text,
|
||||
&[("backgroundColor", "rgb(245, 158, 11)")],
|
||||
);
|
||||
d.add_text(stripe_text, "x");
|
||||
assert!(check_element_stripe_child_dom(&d, stripe_text).is_empty());
|
||||
d.set_styles(stripe, &[("backgroundColor", "rgb(245, 158, 11)")]);
|
||||
d.set_attr(host, "class", "card is-active");
|
||||
assert!(check_element_stripe_child_dom(&d, stripe).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn colors_low_contrast_on_resolved_surface_and_pseudo_surface() {
|
||||
let (mut d, body) = page();
|
||||
|
||||
@@ -88,6 +88,39 @@ pub fn check_borders(
|
||||
findings
|
||||
}
|
||||
|
||||
/// Pure gate for dedicated stripe-child side-tabs (empty narrow chromatic
|
||||
/// `div`/`span` at a card edge).
|
||||
pub fn check_stripe_child(
|
||||
selector: &str,
|
||||
width: f64,
|
||||
edge: Option<&str>,
|
||||
bg: Option<Rgba>,
|
||||
) -> Vec<RuleHit> {
|
||||
let Some(edge) = edge else {
|
||||
return Vec::new();
|
||||
};
|
||||
if !(width >= 2.0 && width <= 12.0) {
|
||||
return Vec::new();
|
||||
}
|
||||
let Some(bg) = bg else {
|
||||
return Vec::new();
|
||||
};
|
||||
if bg.alpha_or_one() <= 0.1 {
|
||||
return Vec::new();
|
||||
}
|
||||
let spread = js::math_max3(bg.r, bg.g, bg.b) - js::math_min3(bg.r, bg.g, bg.b);
|
||||
if spread < 30.0 {
|
||||
return Vec::new();
|
||||
}
|
||||
vec![RuleHit::new(
|
||||
"side-tab",
|
||||
format!(
|
||||
"{selector} — {}px stripe child ({edge})",
|
||||
number_to_string(math_round(width))
|
||||
),
|
||||
)]
|
||||
}
|
||||
|
||||
re!(GRADIENT_CI, ci("gradient"));
|
||||
|
||||
re!(
|
||||
|
||||
@@ -146,36 +146,65 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Opening-tag span that contains `index`, if any. `end` is the `>` byte.
|
||||
fn markup_tag_span(line: &str, index: usize) -> Option<(usize, usize)> {
|
||||
let mut i = 0usize;
|
||||
while i < line.len() {
|
||||
let Some(rel) = line[i..].find('<') else {
|
||||
return None;
|
||||
};
|
||||
let tag_start = i + rel;
|
||||
let after = &line[tag_start + 1..];
|
||||
if !after.chars().next().is_some_and(|c| c.is_ascii_alphabetic()) {
|
||||
i = tag_start + 1;
|
||||
continue;
|
||||
}
|
||||
let mut tag_end: Option<usize> = None;
|
||||
scan_js(line, tag_start + 1, |ch, j, _p, _n, depth| {
|
||||
if ch == '>' && depth.brace == 0 {
|
||||
tag_end = Some(j);
|
||||
return true;
|
||||
}
|
||||
false
|
||||
});
|
||||
let Some(end) = tag_end else {
|
||||
return None;
|
||||
};
|
||||
if index >= tag_start && index <= end {
|
||||
return Some((tag_start, end));
|
||||
}
|
||||
i = end + 1;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// JS: detect-text.mjs#containingMarkupTag (only its `text` is read).
|
||||
fn containing_markup_tag(line: &str) -> impl Fn(usize) -> String + '_ {
|
||||
move |index: usize| {
|
||||
let mut i = 0usize;
|
||||
while i < line.len() {
|
||||
let Some(rel) = line[i..].find('<') else { break };
|
||||
let tag_start = i + rel;
|
||||
let after = &line[tag_start + 1..];
|
||||
if !after.chars().next().is_some_and(|c| c.is_ascii_alphabetic()) {
|
||||
i = tag_start + 1;
|
||||
continue;
|
||||
}
|
||||
let mut tag_end: Option<usize> = None;
|
||||
scan_js(line, tag_start + 1, |ch, j, _p, _n, depth| {
|
||||
if ch == '>' && depth.brace == 0 {
|
||||
tag_end = Some(j);
|
||||
return true;
|
||||
}
|
||||
false
|
||||
});
|
||||
let Some(end) = tag_end else { break };
|
||||
if index >= tag_start && index <= end {
|
||||
return line[tag_start..end + 1].to_string();
|
||||
}
|
||||
i = end + 1;
|
||||
}
|
||||
line.to_string()
|
||||
markup_tag_span(line, index)
|
||||
.map(|(start, end)| line[start..end + 1].to_string())
|
||||
.unwrap_or_else(|| line.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn is_self_closing_tag(tag: &str) -> bool {
|
||||
tag.trim_end_matches('>').trim_end().ends_with('/')
|
||||
}
|
||||
|
||||
/// Text path cannot see the DOM. When this line holds a whole tag, require
|
||||
/// it empty or self-closing. A class list with no `<` is a split JSX tag,
|
||||
/// so emptiness is unknown and the other gates still apply.
|
||||
fn stripe_child_markup_empty(line: &str, index: usize) -> bool {
|
||||
let Some((start, end)) = markup_tag_span(line, index) else {
|
||||
return true;
|
||||
};
|
||||
if is_self_closing_tag(&line[start..end + 1]) {
|
||||
return true;
|
||||
}
|
||||
let rest = line.get(end + 1..).unwrap_or("").trim_start();
|
||||
rest.starts_with("</")
|
||||
}
|
||||
|
||||
struct TernarySplit {
|
||||
common: String,
|
||||
consequent: String,
|
||||
@@ -494,6 +523,43 @@ re!(
|
||||
SIDE_TAB_JS_RE,
|
||||
format!("border(?:Left|Right){WS}*[:=]{WS}*[\"'`]({D}+)px{WS}+solid")
|
||||
);
|
||||
re!(
|
||||
SIDE_TAB_STRIPE_CHILD_TW_RE,
|
||||
r"w-(?:0\.5|1(?:\.5)?|2(?:\.5)?|3|\[(?:[2-9]|1[0-2])px\])"
|
||||
);
|
||||
re!(STRIPE_CHILD_HEIGHT_TOKEN_RE, r"h-(?:px\b|[0-9]|\[)");
|
||||
re!(
|
||||
STRIPE_CHILD_ARIA_RE,
|
||||
r"(?i)aria-(?:current|selected)"
|
||||
);
|
||||
re!(STRIPE_CHILD_ROUNDED_FULL_RE, format!("{B}rounded-full{B}"));
|
||||
re!(
|
||||
STRIPE_CHILD_CUE_RE,
|
||||
format!("{B}(?:shrink-0|rounded-[lres](?:-{W}+)?|left-0|right-0|inset-y-0){B}")
|
||||
);
|
||||
|
||||
/// Hyphen-safe class-token boundary: the byte before `index` must not be `-`
|
||||
/// or an ASCII word character (mirrors JS `(?<![\w-])`; the `regex` crate has
|
||||
/// no lookbehind).
|
||||
fn hyphen_safe_prefix(text: &str, index: usize) -> bool {
|
||||
match text.as_bytes().get(index.wrapping_sub(1)) {
|
||||
Some(b) if index > 0 => !b.is_ascii_alphanumeric() && *b != b'-',
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn hyphen_safe_suffix(text: &str, end: usize) -> bool {
|
||||
!matches!(
|
||||
text.as_bytes().get(end),
|
||||
Some(b) if b.is_ascii_alphanumeric() || *b == b'-' || *b == b'.' || *b == b'/'
|
||||
)
|
||||
}
|
||||
|
||||
fn scope_has_fixed_height(scope: &str) -> bool {
|
||||
STRIPE_CHILD_HEIGHT_TOKEN_RE.find_iter(scope).any(|m| {
|
||||
hyphen_safe_prefix(scope, m.start())
|
||||
})
|
||||
}
|
||||
re!(BORDER_ACCENT_TW_RE, format!("{B}border-[tb]-({D}+){B}"));
|
||||
re!(
|
||||
BORDER_ACCENT_CSS_RE,
|
||||
@@ -864,6 +930,29 @@ pub static REGEX_MATCHERS: Lazy<Vec<Matcher>> = Lazy::new(|| {
|
||||
test: |m, _| num(m.g(1)) >= 3.0,
|
||||
fmt: |m, _| m.whole().to_string(),
|
||||
},
|
||||
Matcher {
|
||||
id: "side-tab",
|
||||
find_all: |l| all(&SIDE_TAB_STRIPE_CHILD_TW_RE, l),
|
||||
test: |m, line| {
|
||||
if !hyphen_safe_prefix(line, m.index)
|
||||
|| !hyphen_safe_suffix(line, m.index + m.whole().len())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
let scope = containing_markup_tag(line)(m.index);
|
||||
find_solid_chromatic_bg(&scope).is_some()
|
||||
&& stripe_child_markup_empty(line, m.index)
|
||||
&& STRIPE_CHILD_CUE_RE.is_match(&scope)
|
||||
&& !scope_has_fixed_height(&scope)
|
||||
&& !STRIPE_CHILD_ROUNDED_FULL_RE.is_match(&scope)
|
||||
&& !STRIPE_CHILD_ARIA_RE.is_match(&scope)
|
||||
},
|
||||
fmt: |m, line| {
|
||||
let scope = containing_markup_tag(line)(m.index);
|
||||
let bg = find_solid_chromatic_bg(&scope).unwrap();
|
||||
format!("{} + {bg} stripe child", m.whole())
|
||||
},
|
||||
},
|
||||
Matcher {
|
||||
id: "border-accent-on-rounded",
|
||||
find_all: |l| all(&BORDER_ACCENT_TW_RE, l),
|
||||
@@ -1433,6 +1522,45 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stripe_child_tailwind() {
|
||||
let s = |line: &str| run("side-tab", line);
|
||||
assert_eq!(
|
||||
s(r#"<div className="w-1 shrink-0 rounded-l-lg bg-amber-500" />"#),
|
||||
vec!["w-1 + bg-amber-500 stripe child"]
|
||||
);
|
||||
assert_eq!(
|
||||
s(r#"<div class="w-[4px] bg-blue-500 shrink-0"></div>"#),
|
||||
vec!["w-[4px] + bg-blue-500 stripe child"]
|
||||
);
|
||||
assert_eq!(
|
||||
s(r#"<span className="w-0.5 bg-rose-500 shrink-0" />"#),
|
||||
vec!["w-0.5 + bg-rose-500 stripe child"]
|
||||
);
|
||||
assert_eq!(
|
||||
s(r#"<div className="w-1 min-h-0 bg-amber-500 shrink-0" />"#),
|
||||
vec!["w-1 + bg-amber-500 stripe child"]
|
||||
);
|
||||
assert!(s(r#"<div className="w-2 h-2 rounded-full bg-green-500" />"#).is_empty());
|
||||
assert!(s(
|
||||
r#"<div className="flex items-center gap-1.5"><div className="w-3 h-3 rounded bg-amber-500" /><span className="text-slate-400">Vital few</span></div>"#
|
||||
)
|
||||
.is_empty());
|
||||
assert!(s(r#"<div className="w-1 bg-amber-500/10" />"#).is_empty());
|
||||
assert!(s(r#"<a className="w-1 bg-amber-500" aria-current="page"></a>"#).is_empty());
|
||||
assert!(s(
|
||||
r#"<div className="w-1 shrink-0"><span className="bg-amber-500" /></div>"#
|
||||
)
|
||||
.is_empty());
|
||||
assert!(s(r#"<div className="w-1 bg-amber-500">|</div>"#).is_empty());
|
||||
assert!(s(r#"<div className="w-1 bg-amber-500" />"#).is_empty());
|
||||
assert_eq!(
|
||||
s(r#" className="w-1 shrink-0 rounded-l-lg bg-amber-500""#),
|
||||
vec!["w-1 + bg-amber-500 stripe child"]
|
||||
);
|
||||
assert!(s(r#"<div className="w-1 shrink-0 bg-amber-500">"#).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matchers() {
|
||||
assert_eq!(
|
||||
|
||||
+117
-4
@@ -9,9 +9,10 @@ use crate::background::{
|
||||
a_ge, a_gt, read_own_background_color, resolve_background, resolve_background_info,
|
||||
resolve_border_radius_px, resolve_gradient_stops, sv, sv_opt, CustomPropMap,
|
||||
};
|
||||
use crate::cascade::StyleValues;
|
||||
use crate::cascade::{expand_static_box_values, split_css_tokens, StyleValues};
|
||||
use crate::dom::{StaticDocument, StaticElement};
|
||||
use crate::quality::{collapse_ws, pf0, resolve_font_size_px};
|
||||
use impeccable_core::checks::css_scan::css_length_to_px;
|
||||
use impeccable_core::checks::measures::{
|
||||
self, border_colors_from_style, border_widths_from_style, check_gpt_thin_border_wide_shadow,
|
||||
check_oversized_h1, check_radial_spotlight, positioned_style_implies_escape, resolve_length_px,
|
||||
@@ -20,9 +21,9 @@ use impeccable_core::checks::measures::{
|
||||
use impeccable_core::checks::rules::{
|
||||
check_borders, check_colors, check_glow, check_hero_eyebrow, check_hover_contrast,
|
||||
check_icon_tile, check_italic_serif, check_kicker_above_heading, check_motion,
|
||||
is_emoji_only_text, is_heading_tag, resolve_hero_heading_size_px, BorderOpts, ColorOpts,
|
||||
GlowOpts, HeroEyebrowOpts, HoverContrastOpts, IconTileOpts, ItalicSerifOpts, KickerCandidate,
|
||||
MotionOpts, RuleHit, Sides,
|
||||
check_stripe_child, is_emoji_only_text, is_heading_tag, resolve_hero_heading_size_px,
|
||||
BorderOpts, ColorOpts, GlowOpts, HeroEyebrowOpts, HoverContrastOpts, IconTileOpts,
|
||||
ItalicSerifOpts, KickerCandidate, MotionOpts, RuleHit, Sides,
|
||||
};
|
||||
use impeccable_core::checks::text_rules::{
|
||||
check_numbered_section_labels, is_kicker_candidate, is_numbered_section_label_candidate,
|
||||
@@ -460,6 +461,118 @@ pub fn check_element_borders(
|
||||
)
|
||||
}
|
||||
|
||||
const STRIPE_CHILD_SKIP: &str = "nav, blockquote, pre, table, button, a, select, progress, meter, [role=\"progressbar\"], [role=\"slider\"], [role=\"scrollbar\"], [role=\"separator\"], [role=\"tablist\"]";
|
||||
|
||||
fn static_edge_hugs(value: &str) -> bool {
|
||||
let n = parse_float(value);
|
||||
n.is_finite() && n.abs() <= 2.0
|
||||
}
|
||||
|
||||
fn static_resolved_inset(style: &StyleValues) -> [String; 4] {
|
||||
let mut out = [
|
||||
sv(style, "top").to_string(),
|
||||
sv(style, "right").to_string(),
|
||||
sv(style, "bottom").to_string(),
|
||||
sv(style, "left").to_string(),
|
||||
];
|
||||
let inset = sv(style, "inset");
|
||||
if !inset.is_empty() {
|
||||
let expanded = expand_static_box_values(&split_css_tokens(inset));
|
||||
for (i, val) in expanded.into_iter().enumerate() {
|
||||
if out[i].is_empty() || out[i] == "auto" {
|
||||
out[i] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// JS: checks.mjs#checkElementStripeChild(el, style)
|
||||
pub fn check_element_stripe_child(el: &StaticElement<'_>, style: &StyleValues) -> Vec<RuleHit> {
|
||||
let tag = el.tag_lower();
|
||||
if tag != "div" && tag != "span" {
|
||||
return Vec::new();
|
||||
}
|
||||
let Some(host) = el.parent_element() else {
|
||||
return Vec::new();
|
||||
};
|
||||
if host.tag_lower() == "body" || host.tag_lower() == "html" {
|
||||
return Vec::new();
|
||||
}
|
||||
if !el.children().is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
if !collapsed_text_content(el).is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
if el.closest(STRIPE_CHILD_SKIP).is_some() {
|
||||
return Vec::new();
|
||||
}
|
||||
if is_tab_context_element(el) || is_status_context_element(el) {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let width = css_length_to_px(sv(style, "width")).unwrap_or_else(|| pf0(sv(style, "width")));
|
||||
let position = js::to_lower_case(sv(style, "position"));
|
||||
let host_style = host.style();
|
||||
let edge = if position == "absolute" || position == "fixed" {
|
||||
let height_raw = sv(style, "height");
|
||||
let inset = static_resolved_inset(style);
|
||||
let height_stretches =
|
||||
height_raw == "100%" || (static_edge_hugs(&inset[0]) && static_edge_hugs(&inset[2]));
|
||||
if !height_stretches {
|
||||
return Vec::new();
|
||||
}
|
||||
if static_edge_hugs(&inset[3]) {
|
||||
Some("left")
|
||||
} else if static_edge_hugs(&inset[1]) {
|
||||
Some("right")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
let pdisplay = sv(host_style, "display");
|
||||
if !pdisplay.contains("flex") {
|
||||
return Vec::new();
|
||||
}
|
||||
let pdir = sv(host_style, "flexDirection");
|
||||
if pdir.starts_with("column") {
|
||||
return Vec::new();
|
||||
}
|
||||
let align_self = sv(style, "alignSelf");
|
||||
let effective_align = if !align_self.is_empty() && align_self != "auto" {
|
||||
align_self
|
||||
} else {
|
||||
sv(host_style, "alignItems")
|
||||
};
|
||||
let is_stretch = effective_align.is_empty()
|
||||
|| effective_align == "stretch"
|
||||
|| effective_align == "normal";
|
||||
let height_raw = sv(style, "height");
|
||||
let height_stretches =
|
||||
height_raw == "100%" || ((height_raw.is_empty() || height_raw == "auto") && is_stretch);
|
||||
if !height_stretches {
|
||||
return Vec::new();
|
||||
}
|
||||
let siblings = host.children();
|
||||
if siblings.len() < 2 {
|
||||
return Vec::new();
|
||||
}
|
||||
let reverse = pdir.contains("reverse");
|
||||
if siblings.first() == Some(el) {
|
||||
Some(if reverse { "right" } else { "left" })
|
||||
} else if siblings.last() == Some(el) {
|
||||
Some(if reverse { "left" } else { "right" })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let bg_raw = sv(style, "backgroundColor");
|
||||
let bg = parse_rgb(Some(&bg_raw)).or_else(|| parse_any_color(Some(&bg_raw)));
|
||||
check_stripe_child(&class_selector(el), width, edge, bg)
|
||||
}
|
||||
|
||||
/// JS: checks.mjs#checkElementColors(el, style, tag, window, customPropMap, hasAnchorInheritRule)
|
||||
pub fn check_element_colors(
|
||||
el: &StaticElement<'_>,
|
||||
|
||||
@@ -184,6 +184,9 @@ pub const STATIC_PROP_MAP: &[(&str, &str)] = &[
|
||||
("left", "left"),
|
||||
("inset", "inset"),
|
||||
("display", "display"),
|
||||
("flex-direction", "flexDirection"),
|
||||
("align-items", "alignItems"),
|
||||
("align-self", "alignSelf"),
|
||||
("overflow", "overflow"),
|
||||
("overflow-x", "overflowX"),
|
||||
("overflow-y", "overflowY"),
|
||||
|
||||
@@ -65,7 +65,8 @@
|
||||
//! parse in the same process).
|
||||
|
||||
use super::csstree::{self, Important, Node};
|
||||
use super::shorthand::expand_static_declaration;
|
||||
use super::shorthand::{expand_static_box_values, expand_static_declaration, Expanded};
|
||||
use super::values::split_css_tokens;
|
||||
use impeccable_core::js;
|
||||
use indexmap::IndexMap;
|
||||
use once_cell::sync::Lazy;
|
||||
@@ -177,6 +178,32 @@ impl<K: Hash + Eq> SpecifiedStore<K> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Layout properties the stripe-child static adapter needs that are not in
|
||||
/// the frozen `expandStaticDeclaration` allowlist. Applied here so the
|
||||
/// recorded vectors stay byte-equal.
|
||||
fn extra_specified_expansions(prop: &str, value: &str) -> Vec<Expanded> {
|
||||
let p = js::to_lower_case(prop);
|
||||
let v = js::trim(value);
|
||||
if v.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
match p.as_str() {
|
||||
"flex-direction" => vec![("flexDirection".into(), v.to_string())],
|
||||
"align-items" => vec![("alignItems".into(), v.to_string())],
|
||||
"align-self" => vec![("alignSelf".into(), v.to_string())],
|
||||
"inset" => {
|
||||
let vals = expand_static_box_values(&split_css_tokens(v));
|
||||
vec![
|
||||
("top".into(), vals[0].clone()),
|
||||
("right".into(), vals[1].clone()),
|
||||
("bottom".into(), vals[2].clone()),
|
||||
("left".into(), vals[3].clone()),
|
||||
]
|
||||
}
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// JS: css-cascade.mjs#applyStaticDeclaration(specified, node, prop, value, meta)
|
||||
pub fn apply_static_declaration<K: Hash + Eq>(
|
||||
specified: &mut SpecifiedStore<K>,
|
||||
@@ -186,7 +213,11 @@ pub fn apply_static_declaration<K: Hash + Eq>(
|
||||
meta: &DeclMeta,
|
||||
) {
|
||||
let map = specified.map.entry(node).or_default();
|
||||
for (expanded_prop, expanded_value) in expand_static_declaration(prop, value) {
|
||||
let extra = extra_specified_expansions(prop, value);
|
||||
for (expanded_prop, expanded_value) in expand_static_declaration(prop, value)
|
||||
.into_iter()
|
||||
.chain(extra)
|
||||
{
|
||||
let existing = map.get(&expanded_prop).map(|d| &d.meta);
|
||||
if compare_static_priority(existing, meta) {
|
||||
let next = SpecifiedDecl {
|
||||
|
||||
@@ -14,8 +14,9 @@ use crate::adapters::{
|
||||
check_element_colors, check_element_glow, check_element_gpt_border_shadow,
|
||||
check_element_hero_eyebrow, check_element_hover_contrast, check_element_icon_tile,
|
||||
check_element_italic_serif, check_element_motion, check_element_oversized_h1,
|
||||
check_element_radial_spotlight, check_kicker_above_heading_from_doc,
|
||||
check_numbered_section_labels_from_doc, scoped_ignore_active,
|
||||
check_element_radial_spotlight, check_element_stripe_child,
|
||||
check_kicker_above_heading_from_doc, check_numbered_section_labels_from_doc,
|
||||
scoped_ignore_active,
|
||||
};
|
||||
use crate::background::{resolve_background, resolve_border_radius_px, sv};
|
||||
use crate::cascade::{build_static_style_map, collect_static_css_text};
|
||||
@@ -109,6 +110,7 @@ const STATIC_ELEMENT_RULES: &[(&str, &str)] = &[
|
||||
("dark-glow", "*"),
|
||||
("motion-rules", "*"),
|
||||
("icon-tile-stack", "h1,h2,h3,h4,h5,h6"),
|
||||
("stripe-child", "div,span"),
|
||||
("italic-serif-display", "h1,h2"),
|
||||
("hero-eyebrow-chip", "h1"),
|
||||
("broken-image", "img"),
|
||||
@@ -134,6 +136,7 @@ fn run_rule(rule_id: &str, el: &StaticElement<'_>, tag: &str) -> Vec<RuleHit> {
|
||||
}
|
||||
"motion-rules" => check_element_motion(tag, style),
|
||||
"icon-tile-stack" => check_element_icon_tile(el, tag),
|
||||
"stripe-child" => check_element_stripe_child(el, style),
|
||||
"italic-serif-display" => check_element_italic_serif(el, style, tag),
|
||||
"hero-eyebrow-chip" => check_element_hero_eyebrow(el, style, tag),
|
||||
"broken-image" => check_element_broken_image(el),
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
use impeccable_html::{detect_html_source, DetectHtmlOptions};
|
||||
use std::path::Path;
|
||||
|
||||
fn side_tab_snippets(html: &str) -> Vec<String> {
|
||||
detect_html_source(
|
||||
html,
|
||||
Path::new("/app/stripe.html"),
|
||||
&DetectHtmlOptions::default(),
|
||||
)
|
||||
.into_iter()
|
||||
.filter(|f| f.antipattern == "side-tab")
|
||||
.map(|f| f.snippet)
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flex_row_first_child_flags() {
|
||||
let html = r#"<!DOCTYPE html><html><head><style>
|
||||
.card { display: flex; flex-direction: row; width: 320px; height: 100px; }
|
||||
.stripe { width: 4px; background: #f59e0b; }
|
||||
.body { flex: 1; }
|
||||
</style></head><body>
|
||||
<div class="card"><div class="stripe"></div><div class="body">Content</div></div>
|
||||
</body></html>"#;
|
||||
let hits = side_tab_snippets(html);
|
||||
assert_eq!(hits.len(), 1);
|
||||
assert!(hits[0].contains("stripe child (left)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn absolute_left_inset_flags() {
|
||||
let html = r#"<!DOCTYPE html><html><head><style>
|
||||
.card { position: relative; width: 320px; height: 100px; }
|
||||
.stripe { position: absolute; inset: 0 auto 0 0; width: 4px; background: #3b82f6; }
|
||||
</style></head><body>
|
||||
<div class="card"><div class="stripe"></div></div>
|
||||
</body></html>"#;
|
||||
let hits = side_tab_snippets(html);
|
||||
assert_eq!(hits.len(), 1);
|
||||
assert!(hits[0].contains("stripe child (left)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn absolute_top_bottom_flags() {
|
||||
let html = r#"<!DOCTYPE html><html><head><style>
|
||||
.card { position: relative; width: 320px; height: 100px; }
|
||||
.stripe { position: absolute; left: 0; top: 0; bottom: 0; width: 4px; background: #3b82f6; }
|
||||
</style></head><body>
|
||||
<div class="card"><div class="stripe"></div></div>
|
||||
</body></html>"#;
|
||||
let hits = side_tab_snippets(html);
|
||||
assert_eq!(hits.len(), 1);
|
||||
assert!(hits[0].contains("stripe child (left)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flex_column_does_not_flag() {
|
||||
let html = r#"<!DOCTYPE html><html><head><style>
|
||||
.card { display: flex; flex-direction: column; width: 320px; height: 100px; }
|
||||
.stripe { width: 4px; background: #f59e0b; }
|
||||
</style></head><body>
|
||||
<div class="card"><div class="stripe"></div><div>Body</div></div>
|
||||
</body></html>"#;
|
||||
assert!(side_tab_snippets(html).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn align_items_center_does_not_flag() {
|
||||
let html = r#"<!DOCTYPE html><html><head><style>
|
||||
.card { display: flex; align-items: center; width: 320px; height: 100px; }
|
||||
.stripe { width: 4px; background: #f59e0b; }
|
||||
</style></head><body>
|
||||
<div class="card"><div class="stripe"></div><div>Body</div></div>
|
||||
</body></html>"#;
|
||||
assert!(side_tab_snippets(html).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn align_self_flex_start_does_not_flag() {
|
||||
let html = r#"<!DOCTYPE html><html><head><style>
|
||||
.card { display: flex; width: 320px; height: 100px; }
|
||||
.stripe { width: 4px; align-self: flex-start; background: #f59e0b; }
|
||||
</style></head><body>
|
||||
<div class="card"><div class="stripe"></div><div>Body</div></div>
|
||||
</body></html>"#;
|
||||
assert!(side_tab_snippets(html).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn neutral_and_contentful_and_wide_do_not_flag() {
|
||||
let neutral = r#"<!DOCTYPE html><html><head><style>
|
||||
.card { display: flex; width: 320px; height: 100px; }
|
||||
.stripe { width: 4px; background: #e5e5e5; }
|
||||
</style></head><body>
|
||||
<div class="card"><div class="stripe"></div><div>Body</div></div>
|
||||
</body></html>"#;
|
||||
assert!(side_tab_snippets(neutral).is_empty());
|
||||
|
||||
let text = r#"<!DOCTYPE html><html><head><style>
|
||||
.card { display: flex; width: 320px; height: 100px; }
|
||||
.stripe { width: 4px; background: #f59e0b; }
|
||||
</style></head><body>
|
||||
<div class="card"><div class="stripe">!</div><div>Body</div></div>
|
||||
</body></html>"#;
|
||||
assert!(side_tab_snippets(text).is_empty());
|
||||
|
||||
let wide = r#"<!DOCTYPE html><html><head><style>
|
||||
.card { display: flex; width: 320px; height: 100px; }
|
||||
.stripe { width: 40px; background: #f59e0b; }
|
||||
</style></head><body>
|
||||
<div class="card"><div class="stripe"></div><div>Body</div></div>
|
||||
</body></html>"#;
|
||||
assert!(side_tab_snippets(wide).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rem_width_flags() {
|
||||
let html = r#"<!DOCTYPE html><html><head><style>
|
||||
.card { display: flex; width: 320px; height: 100px; }
|
||||
.stripe { width: 0.25rem; background: #f59e0b; }
|
||||
</style></head><body>
|
||||
<div class="card"><div class="stripe"></div><div>Body</div></div>
|
||||
</body></html>"#;
|
||||
let hits = side_tab_snippets(html);
|
||||
assert_eq!(hits.len(), 1);
|
||||
assert!(hits[0].contains("stripe child (left)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn height_full_with_align_center_flags() {
|
||||
let html = r#"<!DOCTYPE html><html><head><style>
|
||||
.card { display: flex; align-items: center; width: 320px; height: 100px; }
|
||||
.stripe { width: 4px; height: 100%; background: #f59e0b; }
|
||||
</style></head><body>
|
||||
<div class="card"><div class="stripe"></div><div>Body</div></div>
|
||||
</body></html>"#;
|
||||
let hits = side_tab_snippets(html);
|
||||
assert_eq!(hits.len(), 1);
|
||||
assert!(hits[0].contains("stripe child (left)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inset_after_left_longhand_flags() {
|
||||
let html = r#"<!DOCTYPE html><html><head><style>
|
||||
.card { position: relative; width: 320px; height: 100px; }
|
||||
.stripe { position: absolute; left: 10px; inset: 0 auto 0 0; width: 4px; background: #3b82f6; }
|
||||
</style></head><body>
|
||||
<div class="card"><div class="stripe"></div></div>
|
||||
</body></html>"#;
|
||||
let hits = side_tab_snippets(html);
|
||||
assert_eq!(hits.len(), 1);
|
||||
assert!(hits[0].contains("stripe child (left)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn row_reverse_first_child_is_right() {
|
||||
let html = r#"<!DOCTYPE html><html><head><style>
|
||||
.card { display: flex; flex-direction: row-reverse; width: 320px; height: 100px; }
|
||||
.stripe { width: 4px; background: #f59e0b; }
|
||||
.body { flex: 1; }
|
||||
</style></head><body>
|
||||
<div class="card"><div class="stripe"></div><div class="body">Content</div></div>
|
||||
</body></html>"#;
|
||||
let hits = side_tab_snippets(html);
|
||||
assert_eq!(hits.len(), 1);
|
||||
assert!(hits[0].contains("stripe child (right)"));
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -596,6 +596,18 @@ fn write_carbonize_banner(event: &Map<String, Value>, io: &mut Io) {
|
||||
}
|
||||
}
|
||||
|
||||
fn reply_ack_json(reply: &Reply) -> Value {
|
||||
let mut m = Map::new();
|
||||
m.insert("ok".into(), json!(true));
|
||||
m.insert("id".into(), json!(reply.id));
|
||||
m.insert("status".into(), json!(reply.ty));
|
||||
if let Some(f) = &reply.file {
|
||||
m.insert("file".into(), json!(f));
|
||||
}
|
||||
m.insert("_instructions".into(), json!("Poll again now."));
|
||||
Value::Object(m)
|
||||
}
|
||||
|
||||
/// JS: printPollEvent(event) — a wire-supplied `_instructions` must never
|
||||
/// win over the locally generated one (#488).
|
||||
fn print_poll_event(event: &mut Value, io: &mut Io) {
|
||||
@@ -718,7 +730,13 @@ pub fn run(args: &[String], io: &mut Io) -> i32 {
|
||||
}
|
||||
};
|
||||
return match post_reply(&base, &token, &reply) {
|
||||
Ok(()) => 0,
|
||||
Ok(()) => {
|
||||
println(
|
||||
io,
|
||||
&serde_json::to_string(&reply_ack_json(&reply)).unwrap_or_default(),
|
||||
);
|
||||
0
|
||||
}
|
||||
Err(PollError::ConnRefused) => {
|
||||
io.err(&format!(
|
||||
"Live server not running. Start one with: {}\n",
|
||||
@@ -839,4 +857,40 @@ mod tests {
|
||||
}));
|
||||
assert!(parsed.get("_instructions").is_none(), "{}", parsed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reply_ack_json_includes_file_when_present() {
|
||||
let reply = Reply {
|
||||
id: "ab12cd34".into(),
|
||||
ty: "done".into(),
|
||||
message: None,
|
||||
file: Some("index.html".into()),
|
||||
data: None,
|
||||
source_event_type: None,
|
||||
};
|
||||
let parsed = reply_ack_json(&reply);
|
||||
assert_eq!(parsed["ok"], json!(true));
|
||||
assert_eq!(parsed["id"], json!("ab12cd34"));
|
||||
assert_eq!(parsed["status"], json!("done"));
|
||||
assert_eq!(parsed["file"], json!("index.html"));
|
||||
assert_eq!(parsed["_instructions"], json!("Poll again now."));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reply_ack_json_omits_file_when_absent() {
|
||||
let reply = Reply {
|
||||
id: "ab12cd34".into(),
|
||||
ty: "steer_done".into(),
|
||||
message: None,
|
||||
file: None,
|
||||
data: None,
|
||||
source_event_type: None,
|
||||
};
|
||||
let parsed = reply_ack_json(&reply);
|
||||
assert_eq!(parsed["ok"], json!(true));
|
||||
assert_eq!(parsed["id"], json!("ab12cd34"));
|
||||
assert_eq!(parsed["status"], json!("steer_done"));
|
||||
assert!(parsed.get("file").is_none(), "{}", parsed);
|
||||
assert_eq!(parsed["_instructions"], json!("Poll again now."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,6 +418,16 @@ fn push_diag(next: &mut Map<String, Value>, d: Value) {
|
||||
next.insert("diagnostics".to_string(), Value::Array(arr));
|
||||
}
|
||||
|
||||
fn drop_diag(next: &mut Map<String, Value>, error: &str) {
|
||||
let mut arr = next
|
||||
.get("diagnostics")
|
||||
.and_then(|v| v.as_array())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
arr.retain(|d| d.get("error").and_then(|e| e.as_str()) != Some(error));
|
||||
next.insert("diagnostics".to_string(), Value::Array(arr));
|
||||
}
|
||||
|
||||
/// JS: applyEvent(snapshot, entry)
|
||||
pub fn apply_event(snapshot: &Map<String, Value>, entry: &Value) -> Map<String, Value> {
|
||||
let event: Map<String, Value> = match entry.get("event") {
|
||||
@@ -864,6 +874,7 @@ pub fn apply_event(snapshot: &Map<String, Value>, entry: &Value) -> Map<String,
|
||||
set!("phase", json!("discarded"));
|
||||
set!("pendingEventSeq", Value::Null);
|
||||
set!("pendingEvent", Value::Null);
|
||||
drop_diag(&mut next, "carbonize_cleanup_required");
|
||||
}
|
||||
"complete" => {
|
||||
set!("phase", json!("completed"));
|
||||
@@ -876,6 +887,7 @@ pub fn apply_event(snapshot: &Map<String, Value>, entry: &Value) -> Map<String,
|
||||
set_if!("previewMode", ev("previewMode"));
|
||||
set!("pendingEventSeq", Value::Null);
|
||||
set!("pendingEvent", Value::Null);
|
||||
drop_diag(&mut next, "carbonize_cleanup_required");
|
||||
}
|
||||
"agent_error" => {
|
||||
if canceled && ev("sourceEventType").and_then(|v| v.as_str()) == Some("generate") {
|
||||
@@ -925,3 +937,63 @@ fn write_snapshot(path: &str, snapshot: &Map<String, Value>, journal_bytes: i64,
|
||||
pub fn get_str<'a>(m: &'a Map<String, Value>, k: &str) -> Option<&'a str> {
|
||||
get(m, k).and_then(|v| v.as_str())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
fn journal_entry(seq: i64, event: Value) -> Value {
|
||||
json!({ "seq": seq, "ts": "2026-01-01T00:00:00.000Z", "event": event })
|
||||
}
|
||||
|
||||
fn has_diag(snapshot: &Map<String, Value>, error: &str) -> bool {
|
||||
snapshot
|
||||
.get("diagnostics")
|
||||
.and_then(|v| v.as_array())
|
||||
.map(|a| {
|
||||
a.iter()
|
||||
.any(|d| d.get("error").and_then(|e| e.as_str()) == Some(error))
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn replay(id: &str, events: &[Value]) -> Map<String, Value> {
|
||||
let mut snap = base_snapshot(id);
|
||||
for entry in events {
|
||||
snap = apply_event(&snap, entry);
|
||||
}
|
||||
snap
|
||||
}
|
||||
|
||||
fn accept_carbonize_done(id: &str, terminal: &str) -> Map<String, Value> {
|
||||
replay(
|
||||
id,
|
||||
&[
|
||||
journal_entry(
|
||||
1,
|
||||
json!({ "id": id, "type": "accept", "variantId": 2 }),
|
||||
),
|
||||
journal_entry(
|
||||
2,
|
||||
json!({ "id": id, "type": "agent_done", "carbonize": true, "file": "index.html" }),
|
||||
),
|
||||
journal_entry(3, json!({ "id": id, "type": terminal })),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complete_drops_carbonize_cleanup_required() {
|
||||
let snap = accept_carbonize_done("ab12cd34", "complete");
|
||||
assert_eq!(snap.get("phase").and_then(|p| p.as_str()), Some("completed"));
|
||||
assert!(!has_diag(&snap, "carbonize_cleanup_required"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn discarded_drops_carbonize_cleanup_required() {
|
||||
let snap = accept_carbonize_done("ab12cd34", "discarded");
|
||||
assert_eq!(snap.get("phase").and_then(|p| p.as_str()), Some("discarded"));
|
||||
assert!(!has_diag(&snap, "carbonize_cleanup_required"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,13 +219,10 @@ fn rewrite_value(value: &Value, provider: &str, quoted: &QuotedPath, win32: bool
|
||||
}
|
||||
}
|
||||
|
||||
/// JS: valueHasImpeccableHookMarker(value). Command separators are
|
||||
/// normalized to `/` first so a legacy Windows-path guard is still
|
||||
/// recognized as ours and replaced instead of duplicated (upstream
|
||||
/// 665c51b9, #604).
|
||||
/// JS: valueHasImpeccableHookMarker(value).
|
||||
pub fn value_has_impeccable_hook_marker(value: &Value) -> bool {
|
||||
match value {
|
||||
Value::String(s) => is_impeccable_hook_command(&s.replace('\\', "/")),
|
||||
Value::String(s) => is_impeccable_hook_command(s),
|
||||
Value::Array(a) => a.iter().any(value_has_impeccable_hook_marker),
|
||||
Value::Object(o) => o.values().any(value_has_impeccable_hook_marker),
|
||||
_ => false,
|
||||
@@ -233,12 +230,9 @@ pub fn value_has_impeccable_hook_marker(value: &Value) -> bool {
|
||||
}
|
||||
|
||||
/// True when `value` names an Impeccable hook in the launcher generation.
|
||||
/// Separators are normalized to `/` first, matching
|
||||
/// `value_has_impeccable_hook_marker`, so a legacy Windows-path launcher
|
||||
/// command is still recognized.
|
||||
pub fn value_has_launcher_hook_marker(value: &Value) -> bool {
|
||||
match value {
|
||||
Value::String(s) => is_launcher_hook_command(&s.replace('\\', "/")),
|
||||
Value::String(s) => is_launcher_hook_command(s),
|
||||
Value::Array(a) => a.iter().any(value_has_launcher_hook_marker),
|
||||
Value::Object(o) => o.values().any(value_has_launcher_hook_marker),
|
||||
_ => false,
|
||||
|
||||
@@ -357,3 +357,57 @@ fn hook_artifacts_map_providers_to_manifest_files() {
|
||||
assert_eq!(c[0].dest, jsp::join(&["/p", ".codex", "hooks.json"]));
|
||||
assert!(c[0].shared_dest.is_none());
|
||||
}
|
||||
|
||||
fn windows_user_scope_hook_command() -> String {
|
||||
let launcher = r"C:\Users\alice\.claude\skills\impeccable\scripts\impeccable";
|
||||
let q = json_string(launcher);
|
||||
format!("[ ! -f {q} ] || {q} hook")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_json_escaped_windows_launcher_is_idempotent() {
|
||||
let cmd = windows_user_scope_hook_command();
|
||||
let hook_entry = |cmd: String| {
|
||||
json!({ "matcher": "Edit", "hooks": [{ "type": "command", "command": cmd }] })
|
||||
};
|
||||
let stop_entry = |cmd: String| {
|
||||
json!({ "hooks": [{ "type": "command", "command": cmd, "timeout": 30 }] })
|
||||
};
|
||||
let existing = json!({
|
||||
"hooks": {
|
||||
"PostToolUse": [hook_entry(cmd.clone())],
|
||||
"Stop": [stop_entry(cmd.clone())]
|
||||
}
|
||||
});
|
||||
let fresh = json!({
|
||||
"description": "fresh",
|
||||
"hooks": {
|
||||
"PostToolUse": [hook_entry(cmd.clone())],
|
||||
"Stop": [stop_entry(cmd.clone())]
|
||||
}
|
||||
});
|
||||
let merged = merge_hook_manifests(&existing, &fresh);
|
||||
assert_eq!(merged["hooks"]["PostToolUse"].as_array().unwrap().len(), 1);
|
||||
assert_eq!(merged["hooks"]["Stop"].as_array().unwrap().len(), 1);
|
||||
let merged2 = merge_hook_manifests(&merged, &fresh);
|
||||
assert_eq!(merged2["hooks"]["PostToolUse"].as_array().unwrap().len(), 1);
|
||||
assert_eq!(merged2["hooks"]["Stop"].as_array().unwrap().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_heals_triplicated_stop_groups() {
|
||||
let cmd = windows_user_scope_hook_command();
|
||||
let stop_entry = json!({ "hooks": [{ "type": "command", "command": cmd.clone(), "timeout": 30 }] });
|
||||
let existing = json!({
|
||||
"hooks": {
|
||||
"Stop": [stop_entry.clone(), stop_entry.clone(), stop_entry]
|
||||
}
|
||||
});
|
||||
let fresh = json!({
|
||||
"hooks": {
|
||||
"Stop": [json!({ "hooks": [{ "type": "command", "command": cmd, "timeout": 30 }] })]
|
||||
}
|
||||
});
|
||||
let merged = merge_hook_manifests(&existing, &fresh);
|
||||
assert_eq!(merged["hooks"]["Stop"].as_array().unwrap().len(), 1);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "impeccable",
|
||||
"version": "4.2.3",
|
||||
"version": "4.3.1",
|
||||
"description": "Design and refine interfaces with Impeccable skills, specialist agents, and design checks.",
|
||||
"author": {
|
||||
"name": "Renaissance Geek, Inc."
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: impeccable
|
||||
description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks.
|
||||
version: 4.2.3
|
||||
version: 4.3.1
|
||||
license: Apache 2.0
|
||||
---
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.4
|
||||
0.1.5
|
||||
|
||||
@@ -87,6 +87,11 @@ fi
|
||||
# Last resort: fetch this version's binary for the current platform from the
|
||||
# public release channel into the user cache. Needs network; sandboxes without
|
||||
# egress preinstall the binary on PATH instead.
|
||||
setup_help() {
|
||||
echo "Engine $version setup needs network access and write permission to $cache_root/bin/$version." >&2
|
||||
echo "Run this launcher ($0) with engine-probe in a terminal that has those permissions, then retry the original command." >&2
|
||||
echo "Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary." >&2
|
||||
}
|
||||
fetch_url() {
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
||||
@@ -119,7 +124,18 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
[ "$os" = windows ] && asset="$asset.exe"
|
||||
url="$base/engine-v$version/$asset"
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
if ! mkdir -p "$cache_root/bin/$version" 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot create cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
# Check the actual staging file, not just directory existence: a cache from
|
||||
# an earlier run can be readable but no longer writable inside a sandbox.
|
||||
if ! (umask 077; : > "$tmp") 2>/dev/null; then
|
||||
echo "impeccable: engine $version is not installed; cannot write to cache directory: $cache_root/bin/$version" >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
fetched=0
|
||||
if fetch_url "$url"; then
|
||||
fetched=1
|
||||
@@ -180,6 +196,9 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
echo "impeccable: could not download engine $version from $url; check network access, the release URL, and curl or wget availability." >&2
|
||||
setup_help
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
|
||||
@@ -68,18 +68,27 @@ rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto curl_missing
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/pbakaus/impeccable/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if exist "%IMPECCABLE_HOME%\bin\%version%\" goto cache_ready
|
||||
mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
if errorlevel 1 goto cache_directory_failed
|
||||
:cache_ready
|
||||
rem Check the staging file too: an existing directory may be read-only.
|
||||
rem Redirection failures do not reliably update ERRORLEVEL in cmd.exe;
|
||||
rem branch on the command's failure directly. Never treat a directory as a
|
||||
rem staging file (later del cleanup would prompt to delete its contents).
|
||||
if exist "%cached%.part\" goto cache_write_failed
|
||||
(type nul >"%cached%.part") 2>nul || goto cache_write_failed
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
if not "%arch%"=="arm64" goto download_failed
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/engine-v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if errorlevel 1 goto download_failed
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
@@ -176,6 +185,28 @@ if not errorlevel 1 set "probe_ok=1"
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:cache_directory_failed
|
||||
echo impeccable: engine %version% is not installed; cannot create cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:cache_write_failed
|
||||
echo impeccable: engine %version% is not installed; cannot write to cache directory: "%IMPECCABLE_HOME%\bin\%version%" 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:curl_missing
|
||||
echo impeccable: cannot download engine %version%; curl.exe is unavailable. 1>&2
|
||||
goto setup_failed
|
||||
|
||||
:download_failed
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not download engine %version% from %url%; check network access and the release URL. 1>&2
|
||||
|
||||
:setup_failed
|
||||
echo Engine %version% setup needs network access and write permission to "%IMPECCABLE_HOME%\bin\%version%". 1>&2
|
||||
echo Run this launcher ("%~f0") with engine-probe in a terminal that has those permissions, then retry the original command. 1>&2
|
||||
echo Alternatively, set IMPECCABLE_HOME to a writable cache location, or IMPECCABLE_BIN to a preinstalled engine binary. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
|
||||
@@ -332,7 +332,7 @@ Optional keys added later by engines (appended after the above): `ignoreValue` (
|
||||
#### Static and regex engines (only what affects the contract)
|
||||
|
||||
- `detectHtml`: reads file, imports `htmlparser2`, `css-select`, `css-tree`, `domutils`; on import failure prints once to stderr `impeccable detect: DEGRADED - HTML parser modules unavailable (htmlparser2, css-select, css-tree, domutils).\nFalling back to regex matching. Custom properties, selector matching and computed contrast are NOT evaluated; findings are an undercount, not a clean bill of health.\n` and falls back to `detectText`. Inlines `<link rel=stylesheet href>` that are local (not `/^(https?:)?\/\//i`), query/hash stripped. Runs element rules, design-system rules (`checkSourceDesignSystem` + `collectStaticDesignSystemFindings`, merged), then page rules only when `isFullPage(html)` (`/<!doctype\s|<html[\s>]|<head[\s>]/i` after stripping comments), plus text-content analyzers; ends with inline-ignore filtering.
|
||||
- `detectText`: regex line matchers (ids: side-tab, border-accent-on-rounded, overused-font, gradient-text, ai-color-palette, gray-on-color, bounce-easing, layout-transition, broken-image), inset-stripe/pseudo-stripe CSS scans, `codex-grid-background`, `<style>` blocks (Astro/Vue/Svelte), CSS-in-JS templates, design-system source checks; dedupe (same antipattern+snippet within 2 lines); page analyzers only when `isFullPage` and ext ∈ `{'.html','.htm','.astro','.vue','.svelte'}` or no ext (`<stdin>`): flat-type-hierarchy, monotonous-spacing, em-dash-overuse, marketing-buzzword, aphoristic-cadence, dark-glow (+ radial-halo, marquee); inline ignores last.
|
||||
- `detectText`: regex line matchers (ids: side-tab including Tailwind stripe-child empty `w-*` + chromatic `bg-*` plus a `shrink-0` / edge-round cue, border-accent-on-rounded, overused-font, gradient-text, ai-color-palette, gray-on-color, bounce-easing, layout-transition, broken-image), inset-stripe/pseudo-stripe CSS scans, `codex-grid-background`, `<style>` blocks (Astro/Vue/Svelte), CSS-in-JS templates, design-system source checks; dedupe (same antipattern+snippet within 2 lines); page analyzers only when `isFullPage` and ext ∈ `{'.html','.htm','.astro','.vue','.svelte'}` or no ext (`<stdin>`): flat-type-hierarchy, monotonous-spacing, em-dash-overuse, marketing-buzzword, aphoristic-cadence, dark-glow (+ radial-halo, marquee); inline ignores last.
|
||||
|
||||
#### Profiler (`cli/engine/profile/profiler.mjs`)
|
||||
|
||||
@@ -1740,7 +1740,7 @@ Conventions: every script's "run directly" guard is `process.argv[1]` ending wit
|
||||
|
||||
#### `live-poll.mjs` -> `impeccable poll`
|
||||
- Invoked from live.md poll loop; `--reply` forms quoted in `_instructions` (see instructions.mjs strings in 6.3/below).
|
||||
- Args: `--stream`, `--timeout=MS` (one-shot total, default 600000), `--types=A,B`, `--ack-timeout=MS` (stream, default 600000), `--reply <id> <status> [--file PATH] [--data JSON] [message]`, `--help`. `--reply` errors (stderr, exit 1): `Usage: node "<abs>/live-poll.mjs" --reply <id> <status> [--file path] [--data '<json>'] [message]` + `Missing event id after --reply.` / `The value after --reply must be the event id, not the status "done". Use --reply EVENT_ID done.` / `Missing reply status after event id "X".`; `--data must be valid JSON: <err>`.
|
||||
- Args: `--stream`, `--timeout=MS` (one-shot total, default 600000), `--types=A,B`, `--ack-timeout=MS` (stream, default 600000), `--reply <id> <status> [--file PATH] [--data JSON] [message]`, `--help`. `--reply` success (stdout, exit 0): one compact JSON line `{ok:true,id,status,file? (only when --file was passed),_instructions:'Poll again now.'}`. `--reply` errors (stderr, exit 1): `Usage: node "<abs>/live-poll.mjs" --reply <id> <status> [--file path] [--data '<json>'] [message]` + `Missing event id after --reply.` / `The value after --reply must be the event id, not the status "done". Use --reply EVENT_ID done.` / `Missing reply status after event id "X".`; `--data must be valid JSON: <err>`.
|
||||
- Needs `server.json`; else stderr `No running live server found. Start one with: node "<abs>/live.mjs"` exit 1.
|
||||
- One-shot: loops `GET /poll?token&timeout=<slice ≤270000>&leaseMs=600000[&types]` until an event or total deadline; prints one JSON line (`console.log(JSON.stringify(event))`) with `_instructions` added by `instructionsForEvent` (unless already present). For `accept`/`discard`: spawns `node live-accept.mjs --id ID (--discard | --variant N) [--page-url U] [--param-values JSON]` (30 s), sets `event._acceptResult` (parse failure/throw → `{handled:false, mode:'error', error}`), then POSTs completion `{id, type: completionType, sourceEventType: event.type, message: _acceptResult.error, file: _acceptResult.file, data: {carbonize:true}?}` where completionType = discard: `discarded` if handled else `error`; accept: `agent_done` if handled&carbonize, `complete` if handled, `error` if mode error or (svelte-component unhandled), else `agent_done`; sets `event._completionAck = {ok:true, type}` (+ `final:false, requiresComplete:true, nextCommand:'live-complete.mjs --id <id>', message:'Carbonize cleanup must be verified, then the session must be completed explicitly before polling again.'` for carbonize) or `{ok:false, error}`. Stderr banners: manual_edit_apply → 4-line banner starting `Manual Apply action required: edit source, then reply with \`live-poll.mjs --reply <id> done --data '<json>'\`.`; carbonize → `⚠ Carbonize cleanup REQUIRED before next poll. After cleanup, run live-complete.mjs --id <id>. See reference/live.md "Required after accept".`
|
||||
- Stream: stderr `[impeccable-poll] stream mode: one JSON object per line on stdout; use --reply while this process stays running`; after each reply-needing event waits (poll `/status` every 400 ms) until the id leaves `pendingEvents` (else `Timed out waiting for --reply on event <id>` exit 1); returns on `exit`.
|
||||
|
||||
+6
-6
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "impeccable",
|
||||
"version": "4.0.5",
|
||||
"version": "4.1.0",
|
||||
"author": "Paul Bakaus",
|
||||
"description": "Design skills, commands, and anti-pattern detection for AI coding agents",
|
||||
"keywords": [
|
||||
@@ -73,11 +73,11 @@
|
||||
"check:engine-release": "node scripts/check-engine-release.mjs"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@impeccable/cli-darwin-arm64": "0.1.4",
|
||||
"@impeccable/cli-darwin-x64": "0.1.4",
|
||||
"@impeccable/cli-linux-x64": "0.1.4",
|
||||
"@impeccable/cli-linux-arm64": "0.1.4",
|
||||
"@impeccable/cli-windows-x64": "0.1.4"
|
||||
"@impeccable/cli-darwin-arm64": "0.1.5",
|
||||
"@impeccable/cli-darwin-x64": "0.1.5",
|
||||
"@impeccable/cli-linux-x64": "0.1.5",
|
||||
"@impeccable/cli-linux-arm64": "0.1.5",
|
||||
"@impeccable/cli-windows-x64": "0.1.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ai-sdk/anthropic": "^4.0.7",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user