Commit Graph
67 Commits
Author SHA1 Message Date
67d018fe05 Fix: print JSON on live-poll --reply success (#800)
Successful --reply was exit 0 with empty stdout, so agents could not tell delivery from a hang. Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 08:47:39 +05:00
3bdb9ff06c Fix: drop stale carbonize diagnostic on complete (#801)
Complete and discarded snapshots no longer keep carbonize_cleanup_required after cleanup is done.

AI assistance: Cursor Grok 4.6.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 08:47:16 +05:00
7d6c5bdd92 Fix: keep UNC prefix when normalizing hook paths (#784)
Collapsing every separator run turned \\server\share into /server/share, so doctor would probe the wrong file. Leading // after a quote is left intact.

Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 05:10:10 +05:00
73552b79c5 Fix: user-scope Windows hook group duplication (#784)
JSON-quoted absolute paths doubled backslashes, so merge failed to recognize the group it had just written and appended another copy on every update.

Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 05:10:10 +05:00
00095adb26 Fix: skipScan must cover the visual contrast stage too
Bugbot on PR #665: the skipScan guard emptied only the analytic
collectBrowserFindings pass, and scan()'s detached visual-contrast
stage then repopulated an ignoreFiles-waived page with contrast
markers and a second non-zero results post. Hoist the guard into
skipScanActive() and honor it in scan() and the async collector;
regenerate the browser bundle.

Adds a browser-backed regression test that reproduces the leak
(second results post carrying low-contrast findings) and pins the
zero contract; drops a tautological assert flagged in review.

AI-assisted change: implemented with Claude Code under maintainer
direction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 18:38:36 +05:00
46f13989eb Fix: write the build-path flip before answering the POST
serve-question answered POST /build-path with 200 and only then wrote the
flip file. The caller is a separate process, so the response could reach it
while the server was still preempted before the write landed: a poller that
trusted the 200 could look for the flip file and miss it.

Measured on a loaded machine, the old order lost that race 29 times out of
40; writing first and answering after loses it 0 times out of 40. This is
what made tests/serve-question.test.mjs fail intermittently in CI on the
Node 22 job while passing on Node 24.

AI-assisted change: diagnosed and implemented with Claude Code under
maintainer direction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 18:38:36 +05:00
152d6940b0 Fix: harden live overlay detector waivers (#639 follow-up)
Read waiver config from every live root (appRoot, contextRoot, repoRoot),
so monorepo projects whose config lives at the repo root reach the
overlay; serialize served roots and page identities repo-relative there.

Resolve each page URL to its actual serving file via the inject config's
resolved page list before applying file-scoped waivers; ambiguous URLs
keep the conservative common-ancestor fallback (PR #645 review
discussion r3840011436).

Honour detector.ignoreFiles: a wholly waived page now scans to zero
findings in the overlay, matching the CLI and the edit hook.

Guard the resolver call so a throwing resolver degrades to an unfiltered
scan instead of breaking the detect toggle.

Match design-system-color waivers by color value across hex and rgb()
spellings, and stop extracting font values for bounce-easing findings,
mirroring extractFindingIgnoreValue. Regenerate the browser bundle.

AI-assisted change: reviewed, planned, and implemented with Claude Code
under maintainer direction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 18:38:36 +05:00
Abdul WahabandClaude Opus 5 f379c4c76f COMP-FIDELITY: ninth sweep (sol, artifact fix confirmed) and tenth sweep (opus confirmation on the rebased branch)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 14:59:06 +05:00
377fb112b0 Pass the session key from detached idle-grace tests
Main's #469 tests POSTed /heartbeat and /answer without ?key=, which the
gate now rejects, so those daemons looked dead. The e2e heartbeat counter
also has to match pathname rather than a suffix, now that the URL carries
the key.

Written with AI assistance under maintainer direction.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 05:53:25 +05:00
7982002dac Allow bare loopback Host/Origin on port 80, where browsers omit the suffix
Bugbot caught that the exact-match allowlists 403 every request on --port 80
because browsers drop the default-port suffix; other ports stay strict.

Written with AI assistance under maintainer direction.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 05:53:25 +05:00
2e075dc58c Gate the build-path flip behind the same session key and origin checks
An unauthenticated POST /build-path wrote the flip event that makes --wait
instruct the agent to generate comps: same class as the /answer hole in #555.

Written with AI assistance under maintainer direction.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 05:53:25 +05:00
eaaecbd1fe Fix: require session key and origin/host checks on serve-question POSTs (#555)
Unauthenticated POST /answer copied steer into the agent ANSWER line. The handler now requires the detached session key and rejects foreign Origin and Host.

Written with AI assistance under maintainer direction.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 05:53:25 +05:00
d690349db1 Fix: keep URL basic-auth credentials on the scan origin (#657)
page.authenticate is page-wide, so a cross-origin redirect that then 401s would receive the original credentials. Attach Authorization only to requests for the scan origin.

Written with AI assistance (Cursor); reviewed by maintainer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 05:53:01 +05:00
d5873ff8eb Fix: redact URL userinfo from detect findings (#657)
Strip basic-auth credentials from scan-target URLs before goto and finding output, and pass them to page.authenticate instead.

Written with AI assistance (Cursor); reviewed by maintainer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 05:53:01 +05:00
be87f5eb86 Fix: refuse inert exact ignore-value entries (#662)
ignore-value stored exact values for rules that cannot extract one, so the entries never matched. Refuse them and point at "*" --file.

AI assistance: implemented with Cursor Grok 4.6.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 05:43:36 +05:00
af2e8b3ac3 Fix: stream bundle downloads to disk instead of buffering
AI assistance: implemented with Cursor Grok 4.6.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 05:43:04 +05:00
5d932f9fbe Fix: safe temp staging and downloadFile error handling (#479)
AI assistance: implemented with Cursor Grok 4.6.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 05:43:04 +05:00
63b04e2530 Release: CLI v3.6.1, extension v1.3.3
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-26 11:02:54 +05:00
380cfcb08e Release: skill v4.1.2
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-26 11:02:54 +05:00
Abdul WahabandCursor daae1d4117 Fix: reject root-relative .. segments and warn per scan
Dot-segment hrefs like /../outside.css could leave the project, and a process-wide warning set hid missing-sheet notices on later detectHtml calls.

AI assistance: implemented with Cursor Grok 4.6.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 19:47:54 +05:00
Abdul WahabandCursor 2b88aa5231 Fix: resolve root-relative linked stylesheets in static detect (#652)
Root-relative hrefs like /static/app.css were treated as OS-absolute and silently dropped, hiding contrast findings.

AI assistance: implemented with Cursor Grok 4.6.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 19:34:55 +05:00
Abdul WahabandCursor 47e411952b Fix: own nested workspace packages and honor projectRoots first (#570)
packages/* now includes nested package.json dirs under a matched
workspace package, and Impeccable projectRoots govern a path even when
package-manager workspaces exclude it.

Written with AI assistance (Cursor); reviewed by maintainer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 08:03:16 +05:00
Abdul WahabandCursor 6bea544a0a Fix: drain context stdout before process.exit (#573)
process.exit after a queued write truncated boot output on a backpressured pipe. Await the write callback, then close the fetch dispatcher.

AI assistance: implemented with Cursor Grok 4.6.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 07:51:47 +05:00
Abdul WahabandCursor 5d7c1cce34 Fix: inherit DESIGN.md only from a monorepo root that owns the path (#570)
findDesignRoot continued past every workspace package.json to any
workspace-declaring ancestor. It now matches the boundary against that
ancestor's globs (including negations and globstars), so excluded and
stray packages do not inherit, while included workspaces still do.

Written with AI assistance (Cursor); reviewed by maintainer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 07:47:16 +05:00
Abdul WahabandCursor 2ef8e43d1e Fix: close fetch sockets before context helper exit (#573)
On Windows/Node 24, a live undici keep-alive from the update-check fetch aborted libuv during teardown after valid stdout. Destroy the dispatcher first, matching concept-seed.

AI assistance: implemented with Cursor Grok 4.6.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 07:31:18 +05:00
Abdul Wahab 043e8a5bfd Merge origin/main into fix/570-monorepo-design-root 2026-08-25 06:58:14 +05:00
Abdul Wahab 21510c3632 Docs: Link harness trust guidance
Point installer readers to the harness-specific approval and verification steps on impeccable.style.\n\nAI-assisted: Codex prepared and verified this documentation update under direct maintainer instruction.
2026-08-24 08:00:38 +05:00
Abdul WahabandCursor 2064b0696f Merge origin/main into fix/603-codex-stop-payload
Keep Codex as its own Stop harness (decision/block) while taking main's Grok envelope detection and Stop cache sync.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-24 07:25:47 +05:00
Abdul WahabandClaude Fable 5 bfe634e254 Trim the Grok normalizer to the fields the hook reads
The hook_event_name mapping (and its pre_tool_use entry) had no reader:
hook.mjs routes on the raw stdin via isStopEvent, and nothing downstream
keys on the normalized event name. The trailing-slash strip duplicated
the path.resolve every consumer already does. Pin the one commit-2
behavior that had no test: a Stop detector failure must leave the
remembered set alone.

Prepared with AI assistance.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 06:52:52 +05:00
Abdul WahabandCursor 3c442af7ad Fix: sync Stop finding cache after a clean Grok scan
A clean Stop never replaced remembered keys, so a finding that was fixed and then reintroduced stayed silent. Remember the live scan, including empty, and persist that write.

Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-24 06:23:45 +05:00
Abdul WahabandCursor 35ae07339b Fix: parse Grok Build camelCase hook stdin (#646)
Grok was classified as GitHub Copilot, so the design hook skipped every
edit with no-file-path and never ran Stop. Normalize toolInput/sessionId
and treat Stop additionalContext as the Grok product.

Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-24 05:28:21 +05:00
Abdul WahabandCursor c9e7cd8a64 Fix: emit Codex Stop hook as decision/block (#603)
Codex Stop rejects Claude's hookSpecificOutput shape. Detect Codex from
turn_id at runtime and emit { decision: "block", reason } so existing
installs keep working without rewriting hook commands.

AI-assisted change, prepared with Cursor Grok under maintainer direction.

Fixes #603
Fixes #643

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-24 05:23:51 +05:00
Abdul WahabandCursor 49571365a8 Fix: rewrite Grok project hooks to the global skill path (#642)
Grok was skipped by the hook-command rewrite, so a global skill install left .grok/hooks/impeccable.json pointing at a project-relative hook.mjs that does not exist.

AI assistance: Cursor Grok 4.6 implemented this change.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-24 04:27:01 +05:00
Abdul WahabandCursor 9a7d0fbc50 Fix: skip regex literals in Astro fences and url() protocol-relative slashes
Quote-bearing regexes made the frontmatter closer miss the closing ---, and url(//…) plus interpolations were treated as SCSS line comments that hid live font-family. Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-22 07:13:18 +05:00
Abdul WahabandCursor ba873f7599 Fix: blank preprocessor line comments inside component style blocks
Standalone SCSS/Sass/Less files already ignored // comments, but <style lang="scss"> in Astro/Vue/Svelte still scanned them as live CSS. Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-22 07:02:44 +05:00
Abdul WahabandCursor 7ddcd533a4 Test: pin 1D grid-background pass cases in the fixture suite
The unit suite already covered dashed rules; this adds an isolated HTML fixture so the page-level one-finding cap cannot hide a regression.

Prepared with AI assistance (Cursor agent), directed by @abdulwahabone.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-22 06:58:36 +05:00
Abdul WahabandCursor 7426af446e Fix: give the color-mix hex fixture explicit pixel size
jsdom does no layout; Greptile asked for width/height on .mix-hex-brand so the static fixture stays deterministic. Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-22 06:54:26 +05:00
Abdul WahabandCursor a236137bc6 Fix: stop flagging 1D dashed rules as grid backgrounds (#615)
codex-grid-background treated any 2D px background-size as a grid, so a single hairline tiled as a dash or rail false-positived. A finding now requires two hairline gradients plus a px tile.

Prepared with AI assistance (Cursor agent), directed by @abdulwahabone.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-22 06:51:25 +05:00
Abdul WahabandCursor ddb609936a Fix: keep comment blanking out of script strings, preprocessor //, and Astro fences
Naive HTML/CSS comment regexes were swallowing live markup between script-string delimiters, SCSS/Sass/Less line comments still reached the matchers, and indexOf treated --- inside a frontmatter template literal as the closing fence. Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-22 06:49:20 +05:00
Abdul WahabandCursor 5444031942 Fix: skip hex nested in color-mix when measuring gradient contrast (#578)
parseGradientColors treated #000 inside color-mix() as a stop, so low-contrast scored text against phantom black. Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-22 06:47:29 +05:00
Abdul WahabandCursor 067665cc7e Fix: strip comments in markup and stylesheets before regex matchers (#589)
detectText only blanked comments for JS extensions, so broken-image still fired on <img> inside Astro/Vue/Svelte comments, CSS comments, and extracted style blocks. Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-22 06:34:04 +05:00
Abdul WahabandCursor 869c887372 Test: cover directory, chained, and relative /source symlink escapes (#618)
AI assistance: Cursor Grok 4.6 implemented this change.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-22 05:51:12 +05:00
Abdul WahabandCursor 8347d77f54 Test: give the oklch neon fixture flag and pass columns (#592)
The neon-text path is browser-only, so the matrix lives in the Puppeteer suite rather than the static fixture runner.

AI-assisted (Cursor agent).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-22 05:33:17 +05:00
Abdul WahabandCursor bda7411acd Fix: strip page-controlled poller fields before they reach the agent (#488)
A page-supplied _instructions suppressed the locally generated next step and was presented as authoritative over live.md. Drop reserved poller-owned fields at ingest and always overwrite them locally.

AI assistance: implemented with Cursor Grok 4.6.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-22 05:31:05 +05:00
Abdul WahabandCursor 1b7da15b56 Fix: parse oklch in visual-contrast and neon-text (#592)
Bare parseRgb() dropped Tailwind v4 computed colors, so contrast sampling skipped and neon-text never fired.

AI-assisted (Cursor agent).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-22 05:25:50 +05:00
Abdul WahabandCursor d008dd98c3 Fix: stop live-server /source from following symlinks out of the workspace (#618)
AI assistance: Cursor Grok 4.6 implemented this change.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-22 05:19:36 +05:00
Abdul WahabandCursor e9c62278c1 Make the code-led GROUND fallback deterministic, compare like for like
The quality bar leaves the color-authority chain (it arrives as card
image paths and never governs composition). With no comp, a color
OWN-WORLD names is the target; when it names none, the review states
there is no GROUND authority instead of inventing a target. The build
side of the numeric comparison now samples the same way each record
was taken: patch average against patch average, gradient ends against
gradient ends.

AI-assisted change (Cursor), prepared under maintainer direction.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-16 13:39:57 +05:00
Abdul WahabandCursor 79c648a9ab Resolve bot review: code-led GROUND authority, sampling rules, tolerance
GROUND no longer lapses silently on code-led builds: with no comp to
sample, the authority is the colors OWN-WORLD and the quality bar name,
and no invented target beyond them. Non-uniform fields get sampling
rules (interior pixel, patch average for texture, both ends of a
gradient, never an edge), and the numeric comparison gets tolerance
semantics so render noise never fails a faithful build. The hunt hint
names the dark-ground prior beside the light one.

AI-assisted change (Cursor), prepared under maintainer direction.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-16 12:50:46 +05:00
Abdul WahabandCursor e975bec412 Harden monorepo design-root recognition and the home-directory stop (#570)
Read all four workspace-glob sources context.mjs reads (.impeccable
projectRoots, package.json workspaces, lerna packages, pnpm packages),
so lerna-glob roots and impeccable projectRoots no longer hit the same
abstention. Compare the walk against both the logical and realpath
forms of the home directory: on distros that symlink home paths
(/home to /var/home) the string comparison never matched, and the
post-boundary walk could inherit a workspace-declaring home's
DESIGN.md.

Written with AI assistance (Cursor); reviewed by maintainer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-15 01:07:52 +05:00
Abdul WahabandCursor 91f2c7b47e Fix: strip inline YAML comments when reading pnpm workspace globs (#570)
An inline comment on a pnpm-workspace.yaml packages line defeated the
end-anchored flow-list regex and the block-list state switch, so
workspaces outside apps/ or packages/ went unrecognized. Reuses the
engine's existing stripInlineYamlComment, matching context.mjs.

Written with AI assistance (Cursor); reviewed by maintainer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 23:47:59 +05:00
Abdul WahabandCursor dca8f1ca6f Fix: inherit the monorepo root's DESIGN.md in detect design-system rules (#570)
findDesignRoot stopped at the first package.json boundary, so every
design-system rule silently abstained for files inside monorepo
workspaces. The walk now continues past a workspace boundary to the
monorepo root that owns it, recognized the same way context.mjs does
(declared workspace globs, or a marker file beside apps/ or packages/
children). A nested repo with its own .git, a workspace-owned
DESIGN.md, and non-monorepo projects keep their existing behavior.

Written with AI assistance (Cursor); reviewed by maintainer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 23:41:29 +05:00
Abdul WahabandCursor d40274c47d Remove evidence images from the diff; they live on the pr-evidence branch
AI-assisted change (Cursor), prepared under maintainer direction.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 22:14:18 +05:00
Abdul WahabandCursor 9213bf1511 Generalize color sampling beyond the cream-ground case
Accents join the sampled record alongside ground and dominant fields,
every recorded color (not only the ground) is compared by number
during the build, and the light-ground-only rationale clauses become
value-neutral so dark and saturated comps get the same protection.
Rule anchor renamed to skill-color-by-number to match its scope.

AI-assisted change (Cursor), prepared under maintainer direction.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 21:26:08 +05:00
Abdul WahabandCursor 8230426df8 Add PR evidence images (cursor-control-8 ground drift)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:27:19 +05:00
Abdul WahabandCursor 5b7c9e93cb Fix uncaught ground-color drift on comp-led builds
Sample the approved comp's ground and dominant-field hexes into the
brief (visualize.md), judge the built page's ground by number against
that record including the net value under textures (new-work.md), and
make GROUND a mandatory fidelity-matrix row beside TYPE and MATERIAL
(finish reviewer). Pre-comp palette chips are retired at approval.

AI-assisted change (Cursor), prepared under maintainer direction.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:27:19 +05:00
Abdul WahabandCursor d23fa1c882 Fix: layout-transition false positives on JSX quoted transition values (#548)
The value-capture regex stopped only at ;{}, so in single-line JSX
style objects it ran past the closing quote and swallowed later
properties, flagging layout props that were never transitioned. The
capture now stops at the matching closing quote when the value is a
quoted string, falling back to the old bounds for real CSS.

Prepared with AI assistance under maintainer direction.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-10 14:00:55 +05:00
Abdul WahabandCursor 520a55547e Admit one brace level inside shadow interpolations
Review finding on #553: an object-literal argument like
${getOffset({ size: 2 })} ended the interpolation match at the inner
closing brace, losing the shadow context. Interpolations now admit one
level of braces (with paired quotes inside); the shared subpattern is
hoisted into compiled constants. Deeper nesting stays fail-safe by
design: a line-scoped regex cannot balance arbitrary braces, and the
miss produces a waivable finding, never a leak.

AI-assisted (Cursor agent), reviewed by maintainer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-10 13:28:11 +05:00
Abdul WahabandCursor 82234515e0 Admit paired quoted strings inside shadow interpolations
Review finding on #553: the interpolation subpattern excluded quotes,
so a documented shadow color after ${getShadow('lg')} or a quoted
ternary branch lost its context and fired as drift. Interpolations now
admit complete single/double-quoted strings; the quotes pair up inside
the ${...}, so an unpaired quote or the template's closing backtick
still ends the context and the allowance cannot leak to a later
property.

AI-assisted (Cursor agent), reviewed by maintainer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-10 13:18:10 +05:00
Abdul WahabandCursor 94e957d7fc Keep shadow context across template interpolations
Review finding on #553: the end-anchored shadow-context tails excluded
`}` (JS) and `{`/`}` (CSS), so a documented shadow color after a ${...}
interpolation in a boxShadow template literal or a CSS-in-JS
box-shadow line lost its allowance and fired as drift. Both tails now
admit complete ${...} interpolations; a bare `}`, quote, or `;` still
ends the context, so the allowance cannot leak past a template's
closing backtick into a later property.

AI-assisted (Cursor agent), reviewed by maintainer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-10 13:05:24 +05:00
Abdul WahabandCursor 92c857a9ef Allow documented sidecar shadow colors in shadow contexts (#547)
The detector never read the sidecar's extensions.shadows, and the only
workaround (a colors entry for black) allowlisted every black at every
alpha because colorKey() drops alpha. Shadow token colors now live in a
separate allowlist matched on alpha as well as r/g/b, and the allowance
applies only inside box-shadow / text-shadow values, so a documented
shadow black still fires as a page ground.

AI-assisted (Cursor agent), reviewed by maintainer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-10 12:39:17 +05:00
Abdul WahabandCursor 7d6109b723 Drop deprecated craft alias from the generated argument-hint
craft is a deprecated compatibility alias, but its SKILL_CATEGORIES entry
kept it advertised in every generated SKILL.md argument-hint. Unmapping it
removes it from the hint while the alias keeps routing through the router
table and command-metadata.json.

AI-assisted change (reviewed by maintainer).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 10:36:18 +05:00
fd9076f4f0 Enforce the engines floor in the probe instead of a capability check
The probe asked whether node could load ESM, while the notice promised a
Node 22 floor and package.json engines declares >=22.12.0. Reviewers kept
flagging the gap, and they were right to: a 14.18-to-21 runtime passed the
probe on the strength of one import while the hook and its detector bundle
are only ever exercised on the engines floor, so "can load our code" was a
weaker claim than the one being made for it.

Check the floor directly: parseInt(process.versions.node) >= 22, in
ES5-only syntax that parses on any node old enough to fail it. Probe and
notice now derive from one NODE_MAJOR_FLOOR constant, so they cannot
disagree, and the archaeology about node: scheme support and pre-15
unhandled-rejection semantics goes with the import it explained.

Add the missing contract test: every generated hook command carries the
probe, the notice appears exactly where a harness can render it (Claude
and Codex, project and plugin), and the expected floor is read from
package.json engines rather than repeated by hand.

Verified against a fake pre-22 node, no node, and a real node: one notice
then the marker holds it silent, exit 0 in every failure shape, and the
hook's own exit code still passes through on a supported runtime.

Co-Authored-By: Claude Fable 5 (via Cursor) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-26 22:17:19 +05:00
Abdul WahabandClaude Opus 5 86cdf528c5 Probe the import the hook actually uses, and fail closed on rejection
Greptile flagged that the probe does not enforce the Node 22 engines floor.
Two parts to that, and they land differently.

The real defect is narrower and worse than stated: the hook closure imports
`node:fs`, `node:os`, `node:path` and `node:url`, and the `node:` scheme needs
14.18, so a bare `import('fs')` probe passed on 12 and 13 and those runtimes
then died on the real import, which is the banner this branch exists to remove.
Probing `node:fs` closes that. The added `.catch(()=>process.exit(1))` is load
bearing rather than tidiness: before Node 15 an unhandled rejection is only a
warning and the process still exits 0, so a rejected probe would have read as a
pass on exactly the versions in question.

Not enforcing 22 is deliberate and stays. The probe asks whether this runtime
can load our code, not whether it is a supported one, so a 14.18-to-21 runtime
that works today keeps working rather than being silently switched off. The
notice names 22 because that is the version worth installing, and it only ever
reaches someone whose runtime already failed the probe, so no user is shown a
threshold that contradicts what ran.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 21:20:26 +05:00
Abdul WahabandClaude Opus 5 4f999ceff8 Give Codex the notice too; its hook reference documents systemMessage
Commit 8397d532 took a reviewer's word that Codex expects hookSpecificOutput
and dropped its notice on that basis. Codex documents `systemMessage` for
PostToolUse and Stop as text shown as a warning in the UI or event stream,
the same field Claude Code reads, so the notice belongs there and the earlier
comment asserted something unverified.

Checked the rest against their own references while here. Cursor's preToolUse
output is permission-shaped and its user_message renders only when the action
is DENIED, so warning would mean blocking the edit. Grok treats PostToolUse
and Stop as passive events and ignores stdout outright. Copilot's contract is
unconfirmed. Those three keep the probe alone, which is a verified limit now
rather than an assumption.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 21:10:41 +05:00
Abdul WahabandClaude Opus 5 0c19098754 Guard the remaining harness manifests against a dead node runtime
Bugbot caught the Codex plugin builder still invoking node directly, and the
same reasoning covers GitHub Copilot and Grok Build: all three shipped the
exact failure this branch exists to stop, and sat visibly inconsistent with
their guarded siblings.

Route them through guardedNode with no notice, matching Codex and Cursor.
GitHub gains a second property from it: outside a git repository
`$(git rev-parse --show-toplevel)` expands to nothing, so the old command
handed node a path that could not exist and failed the turn. The file test
now short-circuits that to exit 0.

Every builder carries the probe; only the two Claude manifests carry the
notice, which is the only harness whose response shape is confirmed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 20:58:49 +05:00
Abdul WahabandClaude Opus 5 8397d532b9 Keep the unsupported-node notice to the harness that can render it
`systemMessage` on stdout is a Claude Code contract. The shared guard was
emitting it for Codex and Cursor too, where what a harness does with stdout
it did not ask for is unconfirmed, and a Cursor preToolUse hook printing an
unexpected JSON object is the wrong thing to guess about.

Pass the notice in per harness instead of baking it into the guard. Claude
manifests opt in; Codex and Cursor take the runtime probe alone, so an
unsupported runtime stays as quiet there as it was before the probe existed.
Giving them their own shape later is one more argument at the call site.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 13:37:09 +05:00
Abdul WahabandClaude Opus 5 0db59088ff Stop the design hook erroring on a node too old for ESM
The hook command invokes bare `node`. When that node predates ESM,
`hook.mjs` dies while it is still being parsed, before the script's own
always-exit-0 contract can run, so node exits 1 and the harness reports a
hook error on every Stop and every edit.

Probe the runtime in the command string before invoking the hook, and
route the Claude plugin manifest through the guard that already covered
the project-local manifests. On probe failure the command exits 0 and
emits a one-time `systemMessage` naming the two fixes available to the
user, since nothing written in ESM can report this condition.

Fixes #410.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 12:46:11 +05:00