mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-11 21:57:14 +03:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
329fa3e9f4 | ||
|
|
bb7663ecfb | ||
|
|
707fb6061c | ||
|
|
044a04fd0d | ||
|
|
0ac0b6866f | ||
|
|
2a9957589c | ||
|
|
4bee58d89e | ||
|
|
a443ec0d8f | ||
|
|
94d8611af6 | ||
|
|
6e61113e3c | ||
|
|
381d52b38f |
@@ -69,7 +69,8 @@ Choose the mode from the requested surface, not the product, and persist it only
|
||||
Routing:
|
||||
|
||||
- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command.
|
||||
- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Explicit or clearly implied request to run a command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Workflow or command-selection question:** read [Workflow questions](reference/routing.md#workflow-questions).
|
||||
- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes a new surface or replacement world through init, then new-work; a narrow refinement of existing code proceeds on the incumbent implementation as `impeccable context` directs, offering init afterward rather than blocking on it.
|
||||
- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions.
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ const __impeccableLiveDev =
|
||||
process.env.NODE_ENV === "development" ? ["http://localhost:8400"] : [];
|
||||
```
|
||||
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: `tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts`, `tests/framework-fixtures/sveltekit-csp/expected-after-patch.js`. Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: [nextjs-turborepo/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts), [sveltekit-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/sveltekit-csp/expected-after-patch.js). Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
|
||||
### append-string
|
||||
|
||||
@@ -93,7 +93,7 @@ const __impeccableLiveDev =
|
||||
- `script-src 'self' 'unsafe-inline'` becomes `` `script-src 'self' 'unsafe-inline'${__impeccableLiveDev}` ``
|
||||
- `connect-src 'self'` becomes `` `connect-src 'self'${__impeccableLiveDev}` ``
|
||||
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: `tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js`, `tests/framework-fixtures/nuxt-csp/expected-after-patch.ts`.
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: [nextjs-inline-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js), [nuxt-csp/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nuxt-csp/expected-after-patch.ts).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ Complete HTML replacement of the original element per variant, not a CSS-only pa
|
||||
|
||||
Replace the style opening tag with `cssAuthoring.styleTag` when the tool returns a different one. **Each variant div contains exactly one top-level element**, same tag as the original; loose siblings break outline tracking and accept. First variant visible, all others `display: none`. The browser's MutationObserver accepts atomic or progressive arrival; accepting an arrived variant fences the worker, so later publications are rejected.
|
||||
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in `tests/live-e2e/agent.mjs` is a faithful template.
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in the [repo agent template](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/live-e2e/agent.mjs) is a faithful template.
|
||||
|
||||
**JSX / TSX targets:** wrap `<style>` content in a template literal (CSS braces would parse as JSX), use `className=` / `style={{…}}`, keep `data-impeccable-*` attributes as plain strings:
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
# No-argument routing: the context-aware menu
|
||||
# Command guidance
|
||||
|
||||
## Workflow questions
|
||||
|
||||
Give advice without executing commands; the menu below is only for bare invocations. Consult relevant command references as needed for prerequisites and scope. Link to the [docs](https://impeccable.style/docs/) for the broader workflow guide. If the user also requests execution, follow that request.
|
||||
|
||||
## No-argument routing: the context-aware menu
|
||||
|
||||
Read this when the user invokes `/impeccable` with no argument. They are asking "what should I do?" Make the menu context-aware instead of static.
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.0
|
||||
0.1.1
|
||||
|
||||
@@ -96,6 +96,19 @@ fetch_url() {
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
check_download() {
|
||||
download_file=${1:-$tmp}
|
||||
if [ ! -f "$download_file" ]; then
|
||||
rm -f "$tmp.sha256"
|
||||
echo "impeccable: download completed but the file was removed before execution: $url; check your antivirus quarantine or logs. Refusing to continue; do not disable protection." >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ ! -s "$download_file" ]; then
|
||||
rm -f "$download_file" "$tmp.sha256"
|
||||
echo "impeccable: downloaded file is empty: $url; refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
}
|
||||
if [ -n "$probing" ]; then
|
||||
# Inside another launcher's probe: no download, fail fast and quiet.
|
||||
exit 127
|
||||
@@ -116,6 +129,7 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
fetch_url "$url" && fetched=1
|
||||
fi
|
||||
if [ "$fetched" = 1 ]; then
|
||||
check_download
|
||||
# Fail closed: a freshly downloaded binary runs only after verifying
|
||||
# against its .sha256 sidecar. A sidecar that cannot be fetched, or a
|
||||
# machine with no sha256 tool, refuses the download instead of exec'ing
|
||||
@@ -127,15 +141,20 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -q -O "$tmp.sha256" "$url.sha256" 2>/dev/null && sidecar_ok=1
|
||||
fi
|
||||
check_download
|
||||
expected=""
|
||||
[ "$sidecar_ok" = 1 ] && expected=$(cut -d' ' -f1 < "$tmp.sha256")
|
||||
actual=""
|
||||
if command -v shasum >/dev/null 2>&1; then actual=$(shasum -a 256 "$tmp" | cut -d' ' -f1)
|
||||
elif command -v sha256sum >/dev/null 2>&1; then actual=$(sha256sum "$tmp" | cut -d' ' -f1); fi
|
||||
if command -v shasum >/dev/null 2>&1; then
|
||||
if digest=$(shasum -a 256 "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
elif command -v sha256sum >/dev/null 2>&1; then
|
||||
if digest=$(sha256sum "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
fi
|
||||
check_download
|
||||
rm -f "$tmp.sha256"
|
||||
if [ -z "$expected" ] || [ -z "$actual" ]; then
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or no sha256 tool); refusing the unverified download" >&2
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or hashing failed); refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ "$actual" != "$expected" ]; then
|
||||
@@ -143,8 +162,22 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
echo "impeccable: checksum mismatch downloading $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
chmod +x "$tmp" 2>/dev/null
|
||||
mv -f "$tmp" "$cached" && exec "$cached" "$@"
|
||||
check_download
|
||||
if ! chmod +x "$tmp" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not make the verified download executable: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download
|
||||
if ! mv -f "$tmp" "$cached" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not cache the verified download: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download "$cached"
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
fi
|
||||
|
||||
@@ -82,6 +82,8 @@ curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
rem Mirrors the sh launcher and fails closed: a freshly downloaded binary
|
||||
rem runs only after verifying against its .sha256 sidecar. A sidecar that
|
||||
rem cannot be fetched, or an empty certutil result, refuses the download
|
||||
@@ -91,8 +93,16 @@ if errorlevel 1 goto verify_refuse
|
||||
set "expected="
|
||||
set /p expected=<"%cached%.sha256"
|
||||
for /f "tokens=1" %%h in ("%expected%") do set "expected=%%h"
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
set "actual="
|
||||
for /f "skip=1 delims=" %%h in ('certutil -hashfile "%cached%.part" SHA256 2^>nul') do if not defined actual set "actual=%%h"
|
||||
rem Reuse the sidecar staging file after reading expected. Check certutil's
|
||||
rem status before parsing: its error text on stdout is not a digest.
|
||||
certutil -hashfile "%cached%.part" SHA256 >"%cached%.sha256" 2>nul
|
||||
if errorlevel 1 goto verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
for /f "usebackq skip=1 delims=" %%h in ("%cached%.sha256") do if not defined actual set "actual=%%h"
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
if not defined expected goto verify_refuse
|
||||
if not defined actual goto verify_refuse
|
||||
@@ -103,17 +113,48 @@ echo impeccable: checksum mismatch downloading %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: cannot verify %url% against %url%.sha256; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:check_download
|
||||
set "download_file=%~1"
|
||||
if not defined download_file set "download_file=%cached%.part"
|
||||
if not exist "%download_file%" goto download_missing
|
||||
for %%f in ("%download_file%") do if %%~zf==0 goto download_empty
|
||||
exit /b 0
|
||||
|
||||
:download_missing
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: download completed but the file was removed before execution: %url%; check your antivirus quarantine or logs. Refusing to continue; do not disable protection. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:download_empty
|
||||
del "%download_file%" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: downloaded file is empty: %url%; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:place
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
move /y "%cached%.part" "%cached%" >nul 2>nul
|
||||
if not exist "%cached%" goto fail
|
||||
if errorlevel 1 goto place_failed
|
||||
call :check_download "%cached%"
|
||||
if errorlevel 1 exit /b 127
|
||||
set "run=%cached%"
|
||||
goto run
|
||||
|
||||
:place_failed
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not cache the verified download: %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:run
|
||||
"%run%" %*
|
||||
exit /b
|
||||
|
||||
@@ -7834,7 +7834,6 @@
|
||||
if (editBadgeEl && editBadgeEl.style.display !== 'none') renderEditBadge('idle-disabled');
|
||||
showBar('generating');
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -7916,7 +7915,6 @@
|
||||
showBar('generating');
|
||||
startScrollTracking();
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -8238,7 +8236,8 @@
|
||||
// rasterization from delaying the fetch itself.
|
||||
if (!hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
await sendEvent(basePayload);
|
||||
const created = await sendEvent(basePayload);
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
|
||||
let screenshotPath;
|
||||
@@ -8279,7 +8278,10 @@
|
||||
// is semantic input. Plain requests were already dispatched above.
|
||||
if (hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
const created = await sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
// Capture/upload can take seconds. Progress before this acknowledgment
|
||||
// refers to an unknown session and would clear our own active work.
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,8 @@ Choose the mode from the requested surface, not the product, and persist it only
|
||||
Routing:
|
||||
|
||||
- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command.
|
||||
- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Explicit or clearly implied request to run a command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Workflow or command-selection question:** read [Workflow questions](reference/routing.md#workflow-questions).
|
||||
- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes a new surface or replacement world through init, then new-work; a narrow refinement of existing code proceeds on the incumbent implementation as `impeccable context` directs, offering init afterward rather than blocking on it.
|
||||
- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions.
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ const __impeccableLiveDev =
|
||||
process.env.NODE_ENV === "development" ? ["http://localhost:8400"] : [];
|
||||
```
|
||||
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: `tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts`, `tests/framework-fixtures/sveltekit-csp/expected-after-patch.js`. Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: [nextjs-turborepo/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts), [sveltekit-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/sveltekit-csp/expected-after-patch.js). Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
|
||||
### append-string
|
||||
|
||||
@@ -93,7 +93,7 @@ const __impeccableLiveDev =
|
||||
- `script-src 'self' 'unsafe-inline'` becomes `` `script-src 'self' 'unsafe-inline'${__impeccableLiveDev}` ``
|
||||
- `connect-src 'self'` becomes `` `connect-src 'self'${__impeccableLiveDev}` ``
|
||||
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: `tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js`, `tests/framework-fixtures/nuxt-csp/expected-after-patch.ts`.
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: [nextjs-inline-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js), [nuxt-csp/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nuxt-csp/expected-after-patch.ts).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ Complete HTML replacement of the original element per variant, not a CSS-only pa
|
||||
|
||||
Replace the style opening tag with `cssAuthoring.styleTag` when the tool returns a different one. **Each variant div contains exactly one top-level element**, same tag as the original; loose siblings break outline tracking and accept. First variant visible, all others `display: none`. The browser's MutationObserver accepts atomic or progressive arrival; accepting an arrived variant fences the worker, so later publications are rejected.
|
||||
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in `tests/live-e2e/agent.mjs` is a faithful template.
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in the [repo agent template](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/live-e2e/agent.mjs) is a faithful template.
|
||||
|
||||
**JSX / TSX targets:** wrap `<style>` content in a template literal (CSS braces would parse as JSX), use `className=` / `style={{…}}`, keep `data-impeccable-*` attributes as plain strings:
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
# No-argument routing: the context-aware menu
|
||||
# Command guidance
|
||||
|
||||
## Workflow questions
|
||||
|
||||
Give advice without executing commands; the menu below is only for bare invocations. Consult relevant command references as needed for prerequisites and scope. Link to the [docs](https://impeccable.style/docs/) for the broader workflow guide. If the user also requests execution, follow that request.
|
||||
|
||||
## No-argument routing: the context-aware menu
|
||||
|
||||
Read this when the user invokes `$impeccable` with no argument. They are asking "what should I do?" Make the menu context-aware instead of static.
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.0
|
||||
0.1.1
|
||||
|
||||
@@ -96,6 +96,19 @@ fetch_url() {
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
check_download() {
|
||||
download_file=${1:-$tmp}
|
||||
if [ ! -f "$download_file" ]; then
|
||||
rm -f "$tmp.sha256"
|
||||
echo "impeccable: download completed but the file was removed before execution: $url; check your antivirus quarantine or logs. Refusing to continue; do not disable protection." >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ ! -s "$download_file" ]; then
|
||||
rm -f "$download_file" "$tmp.sha256"
|
||||
echo "impeccable: downloaded file is empty: $url; refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
}
|
||||
if [ -n "$probing" ]; then
|
||||
# Inside another launcher's probe: no download, fail fast and quiet.
|
||||
exit 127
|
||||
@@ -116,6 +129,7 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
fetch_url "$url" && fetched=1
|
||||
fi
|
||||
if [ "$fetched" = 1 ]; then
|
||||
check_download
|
||||
# Fail closed: a freshly downloaded binary runs only after verifying
|
||||
# against its .sha256 sidecar. A sidecar that cannot be fetched, or a
|
||||
# machine with no sha256 tool, refuses the download instead of exec'ing
|
||||
@@ -127,15 +141,20 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -q -O "$tmp.sha256" "$url.sha256" 2>/dev/null && sidecar_ok=1
|
||||
fi
|
||||
check_download
|
||||
expected=""
|
||||
[ "$sidecar_ok" = 1 ] && expected=$(cut -d' ' -f1 < "$tmp.sha256")
|
||||
actual=""
|
||||
if command -v shasum >/dev/null 2>&1; then actual=$(shasum -a 256 "$tmp" | cut -d' ' -f1)
|
||||
elif command -v sha256sum >/dev/null 2>&1; then actual=$(sha256sum "$tmp" | cut -d' ' -f1); fi
|
||||
if command -v shasum >/dev/null 2>&1; then
|
||||
if digest=$(shasum -a 256 "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
elif command -v sha256sum >/dev/null 2>&1; then
|
||||
if digest=$(sha256sum "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
fi
|
||||
check_download
|
||||
rm -f "$tmp.sha256"
|
||||
if [ -z "$expected" ] || [ -z "$actual" ]; then
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or no sha256 tool); refusing the unverified download" >&2
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or hashing failed); refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ "$actual" != "$expected" ]; then
|
||||
@@ -143,8 +162,22 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
echo "impeccable: checksum mismatch downloading $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
chmod +x "$tmp" 2>/dev/null
|
||||
mv -f "$tmp" "$cached" && exec "$cached" "$@"
|
||||
check_download
|
||||
if ! chmod +x "$tmp" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not make the verified download executable: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download
|
||||
if ! mv -f "$tmp" "$cached" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not cache the verified download: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download "$cached"
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
fi
|
||||
|
||||
@@ -82,6 +82,8 @@ curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
rem Mirrors the sh launcher and fails closed: a freshly downloaded binary
|
||||
rem runs only after verifying against its .sha256 sidecar. A sidecar that
|
||||
rem cannot be fetched, or an empty certutil result, refuses the download
|
||||
@@ -91,8 +93,16 @@ if errorlevel 1 goto verify_refuse
|
||||
set "expected="
|
||||
set /p expected=<"%cached%.sha256"
|
||||
for /f "tokens=1" %%h in ("%expected%") do set "expected=%%h"
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
set "actual="
|
||||
for /f "skip=1 delims=" %%h in ('certutil -hashfile "%cached%.part" SHA256 2^>nul') do if not defined actual set "actual=%%h"
|
||||
rem Reuse the sidecar staging file after reading expected. Check certutil's
|
||||
rem status before parsing: its error text on stdout is not a digest.
|
||||
certutil -hashfile "%cached%.part" SHA256 >"%cached%.sha256" 2>nul
|
||||
if errorlevel 1 goto verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
for /f "usebackq skip=1 delims=" %%h in ("%cached%.sha256") do if not defined actual set "actual=%%h"
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
if not defined expected goto verify_refuse
|
||||
if not defined actual goto verify_refuse
|
||||
@@ -103,17 +113,48 @@ echo impeccable: checksum mismatch downloading %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: cannot verify %url% against %url%.sha256; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:check_download
|
||||
set "download_file=%~1"
|
||||
if not defined download_file set "download_file=%cached%.part"
|
||||
if not exist "%download_file%" goto download_missing
|
||||
for %%f in ("%download_file%") do if %%~zf==0 goto download_empty
|
||||
exit /b 0
|
||||
|
||||
:download_missing
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: download completed but the file was removed before execution: %url%; check your antivirus quarantine or logs. Refusing to continue; do not disable protection. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:download_empty
|
||||
del "%download_file%" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: downloaded file is empty: %url%; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:place
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
move /y "%cached%.part" "%cached%" >nul 2>nul
|
||||
if not exist "%cached%" goto fail
|
||||
if errorlevel 1 goto place_failed
|
||||
call :check_download "%cached%"
|
||||
if errorlevel 1 exit /b 127
|
||||
set "run=%cached%"
|
||||
goto run
|
||||
|
||||
:place_failed
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not cache the verified download: %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:run
|
||||
"%run%" %*
|
||||
exit /b
|
||||
|
||||
@@ -7834,7 +7834,6 @@
|
||||
if (editBadgeEl && editBadgeEl.style.display !== 'none') renderEditBadge('idle-disabled');
|
||||
showBar('generating');
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -7916,7 +7915,6 @@
|
||||
showBar('generating');
|
||||
startScrollTracking();
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -8238,7 +8236,8 @@
|
||||
// rasterization from delaying the fetch itself.
|
||||
if (!hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
await sendEvent(basePayload);
|
||||
const created = await sendEvent(basePayload);
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
|
||||
let screenshotPath;
|
||||
@@ -8279,7 +8278,10 @@
|
||||
// is semantic input. Plain requests were already dispatched above.
|
||||
if (hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
const created = await sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
// Capture/upload can take seconds. Progress before this acknowledgment
|
||||
// refers to an unknown session and would clear our own active work.
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,6 @@ version: 4.2.0
|
||||
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
|
||||
allowed-tools:
|
||||
- Bash(npx impeccable *)
|
||||
- Bash(.claude/skills/impeccable/scripts/impeccable *)
|
||||
---
|
||||
|
||||
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.
|
||||
@@ -71,7 +68,8 @@ Choose the mode from the requested surface, not the product, and persist it only
|
||||
Routing:
|
||||
|
||||
- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command.
|
||||
- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Explicit or clearly implied request to run a command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Workflow or command-selection question:** read [Workflow questions](reference/routing.md#workflow-questions).
|
||||
- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes a new surface or replacement world through init, then new-work; a narrow refinement of existing code proceeds on the incumbent implementation as `impeccable context` directs, offering init afterward rather than blocking on it.
|
||||
- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions.
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ const __impeccableLiveDev =
|
||||
process.env.NODE_ENV === "development" ? ["http://localhost:8400"] : [];
|
||||
```
|
||||
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: `tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts`, `tests/framework-fixtures/sveltekit-csp/expected-after-patch.js`. Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: [nextjs-turborepo/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts), [sveltekit-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/sveltekit-csp/expected-after-patch.js). Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
|
||||
### append-string
|
||||
|
||||
@@ -93,7 +93,7 @@ const __impeccableLiveDev =
|
||||
- `script-src 'self' 'unsafe-inline'` becomes `` `script-src 'self' 'unsafe-inline'${__impeccableLiveDev}` ``
|
||||
- `connect-src 'self'` becomes `` `connect-src 'self'${__impeccableLiveDev}` ``
|
||||
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: `tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js`, `tests/framework-fixtures/nuxt-csp/expected-after-patch.ts`.
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: [nextjs-inline-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js), [nuxt-csp/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nuxt-csp/expected-after-patch.ts).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ Complete HTML replacement of the original element per variant, not a CSS-only pa
|
||||
|
||||
Replace the style opening tag with `cssAuthoring.styleTag` when the tool returns a different one. **Each variant div contains exactly one top-level element**, same tag as the original; loose siblings break outline tracking and accept. First variant visible, all others `display: none`. The browser's MutationObserver accepts atomic or progressive arrival; accepting an arrived variant fences the worker, so later publications are rejected.
|
||||
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in `tests/live-e2e/agent.mjs` is a faithful template.
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in the [repo agent template](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/live-e2e/agent.mjs) is a faithful template.
|
||||
|
||||
**JSX / TSX targets:** wrap `<style>` content in a template literal (CSS braces would parse as JSX), use `className=` / `style={{…}}`, keep `data-impeccable-*` attributes as plain strings:
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
# No-argument routing: the context-aware menu
|
||||
# Command guidance
|
||||
|
||||
## Workflow questions
|
||||
|
||||
Give advice without executing commands; the menu below is only for bare invocations. Consult relevant command references as needed for prerequisites and scope. Link to the [docs](https://impeccable.style/docs/) for the broader workflow guide. If the user also requests execution, follow that request.
|
||||
|
||||
## No-argument routing: the context-aware menu
|
||||
|
||||
Read this when the user invokes `/impeccable` with no argument. They are asking "what should I do?" Make the menu context-aware instead of static.
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.0
|
||||
0.1.1
|
||||
|
||||
@@ -96,6 +96,19 @@ fetch_url() {
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
check_download() {
|
||||
download_file=${1:-$tmp}
|
||||
if [ ! -f "$download_file" ]; then
|
||||
rm -f "$tmp.sha256"
|
||||
echo "impeccable: download completed but the file was removed before execution: $url; check your antivirus quarantine or logs. Refusing to continue; do not disable protection." >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ ! -s "$download_file" ]; then
|
||||
rm -f "$download_file" "$tmp.sha256"
|
||||
echo "impeccable: downloaded file is empty: $url; refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
}
|
||||
if [ -n "$probing" ]; then
|
||||
# Inside another launcher's probe: no download, fail fast and quiet.
|
||||
exit 127
|
||||
@@ -116,6 +129,7 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
fetch_url "$url" && fetched=1
|
||||
fi
|
||||
if [ "$fetched" = 1 ]; then
|
||||
check_download
|
||||
# Fail closed: a freshly downloaded binary runs only after verifying
|
||||
# against its .sha256 sidecar. A sidecar that cannot be fetched, or a
|
||||
# machine with no sha256 tool, refuses the download instead of exec'ing
|
||||
@@ -127,15 +141,20 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -q -O "$tmp.sha256" "$url.sha256" 2>/dev/null && sidecar_ok=1
|
||||
fi
|
||||
check_download
|
||||
expected=""
|
||||
[ "$sidecar_ok" = 1 ] && expected=$(cut -d' ' -f1 < "$tmp.sha256")
|
||||
actual=""
|
||||
if command -v shasum >/dev/null 2>&1; then actual=$(shasum -a 256 "$tmp" | cut -d' ' -f1)
|
||||
elif command -v sha256sum >/dev/null 2>&1; then actual=$(sha256sum "$tmp" | cut -d' ' -f1); fi
|
||||
if command -v shasum >/dev/null 2>&1; then
|
||||
if digest=$(shasum -a 256 "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
elif command -v sha256sum >/dev/null 2>&1; then
|
||||
if digest=$(sha256sum "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
fi
|
||||
check_download
|
||||
rm -f "$tmp.sha256"
|
||||
if [ -z "$expected" ] || [ -z "$actual" ]; then
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or no sha256 tool); refusing the unverified download" >&2
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or hashing failed); refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ "$actual" != "$expected" ]; then
|
||||
@@ -143,8 +162,22 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
echo "impeccable: checksum mismatch downloading $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
chmod +x "$tmp" 2>/dev/null
|
||||
mv -f "$tmp" "$cached" && exec "$cached" "$@"
|
||||
check_download
|
||||
if ! chmod +x "$tmp" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not make the verified download executable: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download
|
||||
if ! mv -f "$tmp" "$cached" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not cache the verified download: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download "$cached"
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
fi
|
||||
|
||||
@@ -82,6 +82,8 @@ curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
rem Mirrors the sh launcher and fails closed: a freshly downloaded binary
|
||||
rem runs only after verifying against its .sha256 sidecar. A sidecar that
|
||||
rem cannot be fetched, or an empty certutil result, refuses the download
|
||||
@@ -91,8 +93,16 @@ if errorlevel 1 goto verify_refuse
|
||||
set "expected="
|
||||
set /p expected=<"%cached%.sha256"
|
||||
for /f "tokens=1" %%h in ("%expected%") do set "expected=%%h"
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
set "actual="
|
||||
for /f "skip=1 delims=" %%h in ('certutil -hashfile "%cached%.part" SHA256 2^>nul') do if not defined actual set "actual=%%h"
|
||||
rem Reuse the sidecar staging file after reading expected. Check certutil's
|
||||
rem status before parsing: its error text on stdout is not a digest.
|
||||
certutil -hashfile "%cached%.part" SHA256 >"%cached%.sha256" 2>nul
|
||||
if errorlevel 1 goto verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
for /f "usebackq skip=1 delims=" %%h in ("%cached%.sha256") do if not defined actual set "actual=%%h"
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
if not defined expected goto verify_refuse
|
||||
if not defined actual goto verify_refuse
|
||||
@@ -103,17 +113,48 @@ echo impeccable: checksum mismatch downloading %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: cannot verify %url% against %url%.sha256; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:check_download
|
||||
set "download_file=%~1"
|
||||
if not defined download_file set "download_file=%cached%.part"
|
||||
if not exist "%download_file%" goto download_missing
|
||||
for %%f in ("%download_file%") do if %%~zf==0 goto download_empty
|
||||
exit /b 0
|
||||
|
||||
:download_missing
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: download completed but the file was removed before execution: %url%; check your antivirus quarantine or logs. Refusing to continue; do not disable protection. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:download_empty
|
||||
del "%download_file%" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: downloaded file is empty: %url%; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:place
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
move /y "%cached%.part" "%cached%" >nul 2>nul
|
||||
if not exist "%cached%" goto fail
|
||||
if errorlevel 1 goto place_failed
|
||||
call :check_download "%cached%"
|
||||
if errorlevel 1 exit /b 127
|
||||
set "run=%cached%"
|
||||
goto run
|
||||
|
||||
:place_failed
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not cache the verified download: %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:run
|
||||
"%run%" %*
|
||||
exit /b
|
||||
|
||||
@@ -7834,7 +7834,6 @@
|
||||
if (editBadgeEl && editBadgeEl.style.display !== 'none') renderEditBadge('idle-disabled');
|
||||
showBar('generating');
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -7916,7 +7915,6 @@
|
||||
showBar('generating');
|
||||
startScrollTracking();
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -8238,7 +8236,8 @@
|
||||
// rasterization from delaying the fetch itself.
|
||||
if (!hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
await sendEvent(basePayload);
|
||||
const created = await sendEvent(basePayload);
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
|
||||
let screenshotPath;
|
||||
@@ -8279,7 +8278,10 @@
|
||||
// is semantic input. Plain requests were already dispatched above.
|
||||
if (hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
const created = await sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
// Capture/upload can take seconds. Progress before this acknowledgment
|
||||
// refers to an unknown session and would clear our own active work.
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,8 @@ Choose the mode from the requested surface, not the product, and persist it only
|
||||
Routing:
|
||||
|
||||
- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command.
|
||||
- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Explicit or clearly implied request to run a command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Workflow or command-selection question:** read [Workflow questions](reference/routing.md#workflow-questions).
|
||||
- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes a new surface or replacement world through init, then new-work; a narrow refinement of existing code proceeds on the incumbent implementation as `impeccable context` directs, offering init afterward rather than blocking on it.
|
||||
- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions.
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ const __impeccableLiveDev =
|
||||
process.env.NODE_ENV === "development" ? ["http://localhost:8400"] : [];
|
||||
```
|
||||
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: `tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts`, `tests/framework-fixtures/sveltekit-csp/expected-after-patch.js`. Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: [nextjs-turborepo/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts), [sveltekit-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/sveltekit-csp/expected-after-patch.js). Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
|
||||
### append-string
|
||||
|
||||
@@ -93,7 +93,7 @@ const __impeccableLiveDev =
|
||||
- `script-src 'self' 'unsafe-inline'` becomes `` `script-src 'self' 'unsafe-inline'${__impeccableLiveDev}` ``
|
||||
- `connect-src 'self'` becomes `` `connect-src 'self'${__impeccableLiveDev}` ``
|
||||
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: `tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js`, `tests/framework-fixtures/nuxt-csp/expected-after-patch.ts`.
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: [nextjs-inline-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js), [nuxt-csp/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nuxt-csp/expected-after-patch.ts).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ Complete HTML replacement of the original element per variant, not a CSS-only pa
|
||||
|
||||
Replace the style opening tag with `cssAuthoring.styleTag` when the tool returns a different one. **Each variant div contains exactly one top-level element**, same tag as the original; loose siblings break outline tracking and accept. First variant visible, all others `display: none`. The browser's MutationObserver accepts atomic or progressive arrival; accepting an arrived variant fences the worker, so later publications are rejected.
|
||||
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in `tests/live-e2e/agent.mjs` is a faithful template.
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in the [repo agent template](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/live-e2e/agent.mjs) is a faithful template.
|
||||
|
||||
**JSX / TSX targets:** wrap `<style>` content in a template literal (CSS braces would parse as JSX), use `className=` / `style={{…}}`, keep `data-impeccable-*` attributes as plain strings:
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
# No-argument routing: the context-aware menu
|
||||
# Command guidance
|
||||
|
||||
## Workflow questions
|
||||
|
||||
Give advice without executing commands; the menu below is only for bare invocations. Consult relevant command references as needed for prerequisites and scope. Link to the [docs](https://impeccable.style/docs/) for the broader workflow guide. If the user also requests execution, follow that request.
|
||||
|
||||
## No-argument routing: the context-aware menu
|
||||
|
||||
Read this when the user invokes `/impeccable` with no argument. They are asking "what should I do?" Make the menu context-aware instead of static.
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.0
|
||||
0.1.1
|
||||
|
||||
@@ -96,6 +96,19 @@ fetch_url() {
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
check_download() {
|
||||
download_file=${1:-$tmp}
|
||||
if [ ! -f "$download_file" ]; then
|
||||
rm -f "$tmp.sha256"
|
||||
echo "impeccable: download completed but the file was removed before execution: $url; check your antivirus quarantine or logs. Refusing to continue; do not disable protection." >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ ! -s "$download_file" ]; then
|
||||
rm -f "$download_file" "$tmp.sha256"
|
||||
echo "impeccable: downloaded file is empty: $url; refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
}
|
||||
if [ -n "$probing" ]; then
|
||||
# Inside another launcher's probe: no download, fail fast and quiet.
|
||||
exit 127
|
||||
@@ -116,6 +129,7 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
fetch_url "$url" && fetched=1
|
||||
fi
|
||||
if [ "$fetched" = 1 ]; then
|
||||
check_download
|
||||
# Fail closed: a freshly downloaded binary runs only after verifying
|
||||
# against its .sha256 sidecar. A sidecar that cannot be fetched, or a
|
||||
# machine with no sha256 tool, refuses the download instead of exec'ing
|
||||
@@ -127,15 +141,20 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -q -O "$tmp.sha256" "$url.sha256" 2>/dev/null && sidecar_ok=1
|
||||
fi
|
||||
check_download
|
||||
expected=""
|
||||
[ "$sidecar_ok" = 1 ] && expected=$(cut -d' ' -f1 < "$tmp.sha256")
|
||||
actual=""
|
||||
if command -v shasum >/dev/null 2>&1; then actual=$(shasum -a 256 "$tmp" | cut -d' ' -f1)
|
||||
elif command -v sha256sum >/dev/null 2>&1; then actual=$(sha256sum "$tmp" | cut -d' ' -f1); fi
|
||||
if command -v shasum >/dev/null 2>&1; then
|
||||
if digest=$(shasum -a 256 "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
elif command -v sha256sum >/dev/null 2>&1; then
|
||||
if digest=$(sha256sum "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
fi
|
||||
check_download
|
||||
rm -f "$tmp.sha256"
|
||||
if [ -z "$expected" ] || [ -z "$actual" ]; then
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or no sha256 tool); refusing the unverified download" >&2
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or hashing failed); refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ "$actual" != "$expected" ]; then
|
||||
@@ -143,8 +162,22 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
echo "impeccable: checksum mismatch downloading $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
chmod +x "$tmp" 2>/dev/null
|
||||
mv -f "$tmp" "$cached" && exec "$cached" "$@"
|
||||
check_download
|
||||
if ! chmod +x "$tmp" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not make the verified download executable: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download
|
||||
if ! mv -f "$tmp" "$cached" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not cache the verified download: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download "$cached"
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
fi
|
||||
|
||||
@@ -82,6 +82,8 @@ curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
rem Mirrors the sh launcher and fails closed: a freshly downloaded binary
|
||||
rem runs only after verifying against its .sha256 sidecar. A sidecar that
|
||||
rem cannot be fetched, or an empty certutil result, refuses the download
|
||||
@@ -91,8 +93,16 @@ if errorlevel 1 goto verify_refuse
|
||||
set "expected="
|
||||
set /p expected=<"%cached%.sha256"
|
||||
for /f "tokens=1" %%h in ("%expected%") do set "expected=%%h"
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
set "actual="
|
||||
for /f "skip=1 delims=" %%h in ('certutil -hashfile "%cached%.part" SHA256 2^>nul') do if not defined actual set "actual=%%h"
|
||||
rem Reuse the sidecar staging file after reading expected. Check certutil's
|
||||
rem status before parsing: its error text on stdout is not a digest.
|
||||
certutil -hashfile "%cached%.part" SHA256 >"%cached%.sha256" 2>nul
|
||||
if errorlevel 1 goto verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
for /f "usebackq skip=1 delims=" %%h in ("%cached%.sha256") do if not defined actual set "actual=%%h"
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
if not defined expected goto verify_refuse
|
||||
if not defined actual goto verify_refuse
|
||||
@@ -103,17 +113,48 @@ echo impeccable: checksum mismatch downloading %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: cannot verify %url% against %url%.sha256; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:check_download
|
||||
set "download_file=%~1"
|
||||
if not defined download_file set "download_file=%cached%.part"
|
||||
if not exist "%download_file%" goto download_missing
|
||||
for %%f in ("%download_file%") do if %%~zf==0 goto download_empty
|
||||
exit /b 0
|
||||
|
||||
:download_missing
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: download completed but the file was removed before execution: %url%; check your antivirus quarantine or logs. Refusing to continue; do not disable protection. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:download_empty
|
||||
del "%download_file%" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: downloaded file is empty: %url%; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:place
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
move /y "%cached%.part" "%cached%" >nul 2>nul
|
||||
if not exist "%cached%" goto fail
|
||||
if errorlevel 1 goto place_failed
|
||||
call :check_download "%cached%"
|
||||
if errorlevel 1 exit /b 127
|
||||
set "run=%cached%"
|
||||
goto run
|
||||
|
||||
:place_failed
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not cache the verified download: %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:run
|
||||
"%run%" %*
|
||||
exit /b
|
||||
|
||||
@@ -7834,7 +7834,6 @@
|
||||
if (editBadgeEl && editBadgeEl.style.display !== 'none') renderEditBadge('idle-disabled');
|
||||
showBar('generating');
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -7916,7 +7915,6 @@
|
||||
showBar('generating');
|
||||
startScrollTracking();
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -8238,7 +8236,8 @@
|
||||
// rasterization from delaying the fetch itself.
|
||||
if (!hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
await sendEvent(basePayload);
|
||||
const created = await sendEvent(basePayload);
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
|
||||
let screenshotPath;
|
||||
@@ -8279,7 +8278,10 @@
|
||||
// is semantic input. Plain requests were already dispatched above.
|
||||
if (hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
const created = await sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
// Capture/upload can take seconds. Progress before this acknowledgment
|
||||
// refers to an unknown session and would clear our own active work.
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,8 @@ Choose the mode from the requested surface, not the product, and persist it only
|
||||
Routing:
|
||||
|
||||
- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command.
|
||||
- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Explicit or clearly implied request to run a command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Workflow or command-selection question:** read [Workflow questions](reference/routing.md#workflow-questions).
|
||||
- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes a new surface or replacement world through init, then new-work; a narrow refinement of existing code proceeds on the incumbent implementation as `impeccable context` directs, offering init afterward rather than blocking on it.
|
||||
- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions.
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ const __impeccableLiveDev =
|
||||
process.env.NODE_ENV === "development" ? ["http://localhost:8400"] : [];
|
||||
```
|
||||
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: `tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts`, `tests/framework-fixtures/sveltekit-csp/expected-after-patch.js`. Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: [nextjs-turborepo/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts), [sveltekit-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/sveltekit-csp/expected-after-patch.js). Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
|
||||
### append-string
|
||||
|
||||
@@ -93,7 +93,7 @@ const __impeccableLiveDev =
|
||||
- `script-src 'self' 'unsafe-inline'` becomes `` `script-src 'self' 'unsafe-inline'${__impeccableLiveDev}` ``
|
||||
- `connect-src 'self'` becomes `` `connect-src 'self'${__impeccableLiveDev}` ``
|
||||
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: `tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js`, `tests/framework-fixtures/nuxt-csp/expected-after-patch.ts`.
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: [nextjs-inline-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js), [nuxt-csp/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nuxt-csp/expected-after-patch.ts).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ Complete HTML replacement of the original element per variant, not a CSS-only pa
|
||||
|
||||
Replace the style opening tag with `cssAuthoring.styleTag` when the tool returns a different one. **Each variant div contains exactly one top-level element**, same tag as the original; loose siblings break outline tracking and accept. First variant visible, all others `display: none`. The browser's MutationObserver accepts atomic or progressive arrival; accepting an arrived variant fences the worker, so later publications are rejected.
|
||||
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in `tests/live-e2e/agent.mjs` is a faithful template.
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in the [repo agent template](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/live-e2e/agent.mjs) is a faithful template.
|
||||
|
||||
**JSX / TSX targets:** wrap `<style>` content in a template literal (CSS braces would parse as JSX), use `className=` / `style={{…}}`, keep `data-impeccable-*` attributes as plain strings:
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
# No-argument routing: the context-aware menu
|
||||
# Command guidance
|
||||
|
||||
## Workflow questions
|
||||
|
||||
Give advice without executing commands; the menu below is only for bare invocations. Consult relevant command references as needed for prerequisites and scope. Link to the [docs](https://impeccable.style/docs/) for the broader workflow guide. If the user also requests execution, follow that request.
|
||||
|
||||
## No-argument routing: the context-aware menu
|
||||
|
||||
Read this when the user invokes `/impeccable` with no argument. They are asking "what should I do?" Make the menu context-aware instead of static.
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.0
|
||||
0.1.1
|
||||
|
||||
@@ -96,6 +96,19 @@ fetch_url() {
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
check_download() {
|
||||
download_file=${1:-$tmp}
|
||||
if [ ! -f "$download_file" ]; then
|
||||
rm -f "$tmp.sha256"
|
||||
echo "impeccable: download completed but the file was removed before execution: $url; check your antivirus quarantine or logs. Refusing to continue; do not disable protection." >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ ! -s "$download_file" ]; then
|
||||
rm -f "$download_file" "$tmp.sha256"
|
||||
echo "impeccable: downloaded file is empty: $url; refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
}
|
||||
if [ -n "$probing" ]; then
|
||||
# Inside another launcher's probe: no download, fail fast and quiet.
|
||||
exit 127
|
||||
@@ -116,6 +129,7 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
fetch_url "$url" && fetched=1
|
||||
fi
|
||||
if [ "$fetched" = 1 ]; then
|
||||
check_download
|
||||
# Fail closed: a freshly downloaded binary runs only after verifying
|
||||
# against its .sha256 sidecar. A sidecar that cannot be fetched, or a
|
||||
# machine with no sha256 tool, refuses the download instead of exec'ing
|
||||
@@ -127,15 +141,20 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -q -O "$tmp.sha256" "$url.sha256" 2>/dev/null && sidecar_ok=1
|
||||
fi
|
||||
check_download
|
||||
expected=""
|
||||
[ "$sidecar_ok" = 1 ] && expected=$(cut -d' ' -f1 < "$tmp.sha256")
|
||||
actual=""
|
||||
if command -v shasum >/dev/null 2>&1; then actual=$(shasum -a 256 "$tmp" | cut -d' ' -f1)
|
||||
elif command -v sha256sum >/dev/null 2>&1; then actual=$(sha256sum "$tmp" | cut -d' ' -f1); fi
|
||||
if command -v shasum >/dev/null 2>&1; then
|
||||
if digest=$(shasum -a 256 "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
elif command -v sha256sum >/dev/null 2>&1; then
|
||||
if digest=$(sha256sum "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
fi
|
||||
check_download
|
||||
rm -f "$tmp.sha256"
|
||||
if [ -z "$expected" ] || [ -z "$actual" ]; then
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or no sha256 tool); refusing the unverified download" >&2
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or hashing failed); refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ "$actual" != "$expected" ]; then
|
||||
@@ -143,8 +162,22 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
echo "impeccable: checksum mismatch downloading $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
chmod +x "$tmp" 2>/dev/null
|
||||
mv -f "$tmp" "$cached" && exec "$cached" "$@"
|
||||
check_download
|
||||
if ! chmod +x "$tmp" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not make the verified download executable: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download
|
||||
if ! mv -f "$tmp" "$cached" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not cache the verified download: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download "$cached"
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
fi
|
||||
|
||||
@@ -82,6 +82,8 @@ curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
rem Mirrors the sh launcher and fails closed: a freshly downloaded binary
|
||||
rem runs only after verifying against its .sha256 sidecar. A sidecar that
|
||||
rem cannot be fetched, or an empty certutil result, refuses the download
|
||||
@@ -91,8 +93,16 @@ if errorlevel 1 goto verify_refuse
|
||||
set "expected="
|
||||
set /p expected=<"%cached%.sha256"
|
||||
for /f "tokens=1" %%h in ("%expected%") do set "expected=%%h"
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
set "actual="
|
||||
for /f "skip=1 delims=" %%h in ('certutil -hashfile "%cached%.part" SHA256 2^>nul') do if not defined actual set "actual=%%h"
|
||||
rem Reuse the sidecar staging file after reading expected. Check certutil's
|
||||
rem status before parsing: its error text on stdout is not a digest.
|
||||
certutil -hashfile "%cached%.part" SHA256 >"%cached%.sha256" 2>nul
|
||||
if errorlevel 1 goto verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
for /f "usebackq skip=1 delims=" %%h in ("%cached%.sha256") do if not defined actual set "actual=%%h"
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
if not defined expected goto verify_refuse
|
||||
if not defined actual goto verify_refuse
|
||||
@@ -103,17 +113,48 @@ echo impeccable: checksum mismatch downloading %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: cannot verify %url% against %url%.sha256; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:check_download
|
||||
set "download_file=%~1"
|
||||
if not defined download_file set "download_file=%cached%.part"
|
||||
if not exist "%download_file%" goto download_missing
|
||||
for %%f in ("%download_file%") do if %%~zf==0 goto download_empty
|
||||
exit /b 0
|
||||
|
||||
:download_missing
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: download completed but the file was removed before execution: %url%; check your antivirus quarantine or logs. Refusing to continue; do not disable protection. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:download_empty
|
||||
del "%download_file%" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: downloaded file is empty: %url%; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:place
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
move /y "%cached%.part" "%cached%" >nul 2>nul
|
||||
if not exist "%cached%" goto fail
|
||||
if errorlevel 1 goto place_failed
|
||||
call :check_download "%cached%"
|
||||
if errorlevel 1 exit /b 127
|
||||
set "run=%cached%"
|
||||
goto run
|
||||
|
||||
:place_failed
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not cache the verified download: %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:run
|
||||
"%run%" %*
|
||||
exit /b
|
||||
|
||||
@@ -7834,7 +7834,6 @@
|
||||
if (editBadgeEl && editBadgeEl.style.display !== 'none') renderEditBadge('idle-disabled');
|
||||
showBar('generating');
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -7916,7 +7915,6 @@
|
||||
showBar('generating');
|
||||
startScrollTracking();
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -8238,7 +8236,8 @@
|
||||
// rasterization from delaying the fetch itself.
|
||||
if (!hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
await sendEvent(basePayload);
|
||||
const created = await sendEvent(basePayload);
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
|
||||
let screenshotPath;
|
||||
@@ -8279,7 +8278,10 @@
|
||||
// is semantic input. Plain requests were already dispatched above.
|
||||
if (hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
const created = await sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
// Capture/upload can take seconds. Progress before this acknowledgment
|
||||
// refers to an unknown session and would clear our own active work.
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,8 @@ Choose the mode from the requested surface, not the product, and persist it only
|
||||
Routing:
|
||||
|
||||
- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command.
|
||||
- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Explicit or clearly implied request to run a command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Workflow or command-selection question:** read [Workflow questions](reference/routing.md#workflow-questions).
|
||||
- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes a new surface or replacement world through init, then new-work; a narrow refinement of existing code proceeds on the incumbent implementation as `impeccable context` directs, offering init afterward rather than blocking on it.
|
||||
- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions.
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ const __impeccableLiveDev =
|
||||
process.env.NODE_ENV === "development" ? ["http://localhost:8400"] : [];
|
||||
```
|
||||
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: `tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts`, `tests/framework-fixtures/sveltekit-csp/expected-after-patch.js`. Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: [nextjs-turborepo/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts), [sveltekit-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/sveltekit-csp/expected-after-patch.js). Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
|
||||
### append-string
|
||||
|
||||
@@ -93,7 +93,7 @@ const __impeccableLiveDev =
|
||||
- `script-src 'self' 'unsafe-inline'` becomes `` `script-src 'self' 'unsafe-inline'${__impeccableLiveDev}` ``
|
||||
- `connect-src 'self'` becomes `` `connect-src 'self'${__impeccableLiveDev}` ``
|
||||
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: `tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js`, `tests/framework-fixtures/nuxt-csp/expected-after-patch.ts`.
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: [nextjs-inline-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js), [nuxt-csp/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nuxt-csp/expected-after-patch.ts).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ Complete HTML replacement of the original element per variant, not a CSS-only pa
|
||||
|
||||
Replace the style opening tag with `cssAuthoring.styleTag` when the tool returns a different one. **Each variant div contains exactly one top-level element**, same tag as the original; loose siblings break outline tracking and accept. First variant visible, all others `display: none`. The browser's MutationObserver accepts atomic or progressive arrival; accepting an arrived variant fences the worker, so later publications are rejected.
|
||||
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in `tests/live-e2e/agent.mjs` is a faithful template.
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in the [repo agent template](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/live-e2e/agent.mjs) is a faithful template.
|
||||
|
||||
**JSX / TSX targets:** wrap `<style>` content in a template literal (CSS braces would parse as JSX), use `className=` / `style={{…}}`, keep `data-impeccable-*` attributes as plain strings:
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
# No-argument routing: the context-aware menu
|
||||
# Command guidance
|
||||
|
||||
## Workflow questions
|
||||
|
||||
Give advice without executing commands; the menu below is only for bare invocations. Consult relevant command references as needed for prerequisites and scope. Link to the [docs](https://impeccable.style/docs/) for the broader workflow guide. If the user also requests execution, follow that request.
|
||||
|
||||
## No-argument routing: the context-aware menu
|
||||
|
||||
Read this when the user invokes `/impeccable` with no argument. They are asking "what should I do?" Make the menu context-aware instead of static.
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.0
|
||||
0.1.1
|
||||
|
||||
@@ -96,6 +96,19 @@ fetch_url() {
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
check_download() {
|
||||
download_file=${1:-$tmp}
|
||||
if [ ! -f "$download_file" ]; then
|
||||
rm -f "$tmp.sha256"
|
||||
echo "impeccable: download completed but the file was removed before execution: $url; check your antivirus quarantine or logs. Refusing to continue; do not disable protection." >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ ! -s "$download_file" ]; then
|
||||
rm -f "$download_file" "$tmp.sha256"
|
||||
echo "impeccable: downloaded file is empty: $url; refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
}
|
||||
if [ -n "$probing" ]; then
|
||||
# Inside another launcher's probe: no download, fail fast and quiet.
|
||||
exit 127
|
||||
@@ -116,6 +129,7 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
fetch_url "$url" && fetched=1
|
||||
fi
|
||||
if [ "$fetched" = 1 ]; then
|
||||
check_download
|
||||
# Fail closed: a freshly downloaded binary runs only after verifying
|
||||
# against its .sha256 sidecar. A sidecar that cannot be fetched, or a
|
||||
# machine with no sha256 tool, refuses the download instead of exec'ing
|
||||
@@ -127,15 +141,20 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -q -O "$tmp.sha256" "$url.sha256" 2>/dev/null && sidecar_ok=1
|
||||
fi
|
||||
check_download
|
||||
expected=""
|
||||
[ "$sidecar_ok" = 1 ] && expected=$(cut -d' ' -f1 < "$tmp.sha256")
|
||||
actual=""
|
||||
if command -v shasum >/dev/null 2>&1; then actual=$(shasum -a 256 "$tmp" | cut -d' ' -f1)
|
||||
elif command -v sha256sum >/dev/null 2>&1; then actual=$(sha256sum "$tmp" | cut -d' ' -f1); fi
|
||||
if command -v shasum >/dev/null 2>&1; then
|
||||
if digest=$(shasum -a 256 "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
elif command -v sha256sum >/dev/null 2>&1; then
|
||||
if digest=$(sha256sum "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
fi
|
||||
check_download
|
||||
rm -f "$tmp.sha256"
|
||||
if [ -z "$expected" ] || [ -z "$actual" ]; then
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or no sha256 tool); refusing the unverified download" >&2
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or hashing failed); refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ "$actual" != "$expected" ]; then
|
||||
@@ -143,8 +162,22 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
echo "impeccable: checksum mismatch downloading $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
chmod +x "$tmp" 2>/dev/null
|
||||
mv -f "$tmp" "$cached" && exec "$cached" "$@"
|
||||
check_download
|
||||
if ! chmod +x "$tmp" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not make the verified download executable: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download
|
||||
if ! mv -f "$tmp" "$cached" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not cache the verified download: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download "$cached"
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
fi
|
||||
|
||||
@@ -82,6 +82,8 @@ curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
rem Mirrors the sh launcher and fails closed: a freshly downloaded binary
|
||||
rem runs only after verifying against its .sha256 sidecar. A sidecar that
|
||||
rem cannot be fetched, or an empty certutil result, refuses the download
|
||||
@@ -91,8 +93,16 @@ if errorlevel 1 goto verify_refuse
|
||||
set "expected="
|
||||
set /p expected=<"%cached%.sha256"
|
||||
for /f "tokens=1" %%h in ("%expected%") do set "expected=%%h"
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
set "actual="
|
||||
for /f "skip=1 delims=" %%h in ('certutil -hashfile "%cached%.part" SHA256 2^>nul') do if not defined actual set "actual=%%h"
|
||||
rem Reuse the sidecar staging file after reading expected. Check certutil's
|
||||
rem status before parsing: its error text on stdout is not a digest.
|
||||
certutil -hashfile "%cached%.part" SHA256 >"%cached%.sha256" 2>nul
|
||||
if errorlevel 1 goto verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
for /f "usebackq skip=1 delims=" %%h in ("%cached%.sha256") do if not defined actual set "actual=%%h"
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
if not defined expected goto verify_refuse
|
||||
if not defined actual goto verify_refuse
|
||||
@@ -103,17 +113,48 @@ echo impeccable: checksum mismatch downloading %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: cannot verify %url% against %url%.sha256; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:check_download
|
||||
set "download_file=%~1"
|
||||
if not defined download_file set "download_file=%cached%.part"
|
||||
if not exist "%download_file%" goto download_missing
|
||||
for %%f in ("%download_file%") do if %%~zf==0 goto download_empty
|
||||
exit /b 0
|
||||
|
||||
:download_missing
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: download completed but the file was removed before execution: %url%; check your antivirus quarantine or logs. Refusing to continue; do not disable protection. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:download_empty
|
||||
del "%download_file%" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: downloaded file is empty: %url%; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:place
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
move /y "%cached%.part" "%cached%" >nul 2>nul
|
||||
if not exist "%cached%" goto fail
|
||||
if errorlevel 1 goto place_failed
|
||||
call :check_download "%cached%"
|
||||
if errorlevel 1 exit /b 127
|
||||
set "run=%cached%"
|
||||
goto run
|
||||
|
||||
:place_failed
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not cache the verified download: %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:run
|
||||
"%run%" %*
|
||||
exit /b
|
||||
|
||||
@@ -7834,7 +7834,6 @@
|
||||
if (editBadgeEl && editBadgeEl.style.display !== 'none') renderEditBadge('idle-disabled');
|
||||
showBar('generating');
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -7916,7 +7915,6 @@
|
||||
showBar('generating');
|
||||
startScrollTracking();
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -8238,7 +8236,8 @@
|
||||
// rasterization from delaying the fetch itself.
|
||||
if (!hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
await sendEvent(basePayload);
|
||||
const created = await sendEvent(basePayload);
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
|
||||
let screenshotPath;
|
||||
@@ -8279,7 +8278,10 @@
|
||||
// is semantic input. Plain requests were already dispatched above.
|
||||
if (hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
const created = await sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
// Capture/upload can take seconds. Progress before this acknowledgment
|
||||
// refers to an unknown session and would clear our own active work.
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -188,6 +188,20 @@ jobs:
|
||||
- run: cargo build --workspace --all-targets
|
||||
- run: cargo test --workspace --no-fail-fast
|
||||
|
||||
launcher-windows:
|
||||
runs-on: windows-latest
|
||||
needs: changes
|
||||
if: needs.changes.outputs.core == 'true'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 24
|
||||
- name: Exercise Windows launcher downloads and verification
|
||||
run: node --test tests/launcher-download.test.mjs
|
||||
|
||||
# Behavior gate: replays the tests/oracle/ goldens against a release build
|
||||
# of the engine from THIS checkout (so a PR is judged on its own source,
|
||||
# not on the last published binary). Without this job the oracle only ever
|
||||
|
||||
@@ -11,8 +11,10 @@ name: release-engine
|
||||
on:
|
||||
push:
|
||||
tags: ['engine-v*']
|
||||
# Same-run artifact transfers use ACTIONS_RUNTIME_TOKEN, not GITHUB_TOKEN;
|
||||
# they do not require actions: read/write. Keep downloads scoped to this run.
|
||||
permissions:
|
||||
contents: write
|
||||
contents: read
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
@@ -53,15 +55,79 @@ jobs:
|
||||
run: target/${{ matrix.target }}/release/impeccable${{ runner.os == 'Windows' && '.exe' || '' }} engine-probe
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: impeccable-${{ matrix.short }}
|
||||
# Keep unsigned Windows output outside the publish job's pattern.
|
||||
name: ${{ matrix.short == 'windows-x64' && 'unsigned-windows-x64' || format('impeccable-{0}', matrix.short) }}
|
||||
path: target/${{ matrix.target }}/release/impeccable${{ runner.os == 'Windows' && '.exe' || '' }}
|
||||
if-no-files-found: error
|
||||
publish:
|
||||
sign-windows:
|
||||
needs: build
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 15
|
||||
environment: windows-signing
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
steps:
|
||||
# A fresh runner signs only this run's engine. It does not check out or
|
||||
# execute repository code with the Azure identity available.
|
||||
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: unsigned-windows-x64
|
||||
path: unsigned
|
||||
- name: Azure login (OIDC)
|
||||
uses: azure/login@7ddb5af1ef8758cf1353cf3b42f940aee27ba21c # v3
|
||||
with:
|
||||
client-id: ${{ vars.AZURE_CLIENT_ID }}
|
||||
tenant-id: ${{ vars.AZURE_TENANT_ID }}
|
||||
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
|
||||
- name: Sign Windows engine
|
||||
uses: azure/artifact-signing-action@c7ab2a863ab5f9a846ddb8265964877ef296ee82 # v2
|
||||
with:
|
||||
endpoint: https://eus.codesigning.azure.net/
|
||||
signing-account-name: impeccable-signing
|
||||
certificate-profile-name: impeccable-windows
|
||||
files: ${{ github.workspace }}\unsigned\impeccable.exe
|
||||
file-digest: SHA256
|
||||
timestamp-rfc3161: http://timestamp.acs.microsoft.com
|
||||
timestamp-digest: SHA256
|
||||
description: Impeccable engine
|
||||
description-url: https://impeccable.style
|
||||
exclude-environment-credential: true
|
||||
# Only the preceding OIDC Azure CLI login is used. Other credential
|
||||
# types are excluded by this pinned action's defaults.
|
||||
exclude-azure-cli-credential: false
|
||||
cache-dependencies: false
|
||||
- name: Verify signed engine
|
||||
shell: pwsh
|
||||
run: |
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$signature = Get-AuthenticodeSignature -LiteralPath 'unsigned/impeccable.exe'
|
||||
if ($signature.Status -ne 'Valid') {
|
||||
throw "Invalid Windows signature: $($signature.Status) — $($signature.StatusMessage)"
|
||||
}
|
||||
$publisher = $signature.SignerCertificate.GetNameInfo([System.Security.Cryptography.X509Certificates.X509NameType]::SimpleName, $false)
|
||||
if ($publisher -cne 'Renaissance Geek, Inc.') {
|
||||
throw "Unexpected Windows publisher: $publisher"
|
||||
}
|
||||
if ($null -eq $signature.TimeStamperCertificate) {
|
||||
throw 'The Windows signature has no timestamp.'
|
||||
}
|
||||
Write-Output "Verified publisher: $publisher; certificate: $($signature.SignerCertificate.Thumbprint)"
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: impeccable-windows-x64
|
||||
path: unsigned/impeccable.exe
|
||||
if-no-files-found: error
|
||||
publish:
|
||||
needs: [build, sign-windows]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with: { path: artifacts }
|
||||
with:
|
||||
pattern: impeccable-*
|
||||
path: artifacts
|
||||
- name: Lay out release assets with checksums
|
||||
run: |
|
||||
set -e
|
||||
|
||||
@@ -71,7 +71,8 @@ Choose the mode from the requested surface, not the product, and persist it only
|
||||
Routing:
|
||||
|
||||
- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command.
|
||||
- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Explicit or clearly implied request to run a command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Workflow or command-selection question:** read [Workflow questions](reference/routing.md#workflow-questions).
|
||||
- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes a new surface or replacement world through init, then new-work; a narrow refinement of existing code proceeds on the incumbent implementation as `impeccable context` directs, offering init afterward rather than blocking on it.
|
||||
- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions.
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ const __impeccableLiveDev =
|
||||
process.env.NODE_ENV === "development" ? ["http://localhost:8400"] : [];
|
||||
```
|
||||
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: `tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts`, `tests/framework-fixtures/sveltekit-csp/expected-after-patch.js`. Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: [nextjs-turborepo/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts), [sveltekit-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/sveltekit-csp/expected-after-patch.js). Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
|
||||
### append-string
|
||||
|
||||
@@ -93,7 +93,7 @@ const __impeccableLiveDev =
|
||||
- `script-src 'self' 'unsafe-inline'` becomes `` `script-src 'self' 'unsafe-inline'${__impeccableLiveDev}` ``
|
||||
- `connect-src 'self'` becomes `` `connect-src 'self'${__impeccableLiveDev}` ``
|
||||
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: `tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js`, `tests/framework-fixtures/nuxt-csp/expected-after-patch.ts`.
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: [nextjs-inline-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js), [nuxt-csp/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nuxt-csp/expected-after-patch.ts).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ Complete HTML replacement of the original element per variant, not a CSS-only pa
|
||||
|
||||
Replace the style opening tag with `cssAuthoring.styleTag` when the tool returns a different one. **Each variant div contains exactly one top-level element**, same tag as the original; loose siblings break outline tracking and accept. First variant visible, all others `display: none`. The browser's MutationObserver accepts atomic or progressive arrival; accepting an arrived variant fences the worker, so later publications are rejected.
|
||||
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in `tests/live-e2e/agent.mjs` is a faithful template.
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in the [repo agent template](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/live-e2e/agent.mjs) is a faithful template.
|
||||
|
||||
**JSX / TSX targets:** wrap `<style>` content in a template literal (CSS braces would parse as JSX), use `className=` / `style={{…}}`, keep `data-impeccable-*` attributes as plain strings:
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
# No-argument routing: the context-aware menu
|
||||
# Command guidance
|
||||
|
||||
## Workflow questions
|
||||
|
||||
Give advice without executing commands; the menu below is only for bare invocations. Consult relevant command references as needed for prerequisites and scope. Link to the [docs](https://impeccable.style/docs/) for the broader workflow guide. If the user also requests execution, follow that request.
|
||||
|
||||
## No-argument routing: the context-aware menu
|
||||
|
||||
Read this when the user invokes `/impeccable` with no argument. They are asking "what should I do?" Make the menu context-aware instead of static.
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.0
|
||||
0.1.1
|
||||
|
||||
@@ -96,6 +96,19 @@ fetch_url() {
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
check_download() {
|
||||
download_file=${1:-$tmp}
|
||||
if [ ! -f "$download_file" ]; then
|
||||
rm -f "$tmp.sha256"
|
||||
echo "impeccable: download completed but the file was removed before execution: $url; check your antivirus quarantine or logs. Refusing to continue; do not disable protection." >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ ! -s "$download_file" ]; then
|
||||
rm -f "$download_file" "$tmp.sha256"
|
||||
echo "impeccable: downloaded file is empty: $url; refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
}
|
||||
if [ -n "$probing" ]; then
|
||||
# Inside another launcher's probe: no download, fail fast and quiet.
|
||||
exit 127
|
||||
@@ -116,6 +129,7 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
fetch_url "$url" && fetched=1
|
||||
fi
|
||||
if [ "$fetched" = 1 ]; then
|
||||
check_download
|
||||
# Fail closed: a freshly downloaded binary runs only after verifying
|
||||
# against its .sha256 sidecar. A sidecar that cannot be fetched, or a
|
||||
# machine with no sha256 tool, refuses the download instead of exec'ing
|
||||
@@ -127,15 +141,20 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -q -O "$tmp.sha256" "$url.sha256" 2>/dev/null && sidecar_ok=1
|
||||
fi
|
||||
check_download
|
||||
expected=""
|
||||
[ "$sidecar_ok" = 1 ] && expected=$(cut -d' ' -f1 < "$tmp.sha256")
|
||||
actual=""
|
||||
if command -v shasum >/dev/null 2>&1; then actual=$(shasum -a 256 "$tmp" | cut -d' ' -f1)
|
||||
elif command -v sha256sum >/dev/null 2>&1; then actual=$(sha256sum "$tmp" | cut -d' ' -f1); fi
|
||||
if command -v shasum >/dev/null 2>&1; then
|
||||
if digest=$(shasum -a 256 "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
elif command -v sha256sum >/dev/null 2>&1; then
|
||||
if digest=$(sha256sum "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
fi
|
||||
check_download
|
||||
rm -f "$tmp.sha256"
|
||||
if [ -z "$expected" ] || [ -z "$actual" ]; then
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or no sha256 tool); refusing the unverified download" >&2
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or hashing failed); refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ "$actual" != "$expected" ]; then
|
||||
@@ -143,8 +162,22 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
echo "impeccable: checksum mismatch downloading $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
chmod +x "$tmp" 2>/dev/null
|
||||
mv -f "$tmp" "$cached" && exec "$cached" "$@"
|
||||
check_download
|
||||
if ! chmod +x "$tmp" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not make the verified download executable: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download
|
||||
if ! mv -f "$tmp" "$cached" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not cache the verified download: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download "$cached"
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
fi
|
||||
|
||||
@@ -82,6 +82,8 @@ curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
rem Mirrors the sh launcher and fails closed: a freshly downloaded binary
|
||||
rem runs only after verifying against its .sha256 sidecar. A sidecar that
|
||||
rem cannot be fetched, or an empty certutil result, refuses the download
|
||||
@@ -91,8 +93,16 @@ if errorlevel 1 goto verify_refuse
|
||||
set "expected="
|
||||
set /p expected=<"%cached%.sha256"
|
||||
for /f "tokens=1" %%h in ("%expected%") do set "expected=%%h"
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
set "actual="
|
||||
for /f "skip=1 delims=" %%h in ('certutil -hashfile "%cached%.part" SHA256 2^>nul') do if not defined actual set "actual=%%h"
|
||||
rem Reuse the sidecar staging file after reading expected. Check certutil's
|
||||
rem status before parsing: its error text on stdout is not a digest.
|
||||
certutil -hashfile "%cached%.part" SHA256 >"%cached%.sha256" 2>nul
|
||||
if errorlevel 1 goto verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
for /f "usebackq skip=1 delims=" %%h in ("%cached%.sha256") do if not defined actual set "actual=%%h"
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
if not defined expected goto verify_refuse
|
||||
if not defined actual goto verify_refuse
|
||||
@@ -103,17 +113,48 @@ echo impeccable: checksum mismatch downloading %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: cannot verify %url% against %url%.sha256; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:check_download
|
||||
set "download_file=%~1"
|
||||
if not defined download_file set "download_file=%cached%.part"
|
||||
if not exist "%download_file%" goto download_missing
|
||||
for %%f in ("%download_file%") do if %%~zf==0 goto download_empty
|
||||
exit /b 0
|
||||
|
||||
:download_missing
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: download completed but the file was removed before execution: %url%; check your antivirus quarantine or logs. Refusing to continue; do not disable protection. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:download_empty
|
||||
del "%download_file%" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: downloaded file is empty: %url%; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:place
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
move /y "%cached%.part" "%cached%" >nul 2>nul
|
||||
if not exist "%cached%" goto fail
|
||||
if errorlevel 1 goto place_failed
|
||||
call :check_download "%cached%"
|
||||
if errorlevel 1 exit /b 127
|
||||
set "run=%cached%"
|
||||
goto run
|
||||
|
||||
:place_failed
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not cache the verified download: %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:run
|
||||
"%run%" %*
|
||||
exit /b
|
||||
|
||||
@@ -7834,7 +7834,6 @@
|
||||
if (editBadgeEl && editBadgeEl.style.display !== 'none') renderEditBadge('idle-disabled');
|
||||
showBar('generating');
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -7916,7 +7915,6 @@
|
||||
showBar('generating');
|
||||
startScrollTracking();
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -8238,7 +8236,8 @@
|
||||
// rasterization from delaying the fetch itself.
|
||||
if (!hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
await sendEvent(basePayload);
|
||||
const created = await sendEvent(basePayload);
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
|
||||
let screenshotPath;
|
||||
@@ -8279,7 +8278,10 @@
|
||||
// is semantic input. Plain requests were already dispatched above.
|
||||
if (hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
const created = await sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
// Capture/upload can take seconds. Progress before this acknowledgment
|
||||
// refers to an unknown session and would clear our own active work.
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,8 @@ Choose the mode from the requested surface, not the product, and persist it only
|
||||
Routing:
|
||||
|
||||
- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command.
|
||||
- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Explicit or clearly implied request to run a command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Workflow or command-selection question:** read [Workflow questions](reference/routing.md#workflow-questions).
|
||||
- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes a new surface or replacement world through init, then new-work; a narrow refinement of existing code proceeds on the incumbent implementation as `impeccable context` directs, offering init afterward rather than blocking on it.
|
||||
- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions.
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ const __impeccableLiveDev =
|
||||
process.env.NODE_ENV === "development" ? ["http://localhost:8400"] : [];
|
||||
```
|
||||
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: `tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts`, `tests/framework-fixtures/sveltekit-csp/expected-after-patch.js`. Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: [nextjs-turborepo/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts), [sveltekit-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/sveltekit-csp/expected-after-patch.js). Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
|
||||
### append-string
|
||||
|
||||
@@ -93,7 +93,7 @@ const __impeccableLiveDev =
|
||||
- `script-src 'self' 'unsafe-inline'` becomes `` `script-src 'self' 'unsafe-inline'${__impeccableLiveDev}` ``
|
||||
- `connect-src 'self'` becomes `` `connect-src 'self'${__impeccableLiveDev}` ``
|
||||
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: `tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js`, `tests/framework-fixtures/nuxt-csp/expected-after-patch.ts`.
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: [nextjs-inline-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js), [nuxt-csp/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nuxt-csp/expected-after-patch.ts).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ Complete HTML replacement of the original element per variant, not a CSS-only pa
|
||||
|
||||
Replace the style opening tag with `cssAuthoring.styleTag` when the tool returns a different one. **Each variant div contains exactly one top-level element**, same tag as the original; loose siblings break outline tracking and accept. First variant visible, all others `display: none`. The browser's MutationObserver accepts atomic or progressive arrival; accepting an arrived variant fences the worker, so later publications are rejected.
|
||||
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in `tests/live-e2e/agent.mjs` is a faithful template.
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in the [repo agent template](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/live-e2e/agent.mjs) is a faithful template.
|
||||
|
||||
**JSX / TSX targets:** wrap `<style>` content in a template literal (CSS braces would parse as JSX), use `className=` / `style={{…}}`, keep `data-impeccable-*` attributes as plain strings:
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
# No-argument routing: the context-aware menu
|
||||
# Command guidance
|
||||
|
||||
## Workflow questions
|
||||
|
||||
Give advice without executing commands; the menu below is only for bare invocations. Consult relevant command references as needed for prerequisites and scope. Link to the [docs](https://impeccable.style/docs/) for the broader workflow guide. If the user also requests execution, follow that request.
|
||||
|
||||
## No-argument routing: the context-aware menu
|
||||
|
||||
Read this when the user invokes `/impeccable` with no argument. They are asking "what should I do?" Make the menu context-aware instead of static.
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.0
|
||||
0.1.1
|
||||
|
||||
@@ -96,6 +96,19 @@ fetch_url() {
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
check_download() {
|
||||
download_file=${1:-$tmp}
|
||||
if [ ! -f "$download_file" ]; then
|
||||
rm -f "$tmp.sha256"
|
||||
echo "impeccable: download completed but the file was removed before execution: $url; check your antivirus quarantine or logs. Refusing to continue; do not disable protection." >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ ! -s "$download_file" ]; then
|
||||
rm -f "$download_file" "$tmp.sha256"
|
||||
echo "impeccable: downloaded file is empty: $url; refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
}
|
||||
if [ -n "$probing" ]; then
|
||||
# Inside another launcher's probe: no download, fail fast and quiet.
|
||||
exit 127
|
||||
@@ -116,6 +129,7 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
fetch_url "$url" && fetched=1
|
||||
fi
|
||||
if [ "$fetched" = 1 ]; then
|
||||
check_download
|
||||
# Fail closed: a freshly downloaded binary runs only after verifying
|
||||
# against its .sha256 sidecar. A sidecar that cannot be fetched, or a
|
||||
# machine with no sha256 tool, refuses the download instead of exec'ing
|
||||
@@ -127,15 +141,20 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -q -O "$tmp.sha256" "$url.sha256" 2>/dev/null && sidecar_ok=1
|
||||
fi
|
||||
check_download
|
||||
expected=""
|
||||
[ "$sidecar_ok" = 1 ] && expected=$(cut -d' ' -f1 < "$tmp.sha256")
|
||||
actual=""
|
||||
if command -v shasum >/dev/null 2>&1; then actual=$(shasum -a 256 "$tmp" | cut -d' ' -f1)
|
||||
elif command -v sha256sum >/dev/null 2>&1; then actual=$(sha256sum "$tmp" | cut -d' ' -f1); fi
|
||||
if command -v shasum >/dev/null 2>&1; then
|
||||
if digest=$(shasum -a 256 "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
elif command -v sha256sum >/dev/null 2>&1; then
|
||||
if digest=$(sha256sum "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
fi
|
||||
check_download
|
||||
rm -f "$tmp.sha256"
|
||||
if [ -z "$expected" ] || [ -z "$actual" ]; then
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or no sha256 tool); refusing the unverified download" >&2
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or hashing failed); refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ "$actual" != "$expected" ]; then
|
||||
@@ -143,8 +162,22 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
echo "impeccable: checksum mismatch downloading $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
chmod +x "$tmp" 2>/dev/null
|
||||
mv -f "$tmp" "$cached" && exec "$cached" "$@"
|
||||
check_download
|
||||
if ! chmod +x "$tmp" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not make the verified download executable: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download
|
||||
if ! mv -f "$tmp" "$cached" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not cache the verified download: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download "$cached"
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
fi
|
||||
|
||||
@@ -82,6 +82,8 @@ curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
rem Mirrors the sh launcher and fails closed: a freshly downloaded binary
|
||||
rem runs only after verifying against its .sha256 sidecar. A sidecar that
|
||||
rem cannot be fetched, or an empty certutil result, refuses the download
|
||||
@@ -91,8 +93,16 @@ if errorlevel 1 goto verify_refuse
|
||||
set "expected="
|
||||
set /p expected=<"%cached%.sha256"
|
||||
for /f "tokens=1" %%h in ("%expected%") do set "expected=%%h"
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
set "actual="
|
||||
for /f "skip=1 delims=" %%h in ('certutil -hashfile "%cached%.part" SHA256 2^>nul') do if not defined actual set "actual=%%h"
|
||||
rem Reuse the sidecar staging file after reading expected. Check certutil's
|
||||
rem status before parsing: its error text on stdout is not a digest.
|
||||
certutil -hashfile "%cached%.part" SHA256 >"%cached%.sha256" 2>nul
|
||||
if errorlevel 1 goto verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
for /f "usebackq skip=1 delims=" %%h in ("%cached%.sha256") do if not defined actual set "actual=%%h"
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
if not defined expected goto verify_refuse
|
||||
if not defined actual goto verify_refuse
|
||||
@@ -103,17 +113,48 @@ echo impeccable: checksum mismatch downloading %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: cannot verify %url% against %url%.sha256; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:check_download
|
||||
set "download_file=%~1"
|
||||
if not defined download_file set "download_file=%cached%.part"
|
||||
if not exist "%download_file%" goto download_missing
|
||||
for %%f in ("%download_file%") do if %%~zf==0 goto download_empty
|
||||
exit /b 0
|
||||
|
||||
:download_missing
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: download completed but the file was removed before execution: %url%; check your antivirus quarantine or logs. Refusing to continue; do not disable protection. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:download_empty
|
||||
del "%download_file%" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: downloaded file is empty: %url%; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:place
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
move /y "%cached%.part" "%cached%" >nul 2>nul
|
||||
if not exist "%cached%" goto fail
|
||||
if errorlevel 1 goto place_failed
|
||||
call :check_download "%cached%"
|
||||
if errorlevel 1 exit /b 127
|
||||
set "run=%cached%"
|
||||
goto run
|
||||
|
||||
:place_failed
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not cache the verified download: %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:run
|
||||
"%run%" %*
|
||||
exit /b
|
||||
|
||||
@@ -7834,7 +7834,6 @@
|
||||
if (editBadgeEl && editBadgeEl.style.display !== 'none') renderEditBadge('idle-disabled');
|
||||
showBar('generating');
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -7916,7 +7915,6 @@
|
||||
showBar('generating');
|
||||
startScrollTracking();
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -8238,7 +8236,8 @@
|
||||
// rasterization from delaying the fetch itself.
|
||||
if (!hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
await sendEvent(basePayload);
|
||||
const created = await sendEvent(basePayload);
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
|
||||
let screenshotPath;
|
||||
@@ -8279,7 +8278,10 @@
|
||||
// is semantic input. Plain requests were already dispatched above.
|
||||
if (hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
const created = await sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
// Capture/upload can take seconds. Progress before this acknowledgment
|
||||
// refers to an unknown session and would clear our own active work.
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,8 @@ Choose the mode from the requested surface, not the product, and persist it only
|
||||
Routing:
|
||||
|
||||
- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command.
|
||||
- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Explicit or clearly implied request to run a command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Workflow or command-selection question:** read [Workflow questions](reference/routing.md#workflow-questions).
|
||||
- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes a new surface or replacement world through init, then new-work; a narrow refinement of existing code proceeds on the incumbent implementation as `impeccable context` directs, offering init afterward rather than blocking on it.
|
||||
- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions.
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ const __impeccableLiveDev =
|
||||
process.env.NODE_ENV === "development" ? ["http://localhost:8400"] : [];
|
||||
```
|
||||
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: `tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts`, `tests/framework-fixtures/sveltekit-csp/expected-after-patch.js`. Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: [nextjs-turborepo/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts), [sveltekit-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/sveltekit-csp/expected-after-patch.js). Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
|
||||
### append-string
|
||||
|
||||
@@ -93,7 +93,7 @@ const __impeccableLiveDev =
|
||||
- `script-src 'self' 'unsafe-inline'` becomes `` `script-src 'self' 'unsafe-inline'${__impeccableLiveDev}` ``
|
||||
- `connect-src 'self'` becomes `` `connect-src 'self'${__impeccableLiveDev}` ``
|
||||
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: `tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js`, `tests/framework-fixtures/nuxt-csp/expected-after-patch.ts`.
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: [nextjs-inline-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js), [nuxt-csp/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nuxt-csp/expected-after-patch.ts).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ Complete HTML replacement of the original element per variant, not a CSS-only pa
|
||||
|
||||
Replace the style opening tag with `cssAuthoring.styleTag` when the tool returns a different one. **Each variant div contains exactly one top-level element**, same tag as the original; loose siblings break outline tracking and accept. First variant visible, all others `display: none`. The browser's MutationObserver accepts atomic or progressive arrival; accepting an arrived variant fences the worker, so later publications are rejected.
|
||||
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in `tests/live-e2e/agent.mjs` is a faithful template.
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in the [repo agent template](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/live-e2e/agent.mjs) is a faithful template.
|
||||
|
||||
**JSX / TSX targets:** wrap `<style>` content in a template literal (CSS braces would parse as JSX), use `className=` / `style={{…}}`, keep `data-impeccable-*` attributes as plain strings:
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
# No-argument routing: the context-aware menu
|
||||
# Command guidance
|
||||
|
||||
## Workflow questions
|
||||
|
||||
Give advice without executing commands; the menu below is only for bare invocations. Consult relevant command references as needed for prerequisites and scope. Link to the [docs](https://impeccable.style/docs/) for the broader workflow guide. If the user also requests execution, follow that request.
|
||||
|
||||
## No-argument routing: the context-aware menu
|
||||
|
||||
Read this when the user invokes `/impeccable` with no argument. They are asking "what should I do?" Make the menu context-aware instead of static.
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.0
|
||||
0.1.1
|
||||
|
||||
@@ -96,6 +96,19 @@ fetch_url() {
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
check_download() {
|
||||
download_file=${1:-$tmp}
|
||||
if [ ! -f "$download_file" ]; then
|
||||
rm -f "$tmp.sha256"
|
||||
echo "impeccable: download completed but the file was removed before execution: $url; check your antivirus quarantine or logs. Refusing to continue; do not disable protection." >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ ! -s "$download_file" ]; then
|
||||
rm -f "$download_file" "$tmp.sha256"
|
||||
echo "impeccable: downloaded file is empty: $url; refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
}
|
||||
if [ -n "$probing" ]; then
|
||||
# Inside another launcher's probe: no download, fail fast and quiet.
|
||||
exit 127
|
||||
@@ -116,6 +129,7 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
fetch_url "$url" && fetched=1
|
||||
fi
|
||||
if [ "$fetched" = 1 ]; then
|
||||
check_download
|
||||
# Fail closed: a freshly downloaded binary runs only after verifying
|
||||
# against its .sha256 sidecar. A sidecar that cannot be fetched, or a
|
||||
# machine with no sha256 tool, refuses the download instead of exec'ing
|
||||
@@ -127,15 +141,20 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -q -O "$tmp.sha256" "$url.sha256" 2>/dev/null && sidecar_ok=1
|
||||
fi
|
||||
check_download
|
||||
expected=""
|
||||
[ "$sidecar_ok" = 1 ] && expected=$(cut -d' ' -f1 < "$tmp.sha256")
|
||||
actual=""
|
||||
if command -v shasum >/dev/null 2>&1; then actual=$(shasum -a 256 "$tmp" | cut -d' ' -f1)
|
||||
elif command -v sha256sum >/dev/null 2>&1; then actual=$(sha256sum "$tmp" | cut -d' ' -f1); fi
|
||||
if command -v shasum >/dev/null 2>&1; then
|
||||
if digest=$(shasum -a 256 "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
elif command -v sha256sum >/dev/null 2>&1; then
|
||||
if digest=$(sha256sum "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
fi
|
||||
check_download
|
||||
rm -f "$tmp.sha256"
|
||||
if [ -z "$expected" ] || [ -z "$actual" ]; then
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or no sha256 tool); refusing the unverified download" >&2
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or hashing failed); refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ "$actual" != "$expected" ]; then
|
||||
@@ -143,8 +162,22 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
echo "impeccable: checksum mismatch downloading $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
chmod +x "$tmp" 2>/dev/null
|
||||
mv -f "$tmp" "$cached" && exec "$cached" "$@"
|
||||
check_download
|
||||
if ! chmod +x "$tmp" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not make the verified download executable: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download
|
||||
if ! mv -f "$tmp" "$cached" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not cache the verified download: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download "$cached"
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
fi
|
||||
|
||||
@@ -82,6 +82,8 @@ curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
rem Mirrors the sh launcher and fails closed: a freshly downloaded binary
|
||||
rem runs only after verifying against its .sha256 sidecar. A sidecar that
|
||||
rem cannot be fetched, or an empty certutil result, refuses the download
|
||||
@@ -91,8 +93,16 @@ if errorlevel 1 goto verify_refuse
|
||||
set "expected="
|
||||
set /p expected=<"%cached%.sha256"
|
||||
for /f "tokens=1" %%h in ("%expected%") do set "expected=%%h"
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
set "actual="
|
||||
for /f "skip=1 delims=" %%h in ('certutil -hashfile "%cached%.part" SHA256 2^>nul') do if not defined actual set "actual=%%h"
|
||||
rem Reuse the sidecar staging file after reading expected. Check certutil's
|
||||
rem status before parsing: its error text on stdout is not a digest.
|
||||
certutil -hashfile "%cached%.part" SHA256 >"%cached%.sha256" 2>nul
|
||||
if errorlevel 1 goto verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
for /f "usebackq skip=1 delims=" %%h in ("%cached%.sha256") do if not defined actual set "actual=%%h"
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
if not defined expected goto verify_refuse
|
||||
if not defined actual goto verify_refuse
|
||||
@@ -103,17 +113,48 @@ echo impeccable: checksum mismatch downloading %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: cannot verify %url% against %url%.sha256; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:check_download
|
||||
set "download_file=%~1"
|
||||
if not defined download_file set "download_file=%cached%.part"
|
||||
if not exist "%download_file%" goto download_missing
|
||||
for %%f in ("%download_file%") do if %%~zf==0 goto download_empty
|
||||
exit /b 0
|
||||
|
||||
:download_missing
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: download completed but the file was removed before execution: %url%; check your antivirus quarantine or logs. Refusing to continue; do not disable protection. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:download_empty
|
||||
del "%download_file%" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: downloaded file is empty: %url%; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:place
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
move /y "%cached%.part" "%cached%" >nul 2>nul
|
||||
if not exist "%cached%" goto fail
|
||||
if errorlevel 1 goto place_failed
|
||||
call :check_download "%cached%"
|
||||
if errorlevel 1 exit /b 127
|
||||
set "run=%cached%"
|
||||
goto run
|
||||
|
||||
:place_failed
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not cache the verified download: %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:run
|
||||
"%run%" %*
|
||||
exit /b
|
||||
|
||||
@@ -7834,7 +7834,6 @@
|
||||
if (editBadgeEl && editBadgeEl.style.display !== 'none') renderEditBadge('idle-disabled');
|
||||
showBar('generating');
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -7916,7 +7915,6 @@
|
||||
showBar('generating');
|
||||
startScrollTracking();
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -8238,7 +8236,8 @@
|
||||
// rasterization from delaying the fetch itself.
|
||||
if (!hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
await sendEvent(basePayload);
|
||||
const created = await sendEvent(basePayload);
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
|
||||
let screenshotPath;
|
||||
@@ -8279,7 +8278,10 @@
|
||||
// is semantic input. Plain requests were already dispatched above.
|
||||
if (hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
const created = await sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
// Capture/upload can take seconds. Progress before this acknowledgment
|
||||
// refers to an unknown session and would clear our own active work.
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,8 @@ Choose the mode from the requested surface, not the product, and persist it only
|
||||
Routing:
|
||||
|
||||
- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command.
|
||||
- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Explicit or clearly implied request to run a command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Workflow or command-selection question:** read [Workflow questions](reference/routing.md#workflow-questions).
|
||||
- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes a new surface or replacement world through init, then new-work; a narrow refinement of existing code proceeds on the incumbent implementation as `impeccable context` directs, offering init afterward rather than blocking on it.
|
||||
- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions.
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ const __impeccableLiveDev =
|
||||
process.env.NODE_ENV === "development" ? ["http://localhost:8400"] : [];
|
||||
```
|
||||
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: `tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts`, `tests/framework-fixtures/sveltekit-csp/expected-after-patch.js`. Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: [nextjs-turborepo/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts), [sveltekit-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/sveltekit-csp/expected-after-patch.js). Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
|
||||
### append-string
|
||||
|
||||
@@ -93,7 +93,7 @@ const __impeccableLiveDev =
|
||||
- `script-src 'self' 'unsafe-inline'` becomes `` `script-src 'self' 'unsafe-inline'${__impeccableLiveDev}` ``
|
||||
- `connect-src 'self'` becomes `` `connect-src 'self'${__impeccableLiveDev}` ``
|
||||
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: `tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js`, `tests/framework-fixtures/nuxt-csp/expected-after-patch.ts`.
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: [nextjs-inline-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js), [nuxt-csp/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nuxt-csp/expected-after-patch.ts).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ Complete HTML replacement of the original element per variant, not a CSS-only pa
|
||||
|
||||
Replace the style opening tag with `cssAuthoring.styleTag` when the tool returns a different one. **Each variant div contains exactly one top-level element**, same tag as the original; loose siblings break outline tracking and accept. First variant visible, all others `display: none`. The browser's MutationObserver accepts atomic or progressive arrival; accepting an arrived variant fences the worker, so later publications are rejected.
|
||||
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in `tests/live-e2e/agent.mjs` is a faithful template.
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in the [repo agent template](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/live-e2e/agent.mjs) is a faithful template.
|
||||
|
||||
**JSX / TSX targets:** wrap `<style>` content in a template literal (CSS braces would parse as JSX), use `className=` / `style={{…}}`, keep `data-impeccable-*` attributes as plain strings:
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
# No-argument routing: the context-aware menu
|
||||
# Command guidance
|
||||
|
||||
## Workflow questions
|
||||
|
||||
Give advice without executing commands; the menu below is only for bare invocations. Consult relevant command references as needed for prerequisites and scope. Link to the [docs](https://impeccable.style/docs/) for the broader workflow guide. If the user also requests execution, follow that request.
|
||||
|
||||
## No-argument routing: the context-aware menu
|
||||
|
||||
Read this when the user invokes `/impeccable` with no argument. They are asking "what should I do?" Make the menu context-aware instead of static.
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.0
|
||||
0.1.1
|
||||
|
||||
@@ -96,6 +96,19 @@ fetch_url() {
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
check_download() {
|
||||
download_file=${1:-$tmp}
|
||||
if [ ! -f "$download_file" ]; then
|
||||
rm -f "$tmp.sha256"
|
||||
echo "impeccable: download completed but the file was removed before execution: $url; check your antivirus quarantine or logs. Refusing to continue; do not disable protection." >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ ! -s "$download_file" ]; then
|
||||
rm -f "$download_file" "$tmp.sha256"
|
||||
echo "impeccable: downloaded file is empty: $url; refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
}
|
||||
if [ -n "$probing" ]; then
|
||||
# Inside another launcher's probe: no download, fail fast and quiet.
|
||||
exit 127
|
||||
@@ -116,6 +129,7 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
fetch_url "$url" && fetched=1
|
||||
fi
|
||||
if [ "$fetched" = 1 ]; then
|
||||
check_download
|
||||
# Fail closed: a freshly downloaded binary runs only after verifying
|
||||
# against its .sha256 sidecar. A sidecar that cannot be fetched, or a
|
||||
# machine with no sha256 tool, refuses the download instead of exec'ing
|
||||
@@ -127,15 +141,20 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -q -O "$tmp.sha256" "$url.sha256" 2>/dev/null && sidecar_ok=1
|
||||
fi
|
||||
check_download
|
||||
expected=""
|
||||
[ "$sidecar_ok" = 1 ] && expected=$(cut -d' ' -f1 < "$tmp.sha256")
|
||||
actual=""
|
||||
if command -v shasum >/dev/null 2>&1; then actual=$(shasum -a 256 "$tmp" | cut -d' ' -f1)
|
||||
elif command -v sha256sum >/dev/null 2>&1; then actual=$(sha256sum "$tmp" | cut -d' ' -f1); fi
|
||||
if command -v shasum >/dev/null 2>&1; then
|
||||
if digest=$(shasum -a 256 "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
elif command -v sha256sum >/dev/null 2>&1; then
|
||||
if digest=$(sha256sum "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
fi
|
||||
check_download
|
||||
rm -f "$tmp.sha256"
|
||||
if [ -z "$expected" ] || [ -z "$actual" ]; then
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or no sha256 tool); refusing the unverified download" >&2
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or hashing failed); refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ "$actual" != "$expected" ]; then
|
||||
@@ -143,8 +162,22 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
echo "impeccable: checksum mismatch downloading $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
chmod +x "$tmp" 2>/dev/null
|
||||
mv -f "$tmp" "$cached" && exec "$cached" "$@"
|
||||
check_download
|
||||
if ! chmod +x "$tmp" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not make the verified download executable: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download
|
||||
if ! mv -f "$tmp" "$cached" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not cache the verified download: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download "$cached"
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
fi
|
||||
|
||||
@@ -82,6 +82,8 @@ curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
rem Mirrors the sh launcher and fails closed: a freshly downloaded binary
|
||||
rem runs only after verifying against its .sha256 sidecar. A sidecar that
|
||||
rem cannot be fetched, or an empty certutil result, refuses the download
|
||||
@@ -91,8 +93,16 @@ if errorlevel 1 goto verify_refuse
|
||||
set "expected="
|
||||
set /p expected=<"%cached%.sha256"
|
||||
for /f "tokens=1" %%h in ("%expected%") do set "expected=%%h"
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
set "actual="
|
||||
for /f "skip=1 delims=" %%h in ('certutil -hashfile "%cached%.part" SHA256 2^>nul') do if not defined actual set "actual=%%h"
|
||||
rem Reuse the sidecar staging file after reading expected. Check certutil's
|
||||
rem status before parsing: its error text on stdout is not a digest.
|
||||
certutil -hashfile "%cached%.part" SHA256 >"%cached%.sha256" 2>nul
|
||||
if errorlevel 1 goto verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
for /f "usebackq skip=1 delims=" %%h in ("%cached%.sha256") do if not defined actual set "actual=%%h"
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
if not defined expected goto verify_refuse
|
||||
if not defined actual goto verify_refuse
|
||||
@@ -103,17 +113,48 @@ echo impeccable: checksum mismatch downloading %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: cannot verify %url% against %url%.sha256; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:check_download
|
||||
set "download_file=%~1"
|
||||
if not defined download_file set "download_file=%cached%.part"
|
||||
if not exist "%download_file%" goto download_missing
|
||||
for %%f in ("%download_file%") do if %%~zf==0 goto download_empty
|
||||
exit /b 0
|
||||
|
||||
:download_missing
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: download completed but the file was removed before execution: %url%; check your antivirus quarantine or logs. Refusing to continue; do not disable protection. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:download_empty
|
||||
del "%download_file%" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: downloaded file is empty: %url%; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:place
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
move /y "%cached%.part" "%cached%" >nul 2>nul
|
||||
if not exist "%cached%" goto fail
|
||||
if errorlevel 1 goto place_failed
|
||||
call :check_download "%cached%"
|
||||
if errorlevel 1 exit /b 127
|
||||
set "run=%cached%"
|
||||
goto run
|
||||
|
||||
:place_failed
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not cache the verified download: %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:run
|
||||
"%run%" %*
|
||||
exit /b
|
||||
|
||||
@@ -7834,7 +7834,6 @@
|
||||
if (editBadgeEl && editBadgeEl.style.display !== 'none') renderEditBadge('idle-disabled');
|
||||
showBar('generating');
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -7916,7 +7915,6 @@
|
||||
showBar('generating');
|
||||
startScrollTracking();
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -8238,7 +8236,8 @@
|
||||
// rasterization from delaying the fetch itself.
|
||||
if (!hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
await sendEvent(basePayload);
|
||||
const created = await sendEvent(basePayload);
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
|
||||
let screenshotPath;
|
||||
@@ -8279,7 +8278,10 @@
|
||||
// is semantic input. Plain requests were already dispatched above.
|
||||
if (hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
const created = await sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
// Capture/upload can take seconds. Progress before this acknowledgment
|
||||
// refers to an unknown session and would clear our own active work.
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,8 @@ Choose the mode from the requested surface, not the product, and persist it only
|
||||
Routing:
|
||||
|
||||
- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command.
|
||||
- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Explicit or clearly implied request to run a command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Workflow or command-selection question:** read [Workflow questions](reference/routing.md#workflow-questions).
|
||||
- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes a new surface or replacement world through init, then new-work; a narrow refinement of existing code proceeds on the incumbent implementation as `impeccable context` directs, offering init afterward rather than blocking on it.
|
||||
- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions.
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ const __impeccableLiveDev =
|
||||
process.env.NODE_ENV === "development" ? ["http://localhost:8400"] : [];
|
||||
```
|
||||
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: `tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts`, `tests/framework-fixtures/sveltekit-csp/expected-after-patch.js`. Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: [nextjs-turborepo/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts), [sveltekit-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/sveltekit-csp/expected-after-patch.js). Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
|
||||
### append-string
|
||||
|
||||
@@ -93,7 +93,7 @@ const __impeccableLiveDev =
|
||||
- `script-src 'self' 'unsafe-inline'` becomes `` `script-src 'self' 'unsafe-inline'${__impeccableLiveDev}` ``
|
||||
- `connect-src 'self'` becomes `` `connect-src 'self'${__impeccableLiveDev}` ``
|
||||
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: `tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js`, `tests/framework-fixtures/nuxt-csp/expected-after-patch.ts`.
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: [nextjs-inline-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js), [nuxt-csp/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nuxt-csp/expected-after-patch.ts).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ Complete HTML replacement of the original element per variant, not a CSS-only pa
|
||||
|
||||
Replace the style opening tag with `cssAuthoring.styleTag` when the tool returns a different one. **Each variant div contains exactly one top-level element**, same tag as the original; loose siblings break outline tracking and accept. First variant visible, all others `display: none`. The browser's MutationObserver accepts atomic or progressive arrival; accepting an arrived variant fences the worker, so later publications are rejected.
|
||||
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in `tests/live-e2e/agent.mjs` is a faithful template.
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in the [repo agent template](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/live-e2e/agent.mjs) is a faithful template.
|
||||
|
||||
**JSX / TSX targets:** wrap `<style>` content in a template literal (CSS braces would parse as JSX), use `className=` / `style={{…}}`, keep `data-impeccable-*` attributes as plain strings:
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
# No-argument routing: the context-aware menu
|
||||
# Command guidance
|
||||
|
||||
## Workflow questions
|
||||
|
||||
Give advice without executing commands; the menu below is only for bare invocations. Consult relevant command references as needed for prerequisites and scope. Link to the [docs](https://impeccable.style/docs/) for the broader workflow guide. If the user also requests execution, follow that request.
|
||||
|
||||
## No-argument routing: the context-aware menu
|
||||
|
||||
Read this when the user invokes `/impeccable` with no argument. They are asking "what should I do?" Make the menu context-aware instead of static.
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.0
|
||||
0.1.1
|
||||
|
||||
@@ -96,6 +96,19 @@ fetch_url() {
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
check_download() {
|
||||
download_file=${1:-$tmp}
|
||||
if [ ! -f "$download_file" ]; then
|
||||
rm -f "$tmp.sha256"
|
||||
echo "impeccable: download completed but the file was removed before execution: $url; check your antivirus quarantine or logs. Refusing to continue; do not disable protection." >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ ! -s "$download_file" ]; then
|
||||
rm -f "$download_file" "$tmp.sha256"
|
||||
echo "impeccable: downloaded file is empty: $url; refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
}
|
||||
if [ -n "$probing" ]; then
|
||||
# Inside another launcher's probe: no download, fail fast and quiet.
|
||||
exit 127
|
||||
@@ -116,6 +129,7 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
fetch_url "$url" && fetched=1
|
||||
fi
|
||||
if [ "$fetched" = 1 ]; then
|
||||
check_download
|
||||
# Fail closed: a freshly downloaded binary runs only after verifying
|
||||
# against its .sha256 sidecar. A sidecar that cannot be fetched, or a
|
||||
# machine with no sha256 tool, refuses the download instead of exec'ing
|
||||
@@ -127,15 +141,20 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -q -O "$tmp.sha256" "$url.sha256" 2>/dev/null && sidecar_ok=1
|
||||
fi
|
||||
check_download
|
||||
expected=""
|
||||
[ "$sidecar_ok" = 1 ] && expected=$(cut -d' ' -f1 < "$tmp.sha256")
|
||||
actual=""
|
||||
if command -v shasum >/dev/null 2>&1; then actual=$(shasum -a 256 "$tmp" | cut -d' ' -f1)
|
||||
elif command -v sha256sum >/dev/null 2>&1; then actual=$(sha256sum "$tmp" | cut -d' ' -f1); fi
|
||||
if command -v shasum >/dev/null 2>&1; then
|
||||
if digest=$(shasum -a 256 "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
elif command -v sha256sum >/dev/null 2>&1; then
|
||||
if digest=$(sha256sum "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
fi
|
||||
check_download
|
||||
rm -f "$tmp.sha256"
|
||||
if [ -z "$expected" ] || [ -z "$actual" ]; then
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or no sha256 tool); refusing the unverified download" >&2
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or hashing failed); refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ "$actual" != "$expected" ]; then
|
||||
@@ -143,8 +162,22 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
echo "impeccable: checksum mismatch downloading $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
chmod +x "$tmp" 2>/dev/null
|
||||
mv -f "$tmp" "$cached" && exec "$cached" "$@"
|
||||
check_download
|
||||
if ! chmod +x "$tmp" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not make the verified download executable: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download
|
||||
if ! mv -f "$tmp" "$cached" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not cache the verified download: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download "$cached"
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
fi
|
||||
|
||||
@@ -82,6 +82,8 @@ curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
rem Mirrors the sh launcher and fails closed: a freshly downloaded binary
|
||||
rem runs only after verifying against its .sha256 sidecar. A sidecar that
|
||||
rem cannot be fetched, or an empty certutil result, refuses the download
|
||||
@@ -91,8 +93,16 @@ if errorlevel 1 goto verify_refuse
|
||||
set "expected="
|
||||
set /p expected=<"%cached%.sha256"
|
||||
for /f "tokens=1" %%h in ("%expected%") do set "expected=%%h"
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
set "actual="
|
||||
for /f "skip=1 delims=" %%h in ('certutil -hashfile "%cached%.part" SHA256 2^>nul') do if not defined actual set "actual=%%h"
|
||||
rem Reuse the sidecar staging file after reading expected. Check certutil's
|
||||
rem status before parsing: its error text on stdout is not a digest.
|
||||
certutil -hashfile "%cached%.part" SHA256 >"%cached%.sha256" 2>nul
|
||||
if errorlevel 1 goto verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
for /f "usebackq skip=1 delims=" %%h in ("%cached%.sha256") do if not defined actual set "actual=%%h"
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
if not defined expected goto verify_refuse
|
||||
if not defined actual goto verify_refuse
|
||||
@@ -103,17 +113,48 @@ echo impeccable: checksum mismatch downloading %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: cannot verify %url% against %url%.sha256; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:check_download
|
||||
set "download_file=%~1"
|
||||
if not defined download_file set "download_file=%cached%.part"
|
||||
if not exist "%download_file%" goto download_missing
|
||||
for %%f in ("%download_file%") do if %%~zf==0 goto download_empty
|
||||
exit /b 0
|
||||
|
||||
:download_missing
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: download completed but the file was removed before execution: %url%; check your antivirus quarantine or logs. Refusing to continue; do not disable protection. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:download_empty
|
||||
del "%download_file%" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: downloaded file is empty: %url%; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:place
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
move /y "%cached%.part" "%cached%" >nul 2>nul
|
||||
if not exist "%cached%" goto fail
|
||||
if errorlevel 1 goto place_failed
|
||||
call :check_download "%cached%"
|
||||
if errorlevel 1 exit /b 127
|
||||
set "run=%cached%"
|
||||
goto run
|
||||
|
||||
:place_failed
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not cache the verified download: %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:run
|
||||
"%run%" %*
|
||||
exit /b
|
||||
|
||||
@@ -7834,7 +7834,6 @@
|
||||
if (editBadgeEl && editBadgeEl.style.display !== 'none') renderEditBadge('idle-disabled');
|
||||
showBar('generating');
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -7916,7 +7915,6 @@
|
||||
showBar('generating');
|
||||
startScrollTracking();
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -8238,7 +8236,8 @@
|
||||
// rasterization from delaying the fetch itself.
|
||||
if (!hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
await sendEvent(basePayload);
|
||||
const created = await sendEvent(basePayload);
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
|
||||
let screenshotPath;
|
||||
@@ -8279,7 +8278,10 @@
|
||||
// is semantic input. Plain requests were already dispatched above.
|
||||
if (hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
const created = await sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
// Capture/upload can take seconds. Progress before this acknowledgment
|
||||
// refers to an unknown session and would clear our own active work.
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,8 @@ Choose the mode from the requested surface, not the product, and persist it only
|
||||
Routing:
|
||||
|
||||
- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command.
|
||||
- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Explicit or clearly implied request to run a command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Workflow or command-selection question:** read [Workflow questions](reference/routing.md#workflow-questions).
|
||||
- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes a new surface or replacement world through init, then new-work; a narrow refinement of existing code proceeds on the incumbent implementation as `impeccable context` directs, offering init afterward rather than blocking on it.
|
||||
- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions.
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ const __impeccableLiveDev =
|
||||
process.env.NODE_ENV === "development" ? ["http://localhost:8400"] : [];
|
||||
```
|
||||
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: `tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts`, `tests/framework-fixtures/sveltekit-csp/expected-after-patch.js`. Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: [nextjs-turborepo/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts), [sveltekit-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/sveltekit-csp/expected-after-patch.js). Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
|
||||
### append-string
|
||||
|
||||
@@ -93,7 +93,7 @@ const __impeccableLiveDev =
|
||||
- `script-src 'self' 'unsafe-inline'` becomes `` `script-src 'self' 'unsafe-inline'${__impeccableLiveDev}` ``
|
||||
- `connect-src 'self'` becomes `` `connect-src 'self'${__impeccableLiveDev}` ``
|
||||
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: `tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js`, `tests/framework-fixtures/nuxt-csp/expected-after-patch.ts`.
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: [nextjs-inline-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js), [nuxt-csp/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nuxt-csp/expected-after-patch.ts).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ Complete HTML replacement of the original element per variant, not a CSS-only pa
|
||||
|
||||
Replace the style opening tag with `cssAuthoring.styleTag` when the tool returns a different one. **Each variant div contains exactly one top-level element**, same tag as the original; loose siblings break outline tracking and accept. First variant visible, all others `display: none`. The browser's MutationObserver accepts atomic or progressive arrival; accepting an arrived variant fences the worker, so later publications are rejected.
|
||||
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in `tests/live-e2e/agent.mjs` is a faithful template.
|
||||
For `styleMode: "scoped"`, author every `:scope` rule with a descendant combinator: the `@scope` boundary is the variant wrapper div, not your element, so a bare `:scope { ... }` styles a `display: contents` shell. Always step in (`:scope > .card`, `:scope .hero-title`). The fake test agent's CSS in the [repo agent template](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/live-e2e/agent.mjs) is a faithful template.
|
||||
|
||||
**JSX / TSX targets:** wrap `<style>` content in a template literal (CSS braces would parse as JSX), use `className=` / `style={{…}}`, keep `data-impeccable-*` attributes as plain strings:
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
# No-argument routing: the context-aware menu
|
||||
# Command guidance
|
||||
|
||||
## Workflow questions
|
||||
|
||||
Give advice without executing commands; the menu below is only for bare invocations. Consult relevant command references as needed for prerequisites and scope. Link to the [docs](https://impeccable.style/docs/) for the broader workflow guide. If the user also requests execution, follow that request.
|
||||
|
||||
## No-argument routing: the context-aware menu
|
||||
|
||||
Read this when the user invokes `/impeccable` with no argument. They are asking "what should I do?" Make the menu context-aware instead of static.
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.0
|
||||
0.1.1
|
||||
|
||||
@@ -96,6 +96,19 @@ fetch_url() {
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
check_download() {
|
||||
download_file=${1:-$tmp}
|
||||
if [ ! -f "$download_file" ]; then
|
||||
rm -f "$tmp.sha256"
|
||||
echo "impeccable: download completed but the file was removed before execution: $url; check your antivirus quarantine or logs. Refusing to continue; do not disable protection." >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ ! -s "$download_file" ]; then
|
||||
rm -f "$download_file" "$tmp.sha256"
|
||||
echo "impeccable: downloaded file is empty: $url; refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
}
|
||||
if [ -n "$probing" ]; then
|
||||
# Inside another launcher's probe: no download, fail fast and quiet.
|
||||
exit 127
|
||||
@@ -116,6 +129,7 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
fetch_url "$url" && fetched=1
|
||||
fi
|
||||
if [ "$fetched" = 1 ]; then
|
||||
check_download
|
||||
# Fail closed: a freshly downloaded binary runs only after verifying
|
||||
# against its .sha256 sidecar. A sidecar that cannot be fetched, or a
|
||||
# machine with no sha256 tool, refuses the download instead of exec'ing
|
||||
@@ -127,15 +141,20 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -q -O "$tmp.sha256" "$url.sha256" 2>/dev/null && sidecar_ok=1
|
||||
fi
|
||||
check_download
|
||||
expected=""
|
||||
[ "$sidecar_ok" = 1 ] && expected=$(cut -d' ' -f1 < "$tmp.sha256")
|
||||
actual=""
|
||||
if command -v shasum >/dev/null 2>&1; then actual=$(shasum -a 256 "$tmp" | cut -d' ' -f1)
|
||||
elif command -v sha256sum >/dev/null 2>&1; then actual=$(sha256sum "$tmp" | cut -d' ' -f1); fi
|
||||
if command -v shasum >/dev/null 2>&1; then
|
||||
if digest=$(shasum -a 256 "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
elif command -v sha256sum >/dev/null 2>&1; then
|
||||
if digest=$(sha256sum "$tmp" 2>/dev/null); then actual=${digest%% *}; fi
|
||||
fi
|
||||
check_download
|
||||
rm -f "$tmp.sha256"
|
||||
if [ -z "$expected" ] || [ -z "$actual" ]; then
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or no sha256 tool); refusing the unverified download" >&2
|
||||
echo "impeccable: cannot verify $url against $url.sha256 (sidecar unavailable or hashing failed); refusing the unverified download" >&2
|
||||
exit 127
|
||||
fi
|
||||
if [ "$actual" != "$expected" ]; then
|
||||
@@ -143,8 +162,22 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
echo "impeccable: checksum mismatch downloading $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
chmod +x "$tmp" 2>/dev/null
|
||||
mv -f "$tmp" "$cached" && exec "$cached" "$@"
|
||||
check_download
|
||||
if ! chmod +x "$tmp" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not make the verified download executable: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download
|
||||
if ! mv -f "$tmp" "$cached" 2>/dev/null; then
|
||||
check_download
|
||||
rm -f "$tmp"
|
||||
echo "impeccable: could not cache the verified download: $url" >&2
|
||||
exit 127
|
||||
fi
|
||||
check_download "$cached"
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
fi
|
||||
|
||||
@@ -82,6 +82,8 @@ curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
|
||||
:verify
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
rem Mirrors the sh launcher and fails closed: a freshly downloaded binary
|
||||
rem runs only after verifying against its .sha256 sidecar. A sidecar that
|
||||
rem cannot be fetched, or an empty certutil result, refuses the download
|
||||
@@ -91,8 +93,16 @@ if errorlevel 1 goto verify_refuse
|
||||
set "expected="
|
||||
set /p expected=<"%cached%.sha256"
|
||||
for /f "tokens=1" %%h in ("%expected%") do set "expected=%%h"
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
set "actual="
|
||||
for /f "skip=1 delims=" %%h in ('certutil -hashfile "%cached%.part" SHA256 2^>nul') do if not defined actual set "actual=%%h"
|
||||
rem Reuse the sidecar staging file after reading expected. Check certutil's
|
||||
rem status before parsing: its error text on stdout is not a digest.
|
||||
certutil -hashfile "%cached%.part" SHA256 >"%cached%.sha256" 2>nul
|
||||
if errorlevel 1 goto verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
for /f "usebackq skip=1 delims=" %%h in ("%cached%.sha256") do if not defined actual set "actual=%%h"
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
if not defined expected goto verify_refuse
|
||||
if not defined actual goto verify_refuse
|
||||
@@ -103,17 +113,48 @@ echo impeccable: checksum mismatch downloading %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:verify_refuse
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: cannot verify %url% against %url%.sha256; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:check_download
|
||||
set "download_file=%~1"
|
||||
if not defined download_file set "download_file=%cached%.part"
|
||||
if not exist "%download_file%" goto download_missing
|
||||
for %%f in ("%download_file%") do if %%~zf==0 goto download_empty
|
||||
exit /b 0
|
||||
|
||||
:download_missing
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: download completed but the file was removed before execution: %url%; check your antivirus quarantine or logs. Refusing to continue; do not disable protection. 1>&2
|
||||
exit /b 127
|
||||
|
||||
:download_empty
|
||||
del "%download_file%" >nul 2>nul
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
echo impeccable: downloaded file is empty: %url%; refusing the unverified download 1>&2
|
||||
exit /b 127
|
||||
|
||||
:place
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
move /y "%cached%.part" "%cached%" >nul 2>nul
|
||||
if not exist "%cached%" goto fail
|
||||
if errorlevel 1 goto place_failed
|
||||
call :check_download "%cached%"
|
||||
if errorlevel 1 exit /b 127
|
||||
set "run=%cached%"
|
||||
goto run
|
||||
|
||||
:place_failed
|
||||
call :check_download
|
||||
if errorlevel 1 exit /b 127
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: could not cache the verified download: %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:run
|
||||
"%run%" %*
|
||||
exit /b
|
||||
|
||||
@@ -7834,7 +7834,6 @@
|
||||
if (editBadgeEl && editBadgeEl.style.display !== 'none') renderEditBadge('idle-disabled');
|
||||
showBar('generating');
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -7916,7 +7915,6 @@
|
||||
showBar('generating');
|
||||
startScrollTracking();
|
||||
saveSession();
|
||||
sendCheckpoint('generate_started');
|
||||
writeScrollY(window.scrollY);
|
||||
if (variantObserver) variantObserver.disconnect();
|
||||
variantObserver = startVariantObserver(currentSessionId);
|
||||
@@ -8238,7 +8236,8 @@
|
||||
// rasterization from delaying the fetch itself.
|
||||
if (!hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
await sendEvent(basePayload);
|
||||
const created = await sendEvent(basePayload);
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
|
||||
let screenshotPath;
|
||||
@@ -8279,7 +8278,10 @@
|
||||
// is semantic input. Plain requests were already dispatched above.
|
||||
if (hasAnnotations) {
|
||||
basePayload.clientSentAt = Date.now();
|
||||
sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
const created = await sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
|
||||
// Capture/upload can take seconds. Progress before this acknowledgment
|
||||
// refers to an unknown session and would clear our own active work.
|
||||
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,8 @@ Choose the mode from the requested surface, not the product, and persist it only
|
||||
Routing:
|
||||
|
||||
- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command.
|
||||
- **Explicit or clearly implied command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Explicit or clearly implied request to run a command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit.
|
||||
- **Workflow or command-selection question:** read [Workflow questions](reference/routing.md#workflow-questions).
|
||||
- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes a new surface or replacement world through init, then new-work; a narrow refinement of existing code proceeds on the incumbent implementation as `impeccable context` directs, offering init afterward rather than blocking on it.
|
||||
- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions.
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ const __impeccableLiveDev =
|
||||
process.env.NODE_ENV === "development" ? ["http://localhost:8400"] : [];
|
||||
```
|
||||
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: `tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts`, `tests/framework-fixtures/sveltekit-csp/expected-after-patch.js`. Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
Per-framework: Next.js + monorepo helper: edit the *app's* `next.config.*` (not the shared helper), appending to `additionalScriptSrc` / `additionalConnectSrc`. SvelteKit: `svelte.config.js`, `kit.csp.directives['script-src']` and `['connect-src']`. Nuxt + nuxt-security: `nuxt.config.*`, `security.headers.contentSecurityPolicy['script-src']` and `['connect-src']`. Reference outputs: [nextjs-turborepo/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-turborepo/expected-after-patch.ts), [sveltekit-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/sveltekit-csp/expected-after-patch.js). Idempotency: if `__impeccableLiveDev` already exists in the file, the patch is applied; just mark `cspChecked: true`.
|
||||
|
||||
### append-string
|
||||
|
||||
@@ -93,7 +93,7 @@ const __impeccableLiveDev =
|
||||
- `script-src 'self' 'unsafe-inline'` becomes `` `script-src 'self' 'unsafe-inline'${__impeccableLiveDev}` ``
|
||||
- `connect-src 'self'` becomes `` `connect-src 'self'${__impeccableLiveDev}` ``
|
||||
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: `tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js`, `tests/framework-fixtures/nuxt-csp/expected-after-patch.ts`.
|
||||
Per-framework: Next.js inline `headers()` in `next.config.*`; Nuxt `routeRules['/**'].headers['Content-Security-Policy']` in `nuxt.config.*`. Reference outputs: [nextjs-inline-csp/expected-after-patch.js](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nextjs-inline-csp/expected-after-patch.js), [nuxt-csp/expected-after-patch.ts](https://github.com/pbakaus/impeccable/blob/8dac6ae7e020c43ab10ce9b41939f6fd42627b96/tests/framework-fixtures/nuxt-csp/expected-after-patch.ts).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user