Add wrap CLI helper and optimize agent generation loop

Three optimizations to cut the generate loop from ~40s to ~15-20s:

1. wrap CLI helper (src/live/wrap.mjs): finds an element in source
   by ID, class names, or tag+class combo, wraps it in the variant
   container with original snapshot, and returns the file path + insert
   line. Replaces 3-4 agent tool calls (grep + read + edit) with one.

   Supports --element-id, --classes (comma-separated), --tag, --query
   (fallback). Searches in priority order: ID > class combo > single
   class > raw text. Auto-detects comment syntax (HTML vs JSX).

2. Batch variant writes: skill reference updated to instruct the agent
   to write ALL variants in a single file edit instead of one per
   variant. Saves N-1 tool call round-trips (~3-5s each).

3. Page URL in generate event: browser now includes location.pathname
   so the agent can map URL to source file directly (/ = index.html,
   /about = about.tsx, etc.) without grepping.

Net effect: agent flow is now 4 tool calls (wrap + edit + read-variant
+ poll-reply) instead of 8+ (grep + read + create-wrapper + N edits
+ poll-reply).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paul Bakaus
2026-04-12 18:32:51 -07:00
co-authored by Claude Opus 4.6
parent 26e0b8a786
commit 4535525f8e
15 changed files with 748 additions and 588 deletions
+37 -49
View File
@@ -76,80 +76,68 @@ END LOOP
## Handle Generate
The event contains: `{id, action, freeformPrompt, count, element}`.
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
### Step 1: Find the source file
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
### Step 1: Wrap the element (one CLI call)
### Step 2: Create the variant wrapper
Use the `wrap` helper to find the element and create the variant container:
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
**HTML / Vue / Svelte:**
```html
<!-- impeccable-variants-start SESSION_ID -->
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
<div data-impeccable-variant="original" style="display: none">
<!-- move the original element here -->
</div>
</div>
<!-- impeccable-variants-end SESSION_ID -->
```bash
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
```
**JSX / TSX:**
```jsx
{/* impeccable-variants-start SESSION_ID */}
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
<div data-impeccable-variant="original" style={{display: 'none'}}>
{/* move the original element here */}
</div>
</div>
{/* impeccable-variants-end SESSION_ID */}
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
The command outputs JSON with the file path and the insert line:
```json
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
```
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
If `wrap` fails, fall back to manual grep + edit.
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
### Step 3: Generate variants one by one
For each variant (1 through COUNT):
### Step 2: Generate all variants and write them in a SINGLE edit
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
5. **Write the variant** into the wrapper in the source file:
```html
<div data-impeccable-variant="N" style="display: none">
<!-- variant N content -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default).
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
6. **Write scoped CSS** if the variant needs styles beyond inline:
```html
<!-- Variants: insert below this line -->
<div data-impeccable-variant="1">
<!-- variant 1: full element replacement -->
</div>
<div data-impeccable-variant="2" style="display: none">
<!-- variant 2: full element replacement -->
</div>
<div data-impeccable-variant="3" style="display: none">
<!-- variant 3: full element replacement -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
6. **Write scoped CSS** if the variants need styles beyond inline:
```css
/* impeccable-variants-css-start SESSION_ID */
@scope ([data-impeccable-variant="N"]) {
:scope { /* styles for the variant root */ }
.child-class { /* styles for children */ }
}
@scope ([data-impeccable-variant="1"]) { ... }
@scope ([data-impeccable-variant="2"]) { ... }
/* impeccable-variants-css-end SESSION_ID */
```
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
### Step 4: Signal completion
### Step 3: Signal completion
After all variants are written:
```bash
npx impeccable poll --reply SESSION_ID done
npx impeccable poll --reply EVENT_ID done
```
## Handle Accept
+37 -49
View File
@@ -76,80 +76,68 @@ END LOOP
## Handle Generate
The event contains: `{id, action, freeformPrompt, count, element}`.
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
### Step 1: Find the source file
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
### Step 1: Wrap the element (one CLI call)
### Step 2: Create the variant wrapper
Use the `wrap` helper to find the element and create the variant container:
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
**HTML / Vue / Svelte:**
```html
<!-- impeccable-variants-start SESSION_ID -->
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
<div data-impeccable-variant="original" style="display: none">
<!-- move the original element here -->
</div>
</div>
<!-- impeccable-variants-end SESSION_ID -->
```bash
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
```
**JSX / TSX:**
```jsx
{/* impeccable-variants-start SESSION_ID */}
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
<div data-impeccable-variant="original" style={{display: 'none'}}>
{/* move the original element here */}
</div>
</div>
{/* impeccable-variants-end SESSION_ID */}
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
The command outputs JSON with the file path and the insert line:
```json
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
```
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
If `wrap` fails, fall back to manual grep + edit.
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
### Step 3: Generate variants one by one
For each variant (1 through COUNT):
### Step 2: Generate all variants and write them in a SINGLE edit
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
5. **Write the variant** into the wrapper in the source file:
```html
<div data-impeccable-variant="N" style="display: none">
<!-- variant N content -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default).
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
6. **Write scoped CSS** if the variant needs styles beyond inline:
```html
<!-- Variants: insert below this line -->
<div data-impeccable-variant="1">
<!-- variant 1: full element replacement -->
</div>
<div data-impeccable-variant="2" style="display: none">
<!-- variant 2: full element replacement -->
</div>
<div data-impeccable-variant="3" style="display: none">
<!-- variant 3: full element replacement -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
6. **Write scoped CSS** if the variants need styles beyond inline:
```css
/* impeccable-variants-css-start SESSION_ID */
@scope ([data-impeccable-variant="N"]) {
:scope { /* styles for the variant root */ }
.child-class { /* styles for children */ }
}
@scope ([data-impeccable-variant="1"]) { ... }
@scope ([data-impeccable-variant="2"]) { ... }
/* impeccable-variants-css-end SESSION_ID */
```
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
### Step 4: Signal completion
### Step 3: Signal completion
After all variants are written:
```bash
npx impeccable poll --reply SESSION_ID done
npx impeccable poll --reply EVENT_ID done
```
## Handle Accept
+37 -49
View File
@@ -76,80 +76,68 @@ END LOOP
## Handle Generate
The event contains: `{id, action, freeformPrompt, count, element}`.
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
### Step 1: Find the source file
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
### Step 1: Wrap the element (one CLI call)
### Step 2: Create the variant wrapper
Use the `wrap` helper to find the element and create the variant container:
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
**HTML / Vue / Svelte:**
```html
<!-- impeccable-variants-start SESSION_ID -->
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
<div data-impeccable-variant="original" style="display: none">
<!-- move the original element here -->
</div>
</div>
<!-- impeccable-variants-end SESSION_ID -->
```bash
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
```
**JSX / TSX:**
```jsx
{/* impeccable-variants-start SESSION_ID */}
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
<div data-impeccable-variant="original" style={{display: 'none'}}>
{/* move the original element here */}
</div>
</div>
{/* impeccable-variants-end SESSION_ID */}
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
The command outputs JSON with the file path and the insert line:
```json
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
```
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
If `wrap` fails, fall back to manual grep + edit.
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
### Step 3: Generate variants one by one
For each variant (1 through COUNT):
### Step 2: Generate all variants and write them in a SINGLE edit
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
5. **Write the variant** into the wrapper in the source file:
```html
<div data-impeccable-variant="N" style="display: none">
<!-- variant N content -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default).
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
6. **Write scoped CSS** if the variant needs styles beyond inline:
```html
<!-- Variants: insert below this line -->
<div data-impeccable-variant="1">
<!-- variant 1: full element replacement -->
</div>
<div data-impeccable-variant="2" style="display: none">
<!-- variant 2: full element replacement -->
</div>
<div data-impeccable-variant="3" style="display: none">
<!-- variant 3: full element replacement -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
6. **Write scoped CSS** if the variants need styles beyond inline:
```css
/* impeccable-variants-css-start SESSION_ID */
@scope ([data-impeccable-variant="N"]) {
:scope { /* styles for the variant root */ }
.child-class { /* styles for children */ }
}
@scope ([data-impeccable-variant="1"]) { ... }
@scope ([data-impeccable-variant="2"]) { ... }
/* impeccable-variants-css-end SESSION_ID */
```
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
### Step 4: Signal completion
### Step 3: Signal completion
After all variants are written:
```bash
npx impeccable poll --reply SESSION_ID done
npx impeccable poll --reply EVENT_ID done
```
## Handle Accept
+37 -49
View File
@@ -76,80 +76,68 @@ END LOOP
## Handle Generate
The event contains: `{id, action, freeformPrompt, count, element}`.
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
### Step 1: Find the source file
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
### Step 1: Wrap the element (one CLI call)
### Step 2: Create the variant wrapper
Use the `wrap` helper to find the element and create the variant container:
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
**HTML / Vue / Svelte:**
```html
<!-- impeccable-variants-start SESSION_ID -->
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
<div data-impeccable-variant="original" style="display: none">
<!-- move the original element here -->
</div>
</div>
<!-- impeccable-variants-end SESSION_ID -->
```bash
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
```
**JSX / TSX:**
```jsx
{/* impeccable-variants-start SESSION_ID */}
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
<div data-impeccable-variant="original" style={{display: 'none'}}>
{/* move the original element here */}
</div>
</div>
{/* impeccable-variants-end SESSION_ID */}
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
The command outputs JSON with the file path and the insert line:
```json
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
```
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
If `wrap` fails, fall back to manual grep + edit.
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
### Step 3: Generate variants one by one
For each variant (1 through COUNT):
### Step 2: Generate all variants and write them in a SINGLE edit
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
5. **Write the variant** into the wrapper in the source file:
```html
<div data-impeccable-variant="N" style="display: none">
<!-- variant N content -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default).
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
6. **Write scoped CSS** if the variant needs styles beyond inline:
```html
<!-- Variants: insert below this line -->
<div data-impeccable-variant="1">
<!-- variant 1: full element replacement -->
</div>
<div data-impeccable-variant="2" style="display: none">
<!-- variant 2: full element replacement -->
</div>
<div data-impeccable-variant="3" style="display: none">
<!-- variant 3: full element replacement -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
6. **Write scoped CSS** if the variants need styles beyond inline:
```css
/* impeccable-variants-css-start SESSION_ID */
@scope ([data-impeccable-variant="N"]) {
:scope { /* styles for the variant root */ }
.child-class { /* styles for children */ }
}
@scope ([data-impeccable-variant="1"]) { ... }
@scope ([data-impeccable-variant="2"]) { ... }
/* impeccable-variants-css-end SESSION_ID */
```
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
### Step 4: Signal completion
### Step 3: Signal completion
After all variants are written:
```bash
npx impeccable poll --reply SESSION_ID done
npx impeccable poll --reply EVENT_ID done
```
## Handle Accept
+37 -49
View File
@@ -76,80 +76,68 @@ END LOOP
## Handle Generate
The event contains: `{id, action, freeformPrompt, count, element}`.
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
### Step 1: Find the source file
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
### Step 1: Wrap the element (one CLI call)
### Step 2: Create the variant wrapper
Use the `wrap` helper to find the element and create the variant container:
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
**HTML / Vue / Svelte:**
```html
<!-- impeccable-variants-start SESSION_ID -->
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
<div data-impeccable-variant="original" style="display: none">
<!-- move the original element here -->
</div>
</div>
<!-- impeccable-variants-end SESSION_ID -->
```bash
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
```
**JSX / TSX:**
```jsx
{/* impeccable-variants-start SESSION_ID */}
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
<div data-impeccable-variant="original" style={{display: 'none'}}>
{/* move the original element here */}
</div>
</div>
{/* impeccable-variants-end SESSION_ID */}
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
The command outputs JSON with the file path and the insert line:
```json
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
```
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
If `wrap` fails, fall back to manual grep + edit.
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
### Step 3: Generate variants one by one
For each variant (1 through COUNT):
### Step 2: Generate all variants and write them in a SINGLE edit
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
5. **Write the variant** into the wrapper in the source file:
```html
<div data-impeccable-variant="N" style="display: none">
<!-- variant N content -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default).
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
6. **Write scoped CSS** if the variant needs styles beyond inline:
```html
<!-- Variants: insert below this line -->
<div data-impeccable-variant="1">
<!-- variant 1: full element replacement -->
</div>
<div data-impeccable-variant="2" style="display: none">
<!-- variant 2: full element replacement -->
</div>
<div data-impeccable-variant="3" style="display: none">
<!-- variant 3: full element replacement -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
6. **Write scoped CSS** if the variants need styles beyond inline:
```css
/* impeccable-variants-css-start SESSION_ID */
@scope ([data-impeccable-variant="N"]) {
:scope { /* styles for the variant root */ }
.child-class { /* styles for children */ }
}
@scope ([data-impeccable-variant="1"]) { ... }
@scope ([data-impeccable-variant="2"]) { ... }
/* impeccable-variants-css-end SESSION_ID */
```
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
### Step 4: Signal completion
### Step 3: Signal completion
After all variants are written:
```bash
npx impeccable poll --reply SESSION_ID done
npx impeccable poll --reply EVENT_ID done
```
## Handle Accept
+37 -49
View File
@@ -76,80 +76,68 @@ END LOOP
## Handle Generate
The event contains: `{id, action, freeformPrompt, count, element}`.
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
### Step 1: Find the source file
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
### Step 1: Wrap the element (one CLI call)
### Step 2: Create the variant wrapper
Use the `wrap` helper to find the element and create the variant container:
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
**HTML / Vue / Svelte:**
```html
<!-- impeccable-variants-start SESSION_ID -->
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
<div data-impeccable-variant="original" style="display: none">
<!-- move the original element here -->
</div>
</div>
<!-- impeccable-variants-end SESSION_ID -->
```bash
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
```
**JSX / TSX:**
```jsx
{/* impeccable-variants-start SESSION_ID */}
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
<div data-impeccable-variant="original" style={{display: 'none'}}>
{/* move the original element here */}
</div>
</div>
{/* impeccable-variants-end SESSION_ID */}
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
The command outputs JSON with the file path and the insert line:
```json
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
```
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
If `wrap` fails, fall back to manual grep + edit.
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
### Step 3: Generate variants one by one
For each variant (1 through COUNT):
### Step 2: Generate all variants and write them in a SINGLE edit
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
5. **Write the variant** into the wrapper in the source file:
```html
<div data-impeccable-variant="N" style="display: none">
<!-- variant N content -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default).
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
6. **Write scoped CSS** if the variant needs styles beyond inline:
```html
<!-- Variants: insert below this line -->
<div data-impeccable-variant="1">
<!-- variant 1: full element replacement -->
</div>
<div data-impeccable-variant="2" style="display: none">
<!-- variant 2: full element replacement -->
</div>
<div data-impeccable-variant="3" style="display: none">
<!-- variant 3: full element replacement -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
6. **Write scoped CSS** if the variants need styles beyond inline:
```css
/* impeccable-variants-css-start SESSION_ID */
@scope ([data-impeccable-variant="N"]) {
:scope { /* styles for the variant root */ }
.child-class { /* styles for children */ }
}
@scope ([data-impeccable-variant="1"]) { ... }
@scope ([data-impeccable-variant="2"]) { ... }
/* impeccable-variants-css-end SESSION_ID */
```
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
### Step 4: Signal completion
### Step 3: Signal completion
After all variants are written:
```bash
npx impeccable poll --reply SESSION_ID done
npx impeccable poll --reply EVENT_ID done
```
## Handle Accept
+37 -49
View File
@@ -76,80 +76,68 @@ END LOOP
## Handle Generate
The event contains: `{id, action, freeformPrompt, count, element}`.
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
### Step 1: Find the source file
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
### Step 1: Wrap the element (one CLI call)
### Step 2: Create the variant wrapper
Use the `wrap` helper to find the element and create the variant container:
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
**HTML / Vue / Svelte:**
```html
<!-- impeccable-variants-start SESSION_ID -->
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
<div data-impeccable-variant="original" style="display: none">
<!-- move the original element here -->
</div>
</div>
<!-- impeccable-variants-end SESSION_ID -->
```bash
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
```
**JSX / TSX:**
```jsx
{/* impeccable-variants-start SESSION_ID */}
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
<div data-impeccable-variant="original" style={{display: 'none'}}>
{/* move the original element here */}
</div>
</div>
{/* impeccable-variants-end SESSION_ID */}
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
The command outputs JSON with the file path and the insert line:
```json
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
```
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
If `wrap` fails, fall back to manual grep + edit.
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
### Step 3: Generate variants one by one
For each variant (1 through COUNT):
### Step 2: Generate all variants and write them in a SINGLE edit
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
5. **Write the variant** into the wrapper in the source file:
```html
<div data-impeccable-variant="N" style="display: none">
<!-- variant N content -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default).
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
6. **Write scoped CSS** if the variant needs styles beyond inline:
```html
<!-- Variants: insert below this line -->
<div data-impeccable-variant="1">
<!-- variant 1: full element replacement -->
</div>
<div data-impeccable-variant="2" style="display: none">
<!-- variant 2: full element replacement -->
</div>
<div data-impeccable-variant="3" style="display: none">
<!-- variant 3: full element replacement -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
6. **Write scoped CSS** if the variants need styles beyond inline:
```css
/* impeccable-variants-css-start SESSION_ID */
@scope ([data-impeccable-variant="N"]) {
:scope { /* styles for the variant root */ }
.child-class { /* styles for children */ }
}
@scope ([data-impeccable-variant="1"]) { ... }
@scope ([data-impeccable-variant="2"]) { ... }
/* impeccable-variants-css-end SESSION_ID */
```
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
### Step 4: Signal completion
### Step 3: Signal completion
After all variants are written:
```bash
npx impeccable poll --reply SESSION_ID done
npx impeccable poll --reply EVENT_ID done
```
## Handle Accept
+37 -49
View File
@@ -76,80 +76,68 @@ END LOOP
## Handle Generate
The event contains: `{id, action, freeformPrompt, count, element}`.
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
### Step 1: Find the source file
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
### Step 1: Wrap the element (one CLI call)
### Step 2: Create the variant wrapper
Use the `wrap` helper to find the element and create the variant container:
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
**HTML / Vue / Svelte:**
```html
<!-- impeccable-variants-start SESSION_ID -->
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
<div data-impeccable-variant="original" style="display: none">
<!-- move the original element here -->
</div>
</div>
<!-- impeccable-variants-end SESSION_ID -->
```bash
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
```
**JSX / TSX:**
```jsx
{/* impeccable-variants-start SESSION_ID */}
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
<div data-impeccable-variant="original" style={{display: 'none'}}>
{/* move the original element here */}
</div>
</div>
{/* impeccable-variants-end SESSION_ID */}
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
The command outputs JSON with the file path and the insert line:
```json
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
```
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
If `wrap` fails, fall back to manual grep + edit.
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
### Step 3: Generate variants one by one
For each variant (1 through COUNT):
### Step 2: Generate all variants and write them in a SINGLE edit
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
5. **Write the variant** into the wrapper in the source file:
```html
<div data-impeccable-variant="N" style="display: none">
<!-- variant N content -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default).
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
6. **Write scoped CSS** if the variant needs styles beyond inline:
```html
<!-- Variants: insert below this line -->
<div data-impeccable-variant="1">
<!-- variant 1: full element replacement -->
</div>
<div data-impeccable-variant="2" style="display: none">
<!-- variant 2: full element replacement -->
</div>
<div data-impeccable-variant="3" style="display: none">
<!-- variant 3: full element replacement -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
6. **Write scoped CSS** if the variants need styles beyond inline:
```css
/* impeccable-variants-css-start SESSION_ID */
@scope ([data-impeccable-variant="N"]) {
:scope { /* styles for the variant root */ }
.child-class { /* styles for children */ }
}
@scope ([data-impeccable-variant="1"]) { ... }
@scope ([data-impeccable-variant="2"]) { ... }
/* impeccable-variants-css-end SESSION_ID */
```
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
### Step 4: Signal completion
### Step 3: Signal completion
After all variants are written:
```bash
npx impeccable poll --reply SESSION_ID done
npx impeccable poll --reply EVENT_ID done
```
## Handle Accept
+37 -49
View File
@@ -76,80 +76,68 @@ END LOOP
## Handle Generate
The event contains: `{id, action, freeformPrompt, count, element}`.
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
### Step 1: Find the source file
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
### Step 1: Wrap the element (one CLI call)
### Step 2: Create the variant wrapper
Use the `wrap` helper to find the element and create the variant container:
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
**HTML / Vue / Svelte:**
```html
<!-- impeccable-variants-start SESSION_ID -->
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
<div data-impeccable-variant="original" style="display: none">
<!-- move the original element here -->
</div>
</div>
<!-- impeccable-variants-end SESSION_ID -->
```bash
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
```
**JSX / TSX:**
```jsx
{/* impeccable-variants-start SESSION_ID */}
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
<div data-impeccable-variant="original" style={{display: 'none'}}>
{/* move the original element here */}
</div>
</div>
{/* impeccable-variants-end SESSION_ID */}
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
The command outputs JSON with the file path and the insert line:
```json
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
```
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
If `wrap` fails, fall back to manual grep + edit.
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
### Step 3: Generate variants one by one
For each variant (1 through COUNT):
### Step 2: Generate all variants and write them in a SINGLE edit
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
5. **Write the variant** into the wrapper in the source file:
```html
<div data-impeccable-variant="N" style="display: none">
<!-- variant N content -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default).
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
6. **Write scoped CSS** if the variant needs styles beyond inline:
```html
<!-- Variants: insert below this line -->
<div data-impeccable-variant="1">
<!-- variant 1: full element replacement -->
</div>
<div data-impeccable-variant="2" style="display: none">
<!-- variant 2: full element replacement -->
</div>
<div data-impeccable-variant="3" style="display: none">
<!-- variant 3: full element replacement -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
6. **Write scoped CSS** if the variants need styles beyond inline:
```css
/* impeccable-variants-css-start SESSION_ID */
@scope ([data-impeccable-variant="N"]) {
:scope { /* styles for the variant root */ }
.child-class { /* styles for children */ }
}
@scope ([data-impeccable-variant="1"]) { ... }
@scope ([data-impeccable-variant="2"]) { ... }
/* impeccable-variants-css-end SESSION_ID */
```
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
### Step 4: Signal completion
### Step 3: Signal completion
After all variants are written:
```bash
npx impeccable poll --reply SESSION_ID done
npx impeccable poll --reply EVENT_ID done
```
## Handle Accept
+37 -49
View File
@@ -76,80 +76,68 @@ END LOOP
## Handle Generate
The event contains: `{id, action, freeformPrompt, count, element}`.
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
### Step 1: Find the source file
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
### Step 1: Wrap the element (one CLI call)
### Step 2: Create the variant wrapper
Use the `wrap` helper to find the element and create the variant container:
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
**HTML / Vue / Svelte:**
```html
<!-- impeccable-variants-start SESSION_ID -->
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
<div data-impeccable-variant="original" style="display: none">
<!-- move the original element here -->
</div>
</div>
<!-- impeccable-variants-end SESSION_ID -->
```bash
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
```
**JSX / TSX:**
```jsx
{/* impeccable-variants-start SESSION_ID */}
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
<div data-impeccable-variant="original" style={{display: 'none'}}>
{/* move the original element here */}
</div>
</div>
{/* impeccable-variants-end SESSION_ID */}
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
The command outputs JSON with the file path and the insert line:
```json
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
```
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
If `wrap` fails, fall back to manual grep + edit.
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
### Step 3: Generate variants one by one
For each variant (1 through COUNT):
### Step 2: Generate all variants and write them in a SINGLE edit
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
5. **Write the variant** into the wrapper in the source file:
```html
<div data-impeccable-variant="N" style="display: none">
<!-- variant N content -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default).
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
6. **Write scoped CSS** if the variant needs styles beyond inline:
```html
<!-- Variants: insert below this line -->
<div data-impeccable-variant="1">
<!-- variant 1: full element replacement -->
</div>
<div data-impeccable-variant="2" style="display: none">
<!-- variant 2: full element replacement -->
</div>
<div data-impeccable-variant="3" style="display: none">
<!-- variant 3: full element replacement -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
6. **Write scoped CSS** if the variants need styles beyond inline:
```css
/* impeccable-variants-css-start SESSION_ID */
@scope ([data-impeccable-variant="N"]) {
:scope { /* styles for the variant root */ }
.child-class { /* styles for children */ }
}
@scope ([data-impeccable-variant="1"]) { ... }
@scope ([data-impeccable-variant="2"]) { ... }
/* impeccable-variants-css-end SESSION_ID */
```
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
### Step 4: Signal completion
### Step 3: Signal completion
After all variants are written:
```bash
npx impeccable poll --reply SESSION_ID done
npx impeccable poll --reply EVENT_ID done
```
## Handle Accept
+37 -49
View File
@@ -76,80 +76,68 @@ END LOOP
## Handle Generate
The event contains: `{id, action, freeformPrompt, count, element}`.
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
### Step 1: Find the source file
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
### Step 1: Wrap the element (one CLI call)
### Step 2: Create the variant wrapper
Use the `wrap` helper to find the element and create the variant container:
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
**HTML / Vue / Svelte:**
```html
<!-- impeccable-variants-start SESSION_ID -->
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
<div data-impeccable-variant="original" style="display: none">
<!-- move the original element here -->
</div>
</div>
<!-- impeccable-variants-end SESSION_ID -->
```bash
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
```
**JSX / TSX:**
```jsx
{/* impeccable-variants-start SESSION_ID */}
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
<div data-impeccable-variant="original" style={{display: 'none'}}>
{/* move the original element here */}
</div>
</div>
{/* impeccable-variants-end SESSION_ID */}
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
The command outputs JSON with the file path and the insert line:
```json
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
```
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
If `wrap` fails, fall back to manual grep + edit.
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
### Step 3: Generate variants one by one
For each variant (1 through COUNT):
### Step 2: Generate all variants and write them in a SINGLE edit
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
5. **Write the variant** into the wrapper in the source file:
```html
<div data-impeccable-variant="N" style="display: none">
<!-- variant N content -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default).
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
6. **Write scoped CSS** if the variant needs styles beyond inline:
```html
<!-- Variants: insert below this line -->
<div data-impeccable-variant="1">
<!-- variant 1: full element replacement -->
</div>
<div data-impeccable-variant="2" style="display: none">
<!-- variant 2: full element replacement -->
</div>
<div data-impeccable-variant="3" style="display: none">
<!-- variant 3: full element replacement -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
6. **Write scoped CSS** if the variants need styles beyond inline:
```css
/* impeccable-variants-css-start SESSION_ID */
@scope ([data-impeccable-variant="N"]) {
:scope { /* styles for the variant root */ }
.child-class { /* styles for children */ }
}
@scope ([data-impeccable-variant="1"]) { ... }
@scope ([data-impeccable-variant="2"]) { ... }
/* impeccable-variants-css-end SESSION_ID */
```
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
### Step 4: Signal completion
### Step 3: Signal completion
After all variants are written:
```bash
npx impeccable poll --reply SESSION_ID done
npx impeccable poll --reply EVENT_ID done
```
## Handle Accept
+5
View File
@@ -28,6 +28,7 @@ Commands:
live stop Stop a running live server
poll Wait for a browser event from the live server
poll --reply <id> <status> Reply to a pending event (done, error)
wrap --id ID --count N --query Q Find element in source and create variant wrapper
skills help List all available skills and commands
skills install Install impeccable skills into your project
skills update Update skills to the latest version
@@ -59,6 +60,10 @@ if (command === 'detect') {
process.argv = [process.argv[0], process.argv[1], ...args.slice(1)];
const { pollCli } = await import('../src/live/poll.mjs');
await pollCli();
} else if (command === 'wrap') {
process.argv = [process.argv[0], process.argv[1], ...args.slice(1)];
const { wrapCli } = await import('../src/live/wrap.mjs');
await wrapCli();
} else if (command === 'skills') {
const { run } = await import('./commands/skills.mjs');
await run(args.slice(1));
+37 -49
View File
@@ -76,80 +76,68 @@ END LOOP
## Handle Generate
The event contains: `{id, action, freeformPrompt, count, element}`.
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
### Step 1: Find the source file
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
### Step 1: Wrap the element (one CLI call)
### Step 2: Create the variant wrapper
Use the `wrap` helper to find the element and create the variant container:
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
**HTML / Vue / Svelte:**
```html
<!-- impeccable-variants-start SESSION_ID -->
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
<div data-impeccable-variant="original" style="display: none">
<!-- move the original element here -->
</div>
</div>
<!-- impeccable-variants-end SESSION_ID -->
```bash
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
```
**JSX / TSX:**
```jsx
{/* impeccable-variants-start SESSION_ID */}
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
<div data-impeccable-variant="original" style={{display: 'none'}}>
{/* move the original element here */}
</div>
</div>
{/* impeccable-variants-end SESSION_ID */}
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
The command outputs JSON with the file path and the insert line:
```json
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
```
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
If `wrap` fails, fall back to manual grep + edit.
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
### Step 3: Generate variants one by one
For each variant (1 through COUNT):
### Step 2: Generate all variants and write them in a SINGLE edit
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
5. **Write the variant** into the wrapper in the source file:
```html
<div data-impeccable-variant="N" style="display: none">
<!-- variant N content -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default).
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
6. **Write scoped CSS** if the variant needs styles beyond inline:
```html
<!-- Variants: insert below this line -->
<div data-impeccable-variant="1">
<!-- variant 1: full element replacement -->
</div>
<div data-impeccable-variant="2" style="display: none">
<!-- variant 2: full element replacement -->
</div>
<div data-impeccable-variant="3" style="display: none">
<!-- variant 3: full element replacement -->
</div>
```
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
6. **Write scoped CSS** if the variants need styles beyond inline:
```css
/* impeccable-variants-css-start SESSION_ID */
@scope ([data-impeccable-variant="N"]) {
:scope { /* styles for the variant root */ }
.child-class { /* styles for children */ }
}
@scope ([data-impeccable-variant="1"]) { ... }
@scope ([data-impeccable-variant="2"]) { ... }
/* impeccable-variants-css-end SESSION_ID */
```
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
### Step 4: Signal completion
### Step 3: Signal completion
After all variants are written:
```bash
npx impeccable poll --reply SESSION_ID done
npx impeccable poll --reply EVENT_ID done
```
## Handle Accept
+1
View File
@@ -864,6 +864,7 @@
action: selectedAction,
freeformPrompt: prompt || undefined,
count: selectedCount,
pageUrl: location.pathname,
element: extractContext(selectedElement),
});
+298
View File
@@ -0,0 +1,298 @@
/**
* CLI helper: find an element in source and wrap it in a variant container.
*
* Usage:
* npx impeccable wrap --id SESSION_ID --count N --query "hero-combined-left" [--file path]
*
* Searches project files for the element matching the query (class name, ID, or
* text snippet), wraps it with the variant scaffolding, and prints the file path
* + line range where the agent should insert variant HTML.
*
* This replaces 3-4 agent tool calls (grep + read + edit) with a single CLI call.
*/
import fs from 'node:fs';
import path from 'node:path';
const EXTENSIONS = ['.html', '.jsx', '.tsx', '.vue', '.svelte', '.astro'];
export async function wrapCli() {
const args = process.argv.slice(2);
if (args.includes('--help') || args.includes('-h')) {
console.log(`Usage: impeccable wrap [options]
Find an element in source and wrap it in a variant container.
Required:
--id ID Session ID for the variant wrapper
--count N Number of expected variants (1-8)
Element identification (at least one required):
--element-id ID HTML id attribute of the element
--classes A,B,C Comma-separated CSS class names
--tag TAG Tag name (div, section, etc.)
--query TEXT Fallback: raw text to search for
Optional:
--file PATH Source file to search in (skips auto-detection)
--help Show this help message
Output (JSON):
{ file, startLine, endLine, insertLine, commentSyntax }
The agent should insert variant HTML at insertLine.`);
process.exit(0);
}
const id = argVal(args, '--id');
const count = parseInt(argVal(args, '--count') || '3');
const elementId = argVal(args, '--element-id');
const classes = argVal(args, '--classes');
const tag = argVal(args, '--tag');
const query = argVal(args, '--query');
const filePath = argVal(args, '--file');
if (!id) { console.error('Missing --id'); process.exit(1); }
if (!elementId && !classes && !query) {
console.error('Need at least one of: --element-id, --classes, --query');
process.exit(1);
}
// Build search queries in priority order (most specific first)
const queries = buildSearchQueries(elementId, classes, tag, query);
// Find the source file
let targetFile = filePath;
let matchedQuery = null;
if (!targetFile) {
for (const q of queries) {
targetFile = findFileWithQuery(q, process.cwd());
if (targetFile) { matchedQuery = q; break; }
}
if (!targetFile) {
console.error(JSON.stringify({ error: 'Could not find element in project files. Searched for: ' + queries.join(', ') }));
process.exit(1);
}
} else {
matchedQuery = queries[0];
}
const content = fs.readFileSync(targetFile, 'utf-8');
const lines = content.split('\n');
// Find the element, trying each query in priority order
let match = null;
for (const q of queries) {
match = findElement(lines, q);
if (match) break;
}
if (!match) {
console.error(JSON.stringify({ error: 'Found file but could not locate element in ' + targetFile + '. Searched for: ' + queries.join(', ') }));
process.exit(1);
}
const { startLine, endLine } = match;
const commentSyntax = detectCommentSyntax(targetFile);
const indent = lines[startLine].match(/^(\s*)/)[1];
// Extract the original element
const originalLines = lines.slice(startLine, endLine + 1);
const originalIndented = originalLines.map(l => indent + ' ' + l.trimStart()).join('\n');
// Build the wrapper
const wrapperLines = [
indent + commentSyntax.open + ' impeccable-variants-start ' + id + ' ' + commentSyntax.close,
indent + '<div data-impeccable-variants="' + id + '" data-impeccable-variant-count="' + count + '" style="display: contents">',
indent + ' ' + commentSyntax.open + ' Original ' + commentSyntax.close,
indent + ' <div data-impeccable-variant="original" style="display: none">',
originalIndented,
indent + ' </div>',
indent + ' ' + commentSyntax.open + ' Variants: insert below this line ' + commentSyntax.close,
indent + '</div>',
indent + commentSyntax.open + ' impeccable-variants-end ' + id + ' ' + commentSyntax.close,
];
// Replace the original element with the wrapper
const newLines = [
...lines.slice(0, startLine),
...wrapperLines,
...lines.slice(endLine + 1),
];
fs.writeFileSync(targetFile, newLines.join('\n'), 'utf-8');
// Calculate insert line (the "insert below this line" comment)
const insertLine = startLine + 6; // 0-indexed in the new file
console.log(JSON.stringify({
file: path.relative(process.cwd(), targetFile),
startLine: startLine + 1, // 1-indexed for the agent
endLine: startLine + wrapperLines.length, // 1-indexed
insertLine: insertLine + 1, // 1-indexed: where variants go
commentSyntax: commentSyntax,
originalLineCount: originalLines.length,
}));
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function argVal(args, flag) {
const idx = args.indexOf(flag);
return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : null;
}
/**
* Build search query strings in priority order (most specific first).
* ID is most reliable, then specific class combos, then single classes, then raw query.
*/
function buildSearchQueries(elementId, classes, tag, query) {
const queries = [];
// 1. ID is the most specific
if (elementId) {
queries.push('id="' + elementId + '"');
}
// 2. Full class attribute match (for elements with distinctive multi-class combos)
if (classes) {
const classList = classes.split(',').map(c => c.trim()).filter(Boolean);
if (classList.length > 1) {
// Try the most distinctive class first (longest, most specific)
const sorted = [...classList].sort((a, b) => b.length - a.length);
queries.push('class="' + classList.join(' ') + '"'); // exact full match
queries.push(sorted[0]); // most distinctive single class
} else if (classList.length === 1) {
queries.push(classList[0]);
}
}
// 3. Tag + class combo (e.g., <section class="hero">)
if (tag && classes) {
const firstClass = classes.split(',')[0].trim();
queries.push('<' + tag + ' class="' + firstClass);
}
// 4. Raw fallback query
if (query) {
queries.push(query);
}
return queries;
}
function detectCommentSyntax(filePath) {
const ext = path.extname(filePath).toLowerCase();
if (ext === '.jsx' || ext === '.tsx') {
return { open: '{/*', close: '*/}' };
}
// HTML, Vue, Svelte, Astro all use HTML comments
return { open: '<!--', close: '-->' };
}
/**
* Search project files for the query string (class name, ID, etc.)
* Returns the first matching file path, or null.
*/
function findFileWithQuery(query, cwd) {
const searchDirs = ['src', 'app', 'pages', 'components', 'public', 'views', 'templates', '.'];
const seen = new Set();
for (const dir of searchDirs) {
const absDir = path.join(cwd, dir);
if (!fs.existsSync(absDir)) continue;
const result = searchDir(absDir, query, seen, 0);
if (result) return result;
}
return null;
}
function searchDir(dir, query, seen, depth) {
if (depth > 5) return null; // don't go too deep
const realDir = fs.realpathSync(dir);
if (seen.has(realDir)) return null;
seen.add(realDir);
let entries;
try { entries = fs.readdirSync(dir, { withFileTypes: true }); }
catch { return null; }
// Check files first
for (const entry of entries) {
if (!entry.isFile()) continue;
const ext = path.extname(entry.name).toLowerCase();
if (!EXTENSIONS.includes(ext)) continue;
const filePath = path.join(dir, entry.name);
try {
const content = fs.readFileSync(filePath, 'utf-8');
if (content.includes(query)) return filePath;
} catch { /* skip unreadable files */ }
}
// Then recurse into directories
for (const entry of entries) {
if (!entry.isDirectory()) continue;
if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === 'dist' || entry.name === 'build') continue;
const result = searchDir(path.join(dir, entry.name), query, seen, depth + 1);
if (result) return result;
}
return null;
}
/**
* Find the element's start and end line in the file.
* The query is a class name, ID, or text snippet.
* We find the line containing the query, then find the matching closing tag.
*/
function findElement(lines, query) {
// Find the line containing the query
let startLine = -1;
for (let i = 0; i < lines.length; i++) {
if (lines[i].includes(query)) {
// Make sure this looks like a tag opening, not a comment or string
const line = lines[i].trim();
if (line.startsWith('<!--') || line.startsWith('{/*') || line.startsWith('//')) continue;
// Skip lines inside data-impeccable-variant containers (already wrapped)
if (lines[i].includes('data-impeccable-variant')) continue;
startLine = i;
break;
}
}
if (startLine === -1) return null;
// Find the end of this element by counting open/close tags
const endLine = findClosingLine(lines, startLine);
return { startLine, endLine };
}
/**
* Starting from a line with an opening tag, find the line with the matching
* closing tag by counting tag nesting depth.
*/
function findClosingLine(lines, start) {
// Extract the tag name from the opening line
const openMatch = lines[start].match(/<(\w+)[\s>]/);
if (!openMatch) return start; // self-closing or text-only line
const tagName = openMatch[1];
let depth = 0;
for (let i = start; i < lines.length; i++) {
const line = lines[i];
// Count opening tags (not self-closing)
const opens = (line.match(new RegExp('<' + tagName + '[\\s>]', 'g')) || []).length;
const selfCloses = (line.match(new RegExp('<' + tagName + '[^>]*/>', 'g')) || []).length;
const closes = (line.match(new RegExp('</' + tagName + '\\s*>', 'g')) || []).length;
depth += opens - selfCloses - closes;
if (depth <= 0) return i;
}
// If we can't find the close, return a reasonable guess
return Math.min(start + 50, lines.length - 1);
}