Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1547f6c
feat(live): manual text-edit panel + Astro inject + stale-lockfile reap
abdulwahabone May 15, 2026
e9e6534
feat(live): inline contenteditable text editing
abdulwahabone May 16, 2026
ff750f4
fix(live): hide annotation overlay during inline edit
abdulwahabone May 16, 2026
64b0632
feat(live): edit content badge mode with batched saves
abdulwahabone May 16, 2026
d423824
fix(live): use row.el.tagName for tag in applyEditing op
abdulwahabone May 16, 2026
14a4b5c
feat(live): Edit content badge styling + auto-focus + separate buttons
abdulwahabone May 16, 2026
e31f489
feat(live): Subtle button UI + cursor positioning + better copy
abdulwahabone May 16, 2026
44ee7eb
fix(live): Use site design system colors for edit badge buttons
abdulwahabone May 16, 2026
68608f1
fix(live): Match slop-callout style for edit badge buttons
abdulwahabone May 16, 2026
873d5b9
fix(live): Pill-shaped edit badge buttons, 2px padding, no uppercase
abdulwahabone May 16, 2026
b4680f0
fix(live): Cancel button uses mist border + ash text
abdulwahabone May 16, 2026
489f893
fix(live): Remove blue focus outline from contenteditable elements in…
abdulwahabone May 16, 2026
e4e1616
feat(live): Decouple manual edits from agent/poll pipeline
abdulwahabone May 16, 2026
4638a47
feat(live): Stash manual edits server-side; commit via AI on request
abdulwahabone May 16, 2026
2e5bfdf
feat(live): Staged-edits pill becomes an "Apply" button
abdulwahabone May 16, 2026
3127ff2
chore(live): gitignore pending-manual-edits.json runtime buffer
abdulwahabone May 16, 2026
c42c4e6
chore: drop stray site/ test edits from PR
abdulwahabone May 16, 2026
9cb7c25
feat(live): Pill label reads "Apply N staged"
abdulwahabone May 16, 2026
a80c563
fix(live): Manual edit ops use the leaf element's locator, not parent's
abdulwahabone May 16, 2026
d28fa17
fix(live): Climb to nearest classed ancestor when leaf has no locator
abdulwahabone May 16, 2026
dbaa5d5
feat(live): Make mixed-content paragraphs editable
abdulwahabone May 16, 2026
3857a12
fix(live): Address Cursor Bugbot findings (CB-2 through CB-6)
abdulwahabone May 16, 2026
f61a555
fix(live): A3+A4 data-integrity guards, A6 test coverage
abdulwahabone May 16, 2026
b83ea1c
chore: drop .claude/pr-review.md from PR
abdulwahabone May 16, 2026
0b455e5
chore: drop stray site/ test edits from PR (round 2)
abdulwahabone May 16, 2026
a42b400
feat(live): Disable Edit badge while variants are generating
abdulwahabone May 16, 2026
463fe9e
fix(live): live-wrap refuses without --page-url when buffer has pendi…
abdulwahabone May 16, 2026
6af730e
change back
abdulwahabone May 16, 2026
c84d64a
chore: drop stray site/ test edits from PR (round 3)
abdulwahabone May 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions .agents/skills/impeccable/reference/live.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,13 @@ LOOP:
node .agents/skills/impeccable/scripts/live-poll.mjs # default long timeout; no --timeout=
Read JSON; dispatch on "type"

"generate" → Handle Generate; reply done; LOOP
"accept" → Handle Accept; complete carbonize cleanup if required; LOOP
"discard" → Handle Discard; LOOP
"prefetch" → Handle Prefetch; LOOP
"timeout" → LOOP
"exit" → break → Cleanup
"generate" → Handle Generate; reply done; LOOP
"manual_edits" → Handle Manual Edits; LOOP
"accept" → Handle Accept; complete carbonize cleanup if required; LOOP
"discard" → Handle Discard; LOOP
"prefetch" → Handle Prefetch; LOOP
"timeout" → LOOP
"exit" → break → Cleanup
```

## Recovery commands
Expand Down Expand Up @@ -394,6 +395,19 @@ Then remove the temporary wrapper from the served file if it's still there.

Remove the wrapper you inserted in Step 2. Nothing else to do.

## Handle `manual_edits`

Event: `{id, pageUrl, element, ops, _editResult, _completionAck}`. The user picked an element, clicked the "Edit content" badge at its top-right corner, edited text descendants directly on the page (inline contenteditable), then clicked Apply. This triggers a batched save: all edited text leaves are collected into one `manual_edits` event with multiple ops. The poll script already ran `live-edit.mjs` to apply each op to source deterministically, then acknowledged event delivery. Your job is *only* to resolve any ops the script couldn't apply.

- If `_editResult.failed` is empty: nothing to do. Loop and poll again.
- If `_editResult.failed` is non-empty: each entry is `{ref, op, reason, candidates?, file?}`. Common reasons:
- `text_not_in_source`: `op.originalText` doesn't appear verbatim (templated content, e.g. `<h2>{title}</h2>` reads from a data source). Find the data source (JSON, frontmatter, props) and update the entry that matches.
- `element_ambiguous`: multiple source matches; `candidates` lists line numbers. Read the file, pick the right element by surrounding context, apply the op via `Edit`.
- `element_not_found`: element wasn't located. Element may be runtime-injected or in a file not in standard search dirs. Search wider.
- `insufficient_locator`: `op.classes` and `op.elementId` were both absent. Skip; the inline edit should not have emitted this op without a locator.
- Do NOT regenerate variants. Do NOT call `live-wrap.mjs` or `live-accept.mjs`.
- After handling failures, loop. The completion ack was already posted; do not `--reply`.

## Handle `accept`

Event: `{id, variantId, _acceptResult, _completionAck}`. The poll script already ran `live-accept.mjs` to handle the file operation deterministically, then acknowledged event delivery to the helper. The browser DOM is already updated.
Expand Down
11 changes: 10 additions & 1 deletion .agents/skills/impeccable/scripts/impeccable-paths.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,16 @@ export function getLegacyLiveServerPath(cwd = process.cwd()) {
export function readLiveServerInfo(cwd = process.cwd()) {
for (const filePath of [getLiveServerPath(cwd), getLegacyLiveServerPath(cwd)]) {
try {
return { info: JSON.parse(fs.readFileSync(filePath, 'utf-8')), path: filePath };
const info = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
if (info && typeof info.pid === 'number') {
try {
process.kill(info.pid, 0);
} catch {
try { fs.unlinkSync(filePath); } catch {}
continue;
}
}
return { info, path: filePath };
} catch {
/* try next */
}
Expand Down
Loading
Loading