From c39b6425fa54a093749b9a236adcd003818167c1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 23 Aug 2026 23:33:14 +0000 Subject: [PATCH] Sync generated provider output --- .../skills/impeccable/scripts/live-poll.mjs | 9 ++-- .../skills/impeccable/scripts/live-server.mjs | 9 ++++ .../scripts/live/sveltekit-adapter.mjs | 42 +++++++------------ .../scripts/live/tanstack-adapter.mjs | 29 ++----------- .../skills/impeccable/scripts/live-poll.mjs | 9 ++-- .../skills/impeccable/scripts/live-server.mjs | 9 ++++ .../scripts/live/sveltekit-adapter.mjs | 42 +++++++------------ .../scripts/live/tanstack-adapter.mjs | 29 ++----------- .../skills/impeccable/scripts/live-poll.mjs | 9 ++-- .../skills/impeccable/scripts/live-server.mjs | 9 ++++ .../scripts/live/sveltekit-adapter.mjs | 42 +++++++------------ .../scripts/live/tanstack-adapter.mjs | 29 ++----------- .../skills/impeccable/scripts/live-poll.mjs | 9 ++-- .../skills/impeccable/scripts/live-server.mjs | 9 ++++ .../scripts/live/sveltekit-adapter.mjs | 42 +++++++------------ .../scripts/live/tanstack-adapter.mjs | 29 ++----------- .../skills/impeccable/scripts/live-poll.mjs | 9 ++-- .../skills/impeccable/scripts/live-server.mjs | 9 ++++ .../scripts/live/sveltekit-adapter.mjs | 42 +++++++------------ .../scripts/live/tanstack-adapter.mjs | 29 ++----------- .grok/skills/impeccable/scripts/live-poll.mjs | 9 ++-- .../skills/impeccable/scripts/live-server.mjs | 9 ++++ .../scripts/live/sveltekit-adapter.mjs | 42 +++++++------------ .../scripts/live/tanstack-adapter.mjs | 29 ++----------- .../skills/impeccable/scripts/live-poll.mjs | 9 ++-- .../skills/impeccable/scripts/live-server.mjs | 9 ++++ .../scripts/live/sveltekit-adapter.mjs | 42 +++++++------------ .../scripts/live/tanstack-adapter.mjs | 29 ++----------- .kiro/skills/impeccable/scripts/live-poll.mjs | 9 ++-- .../skills/impeccable/scripts/live-server.mjs | 9 ++++ .../scripts/live/sveltekit-adapter.mjs | 42 +++++++------------ .../scripts/live/tanstack-adapter.mjs | 29 ++----------- .../skills/impeccable/scripts/live-poll.mjs | 9 ++-- .../skills/impeccable/scripts/live-server.mjs | 9 ++++ .../scripts/live/sveltekit-adapter.mjs | 42 +++++++------------ .../scripts/live/tanstack-adapter.mjs | 29 ++----------- .pi/skills/impeccable/scripts/live-poll.mjs | 9 ++-- .pi/skills/impeccable/scripts/live-server.mjs | 9 ++++ .../scripts/live/sveltekit-adapter.mjs | 42 +++++++------------ .../scripts/live/tanstack-adapter.mjs | 29 ++----------- .../skills/impeccable/scripts/live-poll.mjs | 9 ++-- .../skills/impeccable/scripts/live-server.mjs | 9 ++++ .../scripts/live/sveltekit-adapter.mjs | 42 +++++++------------ .../scripts/live/tanstack-adapter.mjs | 29 ++----------- .../skills/impeccable/scripts/live-poll.mjs | 9 ++-- .../skills/impeccable/scripts/live-server.mjs | 9 ++++ .../scripts/live/sveltekit-adapter.mjs | 42 +++++++------------ .../scripts/live/tanstack-adapter.mjs | 29 ++----------- .../skills/impeccable/scripts/live-poll.mjs | 9 ++-- .../skills/impeccable/scripts/live-server.mjs | 9 ++++ .../scripts/live/sveltekit-adapter.mjs | 42 +++++++------------ .../scripts/live/tanstack-adapter.mjs | 29 ++----------- .trae/skills/impeccable/scripts/live-poll.mjs | 9 ++-- .../skills/impeccable/scripts/live-server.mjs | 9 ++++ .../scripts/live/sveltekit-adapter.mjs | 42 +++++++------------ .../scripts/live/tanstack-adapter.mjs | 29 ++----------- .vibe/skills/impeccable/scripts/live-poll.mjs | 9 ++-- .../skills/impeccable/scripts/live-server.mjs | 9 ++++ .../scripts/live/sveltekit-adapter.mjs | 42 +++++++------------ .../scripts/live/tanstack-adapter.mjs | 29 ++----------- .../skills/impeccable/scripts/live-poll.mjs | 9 ++-- .../skills/impeccable/scripts/live-server.mjs | 9 ++++ .../scripts/live/sveltekit-adapter.mjs | 42 +++++++------------ .../scripts/live/tanstack-adapter.mjs | 29 ++----------- 64 files changed, 528 insertions(+), 896 deletions(-) diff --git a/.agents/skills/impeccable/scripts/live-poll.mjs b/.agents/skills/impeccable/scripts/live-poll.mjs index 3b2f08c9f..59e21b9d3 100644 --- a/.agents/skills/impeccable/scripts/live-poll.mjs +++ b/.agents/skills/impeccable/scripts/live-poll.mjs @@ -238,10 +238,9 @@ export async function completeAcceptHandling(event, base, token) { }); } catch (err) { event._completionAck = { ok: false, error: err.message }; + return event; } - if (!event._completionAck) { - event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); - } + event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); return event; } @@ -269,9 +268,11 @@ export function printPollEvent(event) { // Situational plumbing rides with the event itself: `_instructions` is the // authoritative next step, with real ids and paths substituted, so the // reference doc can stay lean and can never drift from script behavior. - if (event && typeof event === 'object' && !event._instructions) { + // A wire-supplied value must never win over the locally generated one. + if (event && typeof event === 'object') { const instructions = instructionsForEvent(event, { scriptsPath: SELF_DIR }); if (instructions) event._instructions = instructions; + else delete event._instructions; } console.log(JSON.stringify(event)); } diff --git a/.agents/skills/impeccable/scripts/live-server.mjs b/.agents/skills/impeccable/scripts/live-server.mjs index cba3a222e..dafa8bd0c 100644 --- a/.agents/skills/impeccable/scripts/live-server.mjs +++ b/.agents/skills/impeccable/scripts/live-server.mjs @@ -181,8 +181,16 @@ function chatAgentLikelyActive() { // cap at 10 MB to guard against runaway writes from a misbehaving client. const MAX_ANNOTATION_BYTES = 10 * 1024 * 1024; +const POLLER_OWNED_EVENT_FIELDS = ['_instructions', '_completionAck', '_acceptResult']; + +function stripPollerOwnedEventFields(event) { + if (!event || typeof event !== 'object') return; + for (const key of POLLER_OWNED_EVENT_FIELDS) delete event[key]; +} + function enqueueEvent(event) { if (!event) return; + stripPollerOwnedEventFields(event); // Dedupe by (session, type), except mount failures, which are per-variant: // variant 2 failing must not be swallowed because variant 1's failure is // still queued. @@ -1034,6 +1042,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) { res.end(JSON.stringify({ error })); return; } + stripPollerOwnedEventFields(msg); if (msg.type === 'agent_phase') { recordAgentPhase(msg.id, msg.phase, { ...(Number.isFinite(msg.durationMs) ? { durationMs: msg.durationMs } : {}), diff --git a/.agents/skills/impeccable/scripts/live/sveltekit-adapter.mjs b/.agents/skills/impeccable/scripts/live/sveltekit-adapter.mjs index e94c54f1e..b96e955a6 100644 --- a/.agents/skills/impeccable/scripts/live/sveltekit-adapter.mjs +++ b/.agents/skills/impeccable/scripts/live/sveltekit-adapter.mjs @@ -11,6 +11,8 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; + export const SVELTE_LIVE_ROOT_COMPONENT = 'src/lib/impeccable/ImpeccableLiveRoot.svelte'; export const SVELTE_LAYOUT_MARKER_OPEN = ''; export const SVELTE_LAYOUT_MARKER_CLOSE = ''; @@ -45,11 +47,17 @@ export function detectSvelteKitProject(cwd = process.cwd(), config = null) { && fileIncludes(path.join(cwd, appHtml), '%sveltekit.head%'); if (!hasTemplateMarkers) return null; - const hasSvelteConfig = fs.existsSync(path.join(cwd, 'svelte.config.js')) - || fs.existsSync(path.join(cwd, 'svelte.config.mjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.cjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.ts')); - const hasKitPackage = packageHasSvelteKit(cwd); + const hasSvelteConfig = Boolean(firstExistingFile(cwd, [ + 'svelte.config.js', + 'svelte.config.mjs', + 'svelte.config.cjs', + 'svelte.config.ts', + ])); + const hasKitPackage = hasAnyDependency(cwd, [ + '@sveltejs/kit', + '@sveltejs/vite-plugin-svelte', + 'svelte', + ]); if (!hasSvelteConfig && !hasKitPackage) return null; return { @@ -260,36 +268,16 @@ function findSvelteKitAppHtml(cwd, config) { } function findSvelteKitLayout(cwd) { - const candidates = [ + return firstExistingFile(cwd, [ 'src/routes/+layout.svelte', 'src/routes/(app)/+layout.svelte', - ]; - for (const rel of candidates) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return 'src/routes/+layout.svelte'; + ]) || 'src/routes/+layout.svelte'; } function defaultSvelteLayout() { return `\n\n{@render children?.()}\n`; } -function packageHasSvelteKit(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return Boolean(deps['@sveltejs/kit'] || deps['@sveltejs/vite-plugin-svelte'] || deps.svelte); - } catch { - return false; - } -} - function fileIncludes(file, text) { try { return fs.readFileSync(file, 'utf-8').includes(text); diff --git a/.agents/skills/impeccable/scripts/live/tanstack-adapter.mjs b/.agents/skills/impeccable/scripts/live/tanstack-adapter.mjs index 4a1c81a97..b53978f4f 100644 --- a/.agents/skills/impeccable/scripts/live/tanstack-adapter.mjs +++ b/.agents/skills/impeccable/scripts/live/tanstack-adapter.mjs @@ -19,6 +19,8 @@ import fs from 'node:fs'; import path from 'node:path'; + +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; import { buildLiveScriptSrc } from './frameworks/script-src.mjs'; export const TANSTACK_MARKER_OPEN = '{/* impeccable-live-tanstack-start */}'; @@ -42,8 +44,8 @@ const START_PACKAGES = [ ]; export function detectTanStackStartProject(cwd = process.cwd()) { - if (!packageHasTanStackStart(cwd)) return null; - const rootRoute = findRootRouteFile(cwd); + if (!hasAnyDependency(cwd, START_PACKAGES)) return null; + const rootRoute = firstExistingFile(cwd, ROOT_ROUTE_CANDIDATES); if (!rootRoute) return null; const ext = path.extname(rootRoute); @@ -218,29 +220,6 @@ function isManagedComponent(content) { return String(content || '').includes('impeccable-live-tanstack'); } -function findRootRouteFile(cwd) { - for (const rel of ROOT_ROUTE_CANDIDATES) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return null; -} - -function packageHasTanStackStart(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return START_PACKAGES.some((name) => Boolean(deps[name])); - } catch { - return false; - } -} - function relativeImportSpecifier(fromFile, toFile) { const rel = path.posix.relative( path.posix.dirname(fromFile.split(path.sep).join('/')), diff --git a/.claude/skills/impeccable/scripts/live-poll.mjs b/.claude/skills/impeccable/scripts/live-poll.mjs index 3b2f08c9f..59e21b9d3 100644 --- a/.claude/skills/impeccable/scripts/live-poll.mjs +++ b/.claude/skills/impeccable/scripts/live-poll.mjs @@ -238,10 +238,9 @@ export async function completeAcceptHandling(event, base, token) { }); } catch (err) { event._completionAck = { ok: false, error: err.message }; + return event; } - if (!event._completionAck) { - event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); - } + event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); return event; } @@ -269,9 +268,11 @@ export function printPollEvent(event) { // Situational plumbing rides with the event itself: `_instructions` is the // authoritative next step, with real ids and paths substituted, so the // reference doc can stay lean and can never drift from script behavior. - if (event && typeof event === 'object' && !event._instructions) { + // A wire-supplied value must never win over the locally generated one. + if (event && typeof event === 'object') { const instructions = instructionsForEvent(event, { scriptsPath: SELF_DIR }); if (instructions) event._instructions = instructions; + else delete event._instructions; } console.log(JSON.stringify(event)); } diff --git a/.claude/skills/impeccable/scripts/live-server.mjs b/.claude/skills/impeccable/scripts/live-server.mjs index cba3a222e..dafa8bd0c 100644 --- a/.claude/skills/impeccable/scripts/live-server.mjs +++ b/.claude/skills/impeccable/scripts/live-server.mjs @@ -181,8 +181,16 @@ function chatAgentLikelyActive() { // cap at 10 MB to guard against runaway writes from a misbehaving client. const MAX_ANNOTATION_BYTES = 10 * 1024 * 1024; +const POLLER_OWNED_EVENT_FIELDS = ['_instructions', '_completionAck', '_acceptResult']; + +function stripPollerOwnedEventFields(event) { + if (!event || typeof event !== 'object') return; + for (const key of POLLER_OWNED_EVENT_FIELDS) delete event[key]; +} + function enqueueEvent(event) { if (!event) return; + stripPollerOwnedEventFields(event); // Dedupe by (session, type), except mount failures, which are per-variant: // variant 2 failing must not be swallowed because variant 1's failure is // still queued. @@ -1034,6 +1042,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) { res.end(JSON.stringify({ error })); return; } + stripPollerOwnedEventFields(msg); if (msg.type === 'agent_phase') { recordAgentPhase(msg.id, msg.phase, { ...(Number.isFinite(msg.durationMs) ? { durationMs: msg.durationMs } : {}), diff --git a/.claude/skills/impeccable/scripts/live/sveltekit-adapter.mjs b/.claude/skills/impeccable/scripts/live/sveltekit-adapter.mjs index e94c54f1e..b96e955a6 100644 --- a/.claude/skills/impeccable/scripts/live/sveltekit-adapter.mjs +++ b/.claude/skills/impeccable/scripts/live/sveltekit-adapter.mjs @@ -11,6 +11,8 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; + export const SVELTE_LIVE_ROOT_COMPONENT = 'src/lib/impeccable/ImpeccableLiveRoot.svelte'; export const SVELTE_LAYOUT_MARKER_OPEN = ''; export const SVELTE_LAYOUT_MARKER_CLOSE = ''; @@ -45,11 +47,17 @@ export function detectSvelteKitProject(cwd = process.cwd(), config = null) { && fileIncludes(path.join(cwd, appHtml), '%sveltekit.head%'); if (!hasTemplateMarkers) return null; - const hasSvelteConfig = fs.existsSync(path.join(cwd, 'svelte.config.js')) - || fs.existsSync(path.join(cwd, 'svelte.config.mjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.cjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.ts')); - const hasKitPackage = packageHasSvelteKit(cwd); + const hasSvelteConfig = Boolean(firstExistingFile(cwd, [ + 'svelte.config.js', + 'svelte.config.mjs', + 'svelte.config.cjs', + 'svelte.config.ts', + ])); + const hasKitPackage = hasAnyDependency(cwd, [ + '@sveltejs/kit', + '@sveltejs/vite-plugin-svelte', + 'svelte', + ]); if (!hasSvelteConfig && !hasKitPackage) return null; return { @@ -260,36 +268,16 @@ function findSvelteKitAppHtml(cwd, config) { } function findSvelteKitLayout(cwd) { - const candidates = [ + return firstExistingFile(cwd, [ 'src/routes/+layout.svelte', 'src/routes/(app)/+layout.svelte', - ]; - for (const rel of candidates) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return 'src/routes/+layout.svelte'; + ]) || 'src/routes/+layout.svelte'; } function defaultSvelteLayout() { return `\n\n{@render children?.()}\n`; } -function packageHasSvelteKit(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return Boolean(deps['@sveltejs/kit'] || deps['@sveltejs/vite-plugin-svelte'] || deps.svelte); - } catch { - return false; - } -} - function fileIncludes(file, text) { try { return fs.readFileSync(file, 'utf-8').includes(text); diff --git a/.claude/skills/impeccable/scripts/live/tanstack-adapter.mjs b/.claude/skills/impeccable/scripts/live/tanstack-adapter.mjs index 4a1c81a97..b53978f4f 100644 --- a/.claude/skills/impeccable/scripts/live/tanstack-adapter.mjs +++ b/.claude/skills/impeccable/scripts/live/tanstack-adapter.mjs @@ -19,6 +19,8 @@ import fs from 'node:fs'; import path from 'node:path'; + +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; import { buildLiveScriptSrc } from './frameworks/script-src.mjs'; export const TANSTACK_MARKER_OPEN = '{/* impeccable-live-tanstack-start */}'; @@ -42,8 +44,8 @@ const START_PACKAGES = [ ]; export function detectTanStackStartProject(cwd = process.cwd()) { - if (!packageHasTanStackStart(cwd)) return null; - const rootRoute = findRootRouteFile(cwd); + if (!hasAnyDependency(cwd, START_PACKAGES)) return null; + const rootRoute = firstExistingFile(cwd, ROOT_ROUTE_CANDIDATES); if (!rootRoute) return null; const ext = path.extname(rootRoute); @@ -218,29 +220,6 @@ function isManagedComponent(content) { return String(content || '').includes('impeccable-live-tanstack'); } -function findRootRouteFile(cwd) { - for (const rel of ROOT_ROUTE_CANDIDATES) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return null; -} - -function packageHasTanStackStart(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return START_PACKAGES.some((name) => Boolean(deps[name])); - } catch { - return false; - } -} - function relativeImportSpecifier(fromFile, toFile) { const rel = path.posix.relative( path.posix.dirname(fromFile.split(path.sep).join('/')), diff --git a/.cursor/skills/impeccable/scripts/live-poll.mjs b/.cursor/skills/impeccable/scripts/live-poll.mjs index 3b2f08c9f..59e21b9d3 100644 --- a/.cursor/skills/impeccable/scripts/live-poll.mjs +++ b/.cursor/skills/impeccable/scripts/live-poll.mjs @@ -238,10 +238,9 @@ export async function completeAcceptHandling(event, base, token) { }); } catch (err) { event._completionAck = { ok: false, error: err.message }; + return event; } - if (!event._completionAck) { - event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); - } + event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); return event; } @@ -269,9 +268,11 @@ export function printPollEvent(event) { // Situational plumbing rides with the event itself: `_instructions` is the // authoritative next step, with real ids and paths substituted, so the // reference doc can stay lean and can never drift from script behavior. - if (event && typeof event === 'object' && !event._instructions) { + // A wire-supplied value must never win over the locally generated one. + if (event && typeof event === 'object') { const instructions = instructionsForEvent(event, { scriptsPath: SELF_DIR }); if (instructions) event._instructions = instructions; + else delete event._instructions; } console.log(JSON.stringify(event)); } diff --git a/.cursor/skills/impeccable/scripts/live-server.mjs b/.cursor/skills/impeccable/scripts/live-server.mjs index cba3a222e..dafa8bd0c 100644 --- a/.cursor/skills/impeccable/scripts/live-server.mjs +++ b/.cursor/skills/impeccable/scripts/live-server.mjs @@ -181,8 +181,16 @@ function chatAgentLikelyActive() { // cap at 10 MB to guard against runaway writes from a misbehaving client. const MAX_ANNOTATION_BYTES = 10 * 1024 * 1024; +const POLLER_OWNED_EVENT_FIELDS = ['_instructions', '_completionAck', '_acceptResult']; + +function stripPollerOwnedEventFields(event) { + if (!event || typeof event !== 'object') return; + for (const key of POLLER_OWNED_EVENT_FIELDS) delete event[key]; +} + function enqueueEvent(event) { if (!event) return; + stripPollerOwnedEventFields(event); // Dedupe by (session, type), except mount failures, which are per-variant: // variant 2 failing must not be swallowed because variant 1's failure is // still queued. @@ -1034,6 +1042,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) { res.end(JSON.stringify({ error })); return; } + stripPollerOwnedEventFields(msg); if (msg.type === 'agent_phase') { recordAgentPhase(msg.id, msg.phase, { ...(Number.isFinite(msg.durationMs) ? { durationMs: msg.durationMs } : {}), diff --git a/.cursor/skills/impeccable/scripts/live/sveltekit-adapter.mjs b/.cursor/skills/impeccable/scripts/live/sveltekit-adapter.mjs index e94c54f1e..b96e955a6 100644 --- a/.cursor/skills/impeccable/scripts/live/sveltekit-adapter.mjs +++ b/.cursor/skills/impeccable/scripts/live/sveltekit-adapter.mjs @@ -11,6 +11,8 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; + export const SVELTE_LIVE_ROOT_COMPONENT = 'src/lib/impeccable/ImpeccableLiveRoot.svelte'; export const SVELTE_LAYOUT_MARKER_OPEN = ''; export const SVELTE_LAYOUT_MARKER_CLOSE = ''; @@ -45,11 +47,17 @@ export function detectSvelteKitProject(cwd = process.cwd(), config = null) { && fileIncludes(path.join(cwd, appHtml), '%sveltekit.head%'); if (!hasTemplateMarkers) return null; - const hasSvelteConfig = fs.existsSync(path.join(cwd, 'svelte.config.js')) - || fs.existsSync(path.join(cwd, 'svelte.config.mjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.cjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.ts')); - const hasKitPackage = packageHasSvelteKit(cwd); + const hasSvelteConfig = Boolean(firstExistingFile(cwd, [ + 'svelte.config.js', + 'svelte.config.mjs', + 'svelte.config.cjs', + 'svelte.config.ts', + ])); + const hasKitPackage = hasAnyDependency(cwd, [ + '@sveltejs/kit', + '@sveltejs/vite-plugin-svelte', + 'svelte', + ]); if (!hasSvelteConfig && !hasKitPackage) return null; return { @@ -260,36 +268,16 @@ function findSvelteKitAppHtml(cwd, config) { } function findSvelteKitLayout(cwd) { - const candidates = [ + return firstExistingFile(cwd, [ 'src/routes/+layout.svelte', 'src/routes/(app)/+layout.svelte', - ]; - for (const rel of candidates) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return 'src/routes/+layout.svelte'; + ]) || 'src/routes/+layout.svelte'; } function defaultSvelteLayout() { return `\n\n{@render children?.()}\n`; } -function packageHasSvelteKit(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return Boolean(deps['@sveltejs/kit'] || deps['@sveltejs/vite-plugin-svelte'] || deps.svelte); - } catch { - return false; - } -} - function fileIncludes(file, text) { try { return fs.readFileSync(file, 'utf-8').includes(text); diff --git a/.cursor/skills/impeccable/scripts/live/tanstack-adapter.mjs b/.cursor/skills/impeccable/scripts/live/tanstack-adapter.mjs index 4a1c81a97..b53978f4f 100644 --- a/.cursor/skills/impeccable/scripts/live/tanstack-adapter.mjs +++ b/.cursor/skills/impeccable/scripts/live/tanstack-adapter.mjs @@ -19,6 +19,8 @@ import fs from 'node:fs'; import path from 'node:path'; + +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; import { buildLiveScriptSrc } from './frameworks/script-src.mjs'; export const TANSTACK_MARKER_OPEN = '{/* impeccable-live-tanstack-start */}'; @@ -42,8 +44,8 @@ const START_PACKAGES = [ ]; export function detectTanStackStartProject(cwd = process.cwd()) { - if (!packageHasTanStackStart(cwd)) return null; - const rootRoute = findRootRouteFile(cwd); + if (!hasAnyDependency(cwd, START_PACKAGES)) return null; + const rootRoute = firstExistingFile(cwd, ROOT_ROUTE_CANDIDATES); if (!rootRoute) return null; const ext = path.extname(rootRoute); @@ -218,29 +220,6 @@ function isManagedComponent(content) { return String(content || '').includes('impeccable-live-tanstack'); } -function findRootRouteFile(cwd) { - for (const rel of ROOT_ROUTE_CANDIDATES) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return null; -} - -function packageHasTanStackStart(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return START_PACKAGES.some((name) => Boolean(deps[name])); - } catch { - return false; - } -} - function relativeImportSpecifier(fromFile, toFile) { const rel = path.posix.relative( path.posix.dirname(fromFile.split(path.sep).join('/')), diff --git a/.gemini/skills/impeccable/scripts/live-poll.mjs b/.gemini/skills/impeccable/scripts/live-poll.mjs index 3b2f08c9f..59e21b9d3 100644 --- a/.gemini/skills/impeccable/scripts/live-poll.mjs +++ b/.gemini/skills/impeccable/scripts/live-poll.mjs @@ -238,10 +238,9 @@ export async function completeAcceptHandling(event, base, token) { }); } catch (err) { event._completionAck = { ok: false, error: err.message }; + return event; } - if (!event._completionAck) { - event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); - } + event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); return event; } @@ -269,9 +268,11 @@ export function printPollEvent(event) { // Situational plumbing rides with the event itself: `_instructions` is the // authoritative next step, with real ids and paths substituted, so the // reference doc can stay lean and can never drift from script behavior. - if (event && typeof event === 'object' && !event._instructions) { + // A wire-supplied value must never win over the locally generated one. + if (event && typeof event === 'object') { const instructions = instructionsForEvent(event, { scriptsPath: SELF_DIR }); if (instructions) event._instructions = instructions; + else delete event._instructions; } console.log(JSON.stringify(event)); } diff --git a/.gemini/skills/impeccable/scripts/live-server.mjs b/.gemini/skills/impeccable/scripts/live-server.mjs index cba3a222e..dafa8bd0c 100644 --- a/.gemini/skills/impeccable/scripts/live-server.mjs +++ b/.gemini/skills/impeccable/scripts/live-server.mjs @@ -181,8 +181,16 @@ function chatAgentLikelyActive() { // cap at 10 MB to guard against runaway writes from a misbehaving client. const MAX_ANNOTATION_BYTES = 10 * 1024 * 1024; +const POLLER_OWNED_EVENT_FIELDS = ['_instructions', '_completionAck', '_acceptResult']; + +function stripPollerOwnedEventFields(event) { + if (!event || typeof event !== 'object') return; + for (const key of POLLER_OWNED_EVENT_FIELDS) delete event[key]; +} + function enqueueEvent(event) { if (!event) return; + stripPollerOwnedEventFields(event); // Dedupe by (session, type), except mount failures, which are per-variant: // variant 2 failing must not be swallowed because variant 1's failure is // still queued. @@ -1034,6 +1042,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) { res.end(JSON.stringify({ error })); return; } + stripPollerOwnedEventFields(msg); if (msg.type === 'agent_phase') { recordAgentPhase(msg.id, msg.phase, { ...(Number.isFinite(msg.durationMs) ? { durationMs: msg.durationMs } : {}), diff --git a/.gemini/skills/impeccable/scripts/live/sveltekit-adapter.mjs b/.gemini/skills/impeccable/scripts/live/sveltekit-adapter.mjs index e94c54f1e..b96e955a6 100644 --- a/.gemini/skills/impeccable/scripts/live/sveltekit-adapter.mjs +++ b/.gemini/skills/impeccable/scripts/live/sveltekit-adapter.mjs @@ -11,6 +11,8 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; + export const SVELTE_LIVE_ROOT_COMPONENT = 'src/lib/impeccable/ImpeccableLiveRoot.svelte'; export const SVELTE_LAYOUT_MARKER_OPEN = ''; export const SVELTE_LAYOUT_MARKER_CLOSE = ''; @@ -45,11 +47,17 @@ export function detectSvelteKitProject(cwd = process.cwd(), config = null) { && fileIncludes(path.join(cwd, appHtml), '%sveltekit.head%'); if (!hasTemplateMarkers) return null; - const hasSvelteConfig = fs.existsSync(path.join(cwd, 'svelte.config.js')) - || fs.existsSync(path.join(cwd, 'svelte.config.mjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.cjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.ts')); - const hasKitPackage = packageHasSvelteKit(cwd); + const hasSvelteConfig = Boolean(firstExistingFile(cwd, [ + 'svelte.config.js', + 'svelte.config.mjs', + 'svelte.config.cjs', + 'svelte.config.ts', + ])); + const hasKitPackage = hasAnyDependency(cwd, [ + '@sveltejs/kit', + '@sveltejs/vite-plugin-svelte', + 'svelte', + ]); if (!hasSvelteConfig && !hasKitPackage) return null; return { @@ -260,36 +268,16 @@ function findSvelteKitAppHtml(cwd, config) { } function findSvelteKitLayout(cwd) { - const candidates = [ + return firstExistingFile(cwd, [ 'src/routes/+layout.svelte', 'src/routes/(app)/+layout.svelte', - ]; - for (const rel of candidates) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return 'src/routes/+layout.svelte'; + ]) || 'src/routes/+layout.svelte'; } function defaultSvelteLayout() { return `\n\n{@render children?.()}\n`; } -function packageHasSvelteKit(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return Boolean(deps['@sveltejs/kit'] || deps['@sveltejs/vite-plugin-svelte'] || deps.svelte); - } catch { - return false; - } -} - function fileIncludes(file, text) { try { return fs.readFileSync(file, 'utf-8').includes(text); diff --git a/.gemini/skills/impeccable/scripts/live/tanstack-adapter.mjs b/.gemini/skills/impeccable/scripts/live/tanstack-adapter.mjs index 4a1c81a97..b53978f4f 100644 --- a/.gemini/skills/impeccable/scripts/live/tanstack-adapter.mjs +++ b/.gemini/skills/impeccable/scripts/live/tanstack-adapter.mjs @@ -19,6 +19,8 @@ import fs from 'node:fs'; import path from 'node:path'; + +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; import { buildLiveScriptSrc } from './frameworks/script-src.mjs'; export const TANSTACK_MARKER_OPEN = '{/* impeccable-live-tanstack-start */}'; @@ -42,8 +44,8 @@ const START_PACKAGES = [ ]; export function detectTanStackStartProject(cwd = process.cwd()) { - if (!packageHasTanStackStart(cwd)) return null; - const rootRoute = findRootRouteFile(cwd); + if (!hasAnyDependency(cwd, START_PACKAGES)) return null; + const rootRoute = firstExistingFile(cwd, ROOT_ROUTE_CANDIDATES); if (!rootRoute) return null; const ext = path.extname(rootRoute); @@ -218,29 +220,6 @@ function isManagedComponent(content) { return String(content || '').includes('impeccable-live-tanstack'); } -function findRootRouteFile(cwd) { - for (const rel of ROOT_ROUTE_CANDIDATES) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return null; -} - -function packageHasTanStackStart(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return START_PACKAGES.some((name) => Boolean(deps[name])); - } catch { - return false; - } -} - function relativeImportSpecifier(fromFile, toFile) { const rel = path.posix.relative( path.posix.dirname(fromFile.split(path.sep).join('/')), diff --git a/.github/skills/impeccable/scripts/live-poll.mjs b/.github/skills/impeccable/scripts/live-poll.mjs index 3b2f08c9f..59e21b9d3 100644 --- a/.github/skills/impeccable/scripts/live-poll.mjs +++ b/.github/skills/impeccable/scripts/live-poll.mjs @@ -238,10 +238,9 @@ export async function completeAcceptHandling(event, base, token) { }); } catch (err) { event._completionAck = { ok: false, error: err.message }; + return event; } - if (!event._completionAck) { - event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); - } + event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); return event; } @@ -269,9 +268,11 @@ export function printPollEvent(event) { // Situational plumbing rides with the event itself: `_instructions` is the // authoritative next step, with real ids and paths substituted, so the // reference doc can stay lean and can never drift from script behavior. - if (event && typeof event === 'object' && !event._instructions) { + // A wire-supplied value must never win over the locally generated one. + if (event && typeof event === 'object') { const instructions = instructionsForEvent(event, { scriptsPath: SELF_DIR }); if (instructions) event._instructions = instructions; + else delete event._instructions; } console.log(JSON.stringify(event)); } diff --git a/.github/skills/impeccable/scripts/live-server.mjs b/.github/skills/impeccable/scripts/live-server.mjs index cba3a222e..dafa8bd0c 100644 --- a/.github/skills/impeccable/scripts/live-server.mjs +++ b/.github/skills/impeccable/scripts/live-server.mjs @@ -181,8 +181,16 @@ function chatAgentLikelyActive() { // cap at 10 MB to guard against runaway writes from a misbehaving client. const MAX_ANNOTATION_BYTES = 10 * 1024 * 1024; +const POLLER_OWNED_EVENT_FIELDS = ['_instructions', '_completionAck', '_acceptResult']; + +function stripPollerOwnedEventFields(event) { + if (!event || typeof event !== 'object') return; + for (const key of POLLER_OWNED_EVENT_FIELDS) delete event[key]; +} + function enqueueEvent(event) { if (!event) return; + stripPollerOwnedEventFields(event); // Dedupe by (session, type), except mount failures, which are per-variant: // variant 2 failing must not be swallowed because variant 1's failure is // still queued. @@ -1034,6 +1042,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) { res.end(JSON.stringify({ error })); return; } + stripPollerOwnedEventFields(msg); if (msg.type === 'agent_phase') { recordAgentPhase(msg.id, msg.phase, { ...(Number.isFinite(msg.durationMs) ? { durationMs: msg.durationMs } : {}), diff --git a/.github/skills/impeccable/scripts/live/sveltekit-adapter.mjs b/.github/skills/impeccable/scripts/live/sveltekit-adapter.mjs index e94c54f1e..b96e955a6 100644 --- a/.github/skills/impeccable/scripts/live/sveltekit-adapter.mjs +++ b/.github/skills/impeccable/scripts/live/sveltekit-adapter.mjs @@ -11,6 +11,8 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; + export const SVELTE_LIVE_ROOT_COMPONENT = 'src/lib/impeccable/ImpeccableLiveRoot.svelte'; export const SVELTE_LAYOUT_MARKER_OPEN = ''; export const SVELTE_LAYOUT_MARKER_CLOSE = ''; @@ -45,11 +47,17 @@ export function detectSvelteKitProject(cwd = process.cwd(), config = null) { && fileIncludes(path.join(cwd, appHtml), '%sveltekit.head%'); if (!hasTemplateMarkers) return null; - const hasSvelteConfig = fs.existsSync(path.join(cwd, 'svelte.config.js')) - || fs.existsSync(path.join(cwd, 'svelte.config.mjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.cjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.ts')); - const hasKitPackage = packageHasSvelteKit(cwd); + const hasSvelteConfig = Boolean(firstExistingFile(cwd, [ + 'svelte.config.js', + 'svelte.config.mjs', + 'svelte.config.cjs', + 'svelte.config.ts', + ])); + const hasKitPackage = hasAnyDependency(cwd, [ + '@sveltejs/kit', + '@sveltejs/vite-plugin-svelte', + 'svelte', + ]); if (!hasSvelteConfig && !hasKitPackage) return null; return { @@ -260,36 +268,16 @@ function findSvelteKitAppHtml(cwd, config) { } function findSvelteKitLayout(cwd) { - const candidates = [ + return firstExistingFile(cwd, [ 'src/routes/+layout.svelte', 'src/routes/(app)/+layout.svelte', - ]; - for (const rel of candidates) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return 'src/routes/+layout.svelte'; + ]) || 'src/routes/+layout.svelte'; } function defaultSvelteLayout() { return `\n\n{@render children?.()}\n`; } -function packageHasSvelteKit(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return Boolean(deps['@sveltejs/kit'] || deps['@sveltejs/vite-plugin-svelte'] || deps.svelte); - } catch { - return false; - } -} - function fileIncludes(file, text) { try { return fs.readFileSync(file, 'utf-8').includes(text); diff --git a/.github/skills/impeccable/scripts/live/tanstack-adapter.mjs b/.github/skills/impeccable/scripts/live/tanstack-adapter.mjs index 4a1c81a97..b53978f4f 100644 --- a/.github/skills/impeccable/scripts/live/tanstack-adapter.mjs +++ b/.github/skills/impeccable/scripts/live/tanstack-adapter.mjs @@ -19,6 +19,8 @@ import fs from 'node:fs'; import path from 'node:path'; + +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; import { buildLiveScriptSrc } from './frameworks/script-src.mjs'; export const TANSTACK_MARKER_OPEN = '{/* impeccable-live-tanstack-start */}'; @@ -42,8 +44,8 @@ const START_PACKAGES = [ ]; export function detectTanStackStartProject(cwd = process.cwd()) { - if (!packageHasTanStackStart(cwd)) return null; - const rootRoute = findRootRouteFile(cwd); + if (!hasAnyDependency(cwd, START_PACKAGES)) return null; + const rootRoute = firstExistingFile(cwd, ROOT_ROUTE_CANDIDATES); if (!rootRoute) return null; const ext = path.extname(rootRoute); @@ -218,29 +220,6 @@ function isManagedComponent(content) { return String(content || '').includes('impeccable-live-tanstack'); } -function findRootRouteFile(cwd) { - for (const rel of ROOT_ROUTE_CANDIDATES) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return null; -} - -function packageHasTanStackStart(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return START_PACKAGES.some((name) => Boolean(deps[name])); - } catch { - return false; - } -} - function relativeImportSpecifier(fromFile, toFile) { const rel = path.posix.relative( path.posix.dirname(fromFile.split(path.sep).join('/')), diff --git a/.grok/skills/impeccable/scripts/live-poll.mjs b/.grok/skills/impeccable/scripts/live-poll.mjs index 3b2f08c9f..59e21b9d3 100644 --- a/.grok/skills/impeccable/scripts/live-poll.mjs +++ b/.grok/skills/impeccable/scripts/live-poll.mjs @@ -238,10 +238,9 @@ export async function completeAcceptHandling(event, base, token) { }); } catch (err) { event._completionAck = { ok: false, error: err.message }; + return event; } - if (!event._completionAck) { - event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); - } + event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); return event; } @@ -269,9 +268,11 @@ export function printPollEvent(event) { // Situational plumbing rides with the event itself: `_instructions` is the // authoritative next step, with real ids and paths substituted, so the // reference doc can stay lean and can never drift from script behavior. - if (event && typeof event === 'object' && !event._instructions) { + // A wire-supplied value must never win over the locally generated one. + if (event && typeof event === 'object') { const instructions = instructionsForEvent(event, { scriptsPath: SELF_DIR }); if (instructions) event._instructions = instructions; + else delete event._instructions; } console.log(JSON.stringify(event)); } diff --git a/.grok/skills/impeccable/scripts/live-server.mjs b/.grok/skills/impeccable/scripts/live-server.mjs index cba3a222e..dafa8bd0c 100644 --- a/.grok/skills/impeccable/scripts/live-server.mjs +++ b/.grok/skills/impeccable/scripts/live-server.mjs @@ -181,8 +181,16 @@ function chatAgentLikelyActive() { // cap at 10 MB to guard against runaway writes from a misbehaving client. const MAX_ANNOTATION_BYTES = 10 * 1024 * 1024; +const POLLER_OWNED_EVENT_FIELDS = ['_instructions', '_completionAck', '_acceptResult']; + +function stripPollerOwnedEventFields(event) { + if (!event || typeof event !== 'object') return; + for (const key of POLLER_OWNED_EVENT_FIELDS) delete event[key]; +} + function enqueueEvent(event) { if (!event) return; + stripPollerOwnedEventFields(event); // Dedupe by (session, type), except mount failures, which are per-variant: // variant 2 failing must not be swallowed because variant 1's failure is // still queued. @@ -1034,6 +1042,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) { res.end(JSON.stringify({ error })); return; } + stripPollerOwnedEventFields(msg); if (msg.type === 'agent_phase') { recordAgentPhase(msg.id, msg.phase, { ...(Number.isFinite(msg.durationMs) ? { durationMs: msg.durationMs } : {}), diff --git a/.grok/skills/impeccable/scripts/live/sveltekit-adapter.mjs b/.grok/skills/impeccable/scripts/live/sveltekit-adapter.mjs index e94c54f1e..b96e955a6 100644 --- a/.grok/skills/impeccable/scripts/live/sveltekit-adapter.mjs +++ b/.grok/skills/impeccable/scripts/live/sveltekit-adapter.mjs @@ -11,6 +11,8 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; + export const SVELTE_LIVE_ROOT_COMPONENT = 'src/lib/impeccable/ImpeccableLiveRoot.svelte'; export const SVELTE_LAYOUT_MARKER_OPEN = ''; export const SVELTE_LAYOUT_MARKER_CLOSE = ''; @@ -45,11 +47,17 @@ export function detectSvelteKitProject(cwd = process.cwd(), config = null) { && fileIncludes(path.join(cwd, appHtml), '%sveltekit.head%'); if (!hasTemplateMarkers) return null; - const hasSvelteConfig = fs.existsSync(path.join(cwd, 'svelte.config.js')) - || fs.existsSync(path.join(cwd, 'svelte.config.mjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.cjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.ts')); - const hasKitPackage = packageHasSvelteKit(cwd); + const hasSvelteConfig = Boolean(firstExistingFile(cwd, [ + 'svelte.config.js', + 'svelte.config.mjs', + 'svelte.config.cjs', + 'svelte.config.ts', + ])); + const hasKitPackage = hasAnyDependency(cwd, [ + '@sveltejs/kit', + '@sveltejs/vite-plugin-svelte', + 'svelte', + ]); if (!hasSvelteConfig && !hasKitPackage) return null; return { @@ -260,36 +268,16 @@ function findSvelteKitAppHtml(cwd, config) { } function findSvelteKitLayout(cwd) { - const candidates = [ + return firstExistingFile(cwd, [ 'src/routes/+layout.svelte', 'src/routes/(app)/+layout.svelte', - ]; - for (const rel of candidates) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return 'src/routes/+layout.svelte'; + ]) || 'src/routes/+layout.svelte'; } function defaultSvelteLayout() { return `\n\n{@render children?.()}\n`; } -function packageHasSvelteKit(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return Boolean(deps['@sveltejs/kit'] || deps['@sveltejs/vite-plugin-svelte'] || deps.svelte); - } catch { - return false; - } -} - function fileIncludes(file, text) { try { return fs.readFileSync(file, 'utf-8').includes(text); diff --git a/.grok/skills/impeccable/scripts/live/tanstack-adapter.mjs b/.grok/skills/impeccable/scripts/live/tanstack-adapter.mjs index 4a1c81a97..b53978f4f 100644 --- a/.grok/skills/impeccable/scripts/live/tanstack-adapter.mjs +++ b/.grok/skills/impeccable/scripts/live/tanstack-adapter.mjs @@ -19,6 +19,8 @@ import fs from 'node:fs'; import path from 'node:path'; + +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; import { buildLiveScriptSrc } from './frameworks/script-src.mjs'; export const TANSTACK_MARKER_OPEN = '{/* impeccable-live-tanstack-start */}'; @@ -42,8 +44,8 @@ const START_PACKAGES = [ ]; export function detectTanStackStartProject(cwd = process.cwd()) { - if (!packageHasTanStackStart(cwd)) return null; - const rootRoute = findRootRouteFile(cwd); + if (!hasAnyDependency(cwd, START_PACKAGES)) return null; + const rootRoute = firstExistingFile(cwd, ROOT_ROUTE_CANDIDATES); if (!rootRoute) return null; const ext = path.extname(rootRoute); @@ -218,29 +220,6 @@ function isManagedComponent(content) { return String(content || '').includes('impeccable-live-tanstack'); } -function findRootRouteFile(cwd) { - for (const rel of ROOT_ROUTE_CANDIDATES) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return null; -} - -function packageHasTanStackStart(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return START_PACKAGES.some((name) => Boolean(deps[name])); - } catch { - return false; - } -} - function relativeImportSpecifier(fromFile, toFile) { const rel = path.posix.relative( path.posix.dirname(fromFile.split(path.sep).join('/')), diff --git a/.hermes/skills/impeccable/scripts/live-poll.mjs b/.hermes/skills/impeccable/scripts/live-poll.mjs index 3b2f08c9f..59e21b9d3 100644 --- a/.hermes/skills/impeccable/scripts/live-poll.mjs +++ b/.hermes/skills/impeccable/scripts/live-poll.mjs @@ -238,10 +238,9 @@ export async function completeAcceptHandling(event, base, token) { }); } catch (err) { event._completionAck = { ok: false, error: err.message }; + return event; } - if (!event._completionAck) { - event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); - } + event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); return event; } @@ -269,9 +268,11 @@ export function printPollEvent(event) { // Situational plumbing rides with the event itself: `_instructions` is the // authoritative next step, with real ids and paths substituted, so the // reference doc can stay lean and can never drift from script behavior. - if (event && typeof event === 'object' && !event._instructions) { + // A wire-supplied value must never win over the locally generated one. + if (event && typeof event === 'object') { const instructions = instructionsForEvent(event, { scriptsPath: SELF_DIR }); if (instructions) event._instructions = instructions; + else delete event._instructions; } console.log(JSON.stringify(event)); } diff --git a/.hermes/skills/impeccable/scripts/live-server.mjs b/.hermes/skills/impeccable/scripts/live-server.mjs index cba3a222e..dafa8bd0c 100644 --- a/.hermes/skills/impeccable/scripts/live-server.mjs +++ b/.hermes/skills/impeccable/scripts/live-server.mjs @@ -181,8 +181,16 @@ function chatAgentLikelyActive() { // cap at 10 MB to guard against runaway writes from a misbehaving client. const MAX_ANNOTATION_BYTES = 10 * 1024 * 1024; +const POLLER_OWNED_EVENT_FIELDS = ['_instructions', '_completionAck', '_acceptResult']; + +function stripPollerOwnedEventFields(event) { + if (!event || typeof event !== 'object') return; + for (const key of POLLER_OWNED_EVENT_FIELDS) delete event[key]; +} + function enqueueEvent(event) { if (!event) return; + stripPollerOwnedEventFields(event); // Dedupe by (session, type), except mount failures, which are per-variant: // variant 2 failing must not be swallowed because variant 1's failure is // still queued. @@ -1034,6 +1042,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) { res.end(JSON.stringify({ error })); return; } + stripPollerOwnedEventFields(msg); if (msg.type === 'agent_phase') { recordAgentPhase(msg.id, msg.phase, { ...(Number.isFinite(msg.durationMs) ? { durationMs: msg.durationMs } : {}), diff --git a/.hermes/skills/impeccable/scripts/live/sveltekit-adapter.mjs b/.hermes/skills/impeccable/scripts/live/sveltekit-adapter.mjs index e94c54f1e..b96e955a6 100644 --- a/.hermes/skills/impeccable/scripts/live/sveltekit-adapter.mjs +++ b/.hermes/skills/impeccable/scripts/live/sveltekit-adapter.mjs @@ -11,6 +11,8 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; + export const SVELTE_LIVE_ROOT_COMPONENT = 'src/lib/impeccable/ImpeccableLiveRoot.svelte'; export const SVELTE_LAYOUT_MARKER_OPEN = ''; export const SVELTE_LAYOUT_MARKER_CLOSE = ''; @@ -45,11 +47,17 @@ export function detectSvelteKitProject(cwd = process.cwd(), config = null) { && fileIncludes(path.join(cwd, appHtml), '%sveltekit.head%'); if (!hasTemplateMarkers) return null; - const hasSvelteConfig = fs.existsSync(path.join(cwd, 'svelte.config.js')) - || fs.existsSync(path.join(cwd, 'svelte.config.mjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.cjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.ts')); - const hasKitPackage = packageHasSvelteKit(cwd); + const hasSvelteConfig = Boolean(firstExistingFile(cwd, [ + 'svelte.config.js', + 'svelte.config.mjs', + 'svelte.config.cjs', + 'svelte.config.ts', + ])); + const hasKitPackage = hasAnyDependency(cwd, [ + '@sveltejs/kit', + '@sveltejs/vite-plugin-svelte', + 'svelte', + ]); if (!hasSvelteConfig && !hasKitPackage) return null; return { @@ -260,36 +268,16 @@ function findSvelteKitAppHtml(cwd, config) { } function findSvelteKitLayout(cwd) { - const candidates = [ + return firstExistingFile(cwd, [ 'src/routes/+layout.svelte', 'src/routes/(app)/+layout.svelte', - ]; - for (const rel of candidates) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return 'src/routes/+layout.svelte'; + ]) || 'src/routes/+layout.svelte'; } function defaultSvelteLayout() { return `\n\n{@render children?.()}\n`; } -function packageHasSvelteKit(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return Boolean(deps['@sveltejs/kit'] || deps['@sveltejs/vite-plugin-svelte'] || deps.svelte); - } catch { - return false; - } -} - function fileIncludes(file, text) { try { return fs.readFileSync(file, 'utf-8').includes(text); diff --git a/.hermes/skills/impeccable/scripts/live/tanstack-adapter.mjs b/.hermes/skills/impeccable/scripts/live/tanstack-adapter.mjs index 4a1c81a97..b53978f4f 100644 --- a/.hermes/skills/impeccable/scripts/live/tanstack-adapter.mjs +++ b/.hermes/skills/impeccable/scripts/live/tanstack-adapter.mjs @@ -19,6 +19,8 @@ import fs from 'node:fs'; import path from 'node:path'; + +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; import { buildLiveScriptSrc } from './frameworks/script-src.mjs'; export const TANSTACK_MARKER_OPEN = '{/* impeccable-live-tanstack-start */}'; @@ -42,8 +44,8 @@ const START_PACKAGES = [ ]; export function detectTanStackStartProject(cwd = process.cwd()) { - if (!packageHasTanStackStart(cwd)) return null; - const rootRoute = findRootRouteFile(cwd); + if (!hasAnyDependency(cwd, START_PACKAGES)) return null; + const rootRoute = firstExistingFile(cwd, ROOT_ROUTE_CANDIDATES); if (!rootRoute) return null; const ext = path.extname(rootRoute); @@ -218,29 +220,6 @@ function isManagedComponent(content) { return String(content || '').includes('impeccable-live-tanstack'); } -function findRootRouteFile(cwd) { - for (const rel of ROOT_ROUTE_CANDIDATES) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return null; -} - -function packageHasTanStackStart(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return START_PACKAGES.some((name) => Boolean(deps[name])); - } catch { - return false; - } -} - function relativeImportSpecifier(fromFile, toFile) { const rel = path.posix.relative( path.posix.dirname(fromFile.split(path.sep).join('/')), diff --git a/.kiro/skills/impeccable/scripts/live-poll.mjs b/.kiro/skills/impeccable/scripts/live-poll.mjs index 3b2f08c9f..59e21b9d3 100644 --- a/.kiro/skills/impeccable/scripts/live-poll.mjs +++ b/.kiro/skills/impeccable/scripts/live-poll.mjs @@ -238,10 +238,9 @@ export async function completeAcceptHandling(event, base, token) { }); } catch (err) { event._completionAck = { ok: false, error: err.message }; + return event; } - if (!event._completionAck) { - event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); - } + event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); return event; } @@ -269,9 +268,11 @@ export function printPollEvent(event) { // Situational plumbing rides with the event itself: `_instructions` is the // authoritative next step, with real ids and paths substituted, so the // reference doc can stay lean and can never drift from script behavior. - if (event && typeof event === 'object' && !event._instructions) { + // A wire-supplied value must never win over the locally generated one. + if (event && typeof event === 'object') { const instructions = instructionsForEvent(event, { scriptsPath: SELF_DIR }); if (instructions) event._instructions = instructions; + else delete event._instructions; } console.log(JSON.stringify(event)); } diff --git a/.kiro/skills/impeccable/scripts/live-server.mjs b/.kiro/skills/impeccable/scripts/live-server.mjs index cba3a222e..dafa8bd0c 100644 --- a/.kiro/skills/impeccable/scripts/live-server.mjs +++ b/.kiro/skills/impeccable/scripts/live-server.mjs @@ -181,8 +181,16 @@ function chatAgentLikelyActive() { // cap at 10 MB to guard against runaway writes from a misbehaving client. const MAX_ANNOTATION_BYTES = 10 * 1024 * 1024; +const POLLER_OWNED_EVENT_FIELDS = ['_instructions', '_completionAck', '_acceptResult']; + +function stripPollerOwnedEventFields(event) { + if (!event || typeof event !== 'object') return; + for (const key of POLLER_OWNED_EVENT_FIELDS) delete event[key]; +} + function enqueueEvent(event) { if (!event) return; + stripPollerOwnedEventFields(event); // Dedupe by (session, type), except mount failures, which are per-variant: // variant 2 failing must not be swallowed because variant 1's failure is // still queued. @@ -1034,6 +1042,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) { res.end(JSON.stringify({ error })); return; } + stripPollerOwnedEventFields(msg); if (msg.type === 'agent_phase') { recordAgentPhase(msg.id, msg.phase, { ...(Number.isFinite(msg.durationMs) ? { durationMs: msg.durationMs } : {}), diff --git a/.kiro/skills/impeccable/scripts/live/sveltekit-adapter.mjs b/.kiro/skills/impeccable/scripts/live/sveltekit-adapter.mjs index e94c54f1e..b96e955a6 100644 --- a/.kiro/skills/impeccable/scripts/live/sveltekit-adapter.mjs +++ b/.kiro/skills/impeccable/scripts/live/sveltekit-adapter.mjs @@ -11,6 +11,8 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; + export const SVELTE_LIVE_ROOT_COMPONENT = 'src/lib/impeccable/ImpeccableLiveRoot.svelte'; export const SVELTE_LAYOUT_MARKER_OPEN = ''; export const SVELTE_LAYOUT_MARKER_CLOSE = ''; @@ -45,11 +47,17 @@ export function detectSvelteKitProject(cwd = process.cwd(), config = null) { && fileIncludes(path.join(cwd, appHtml), '%sveltekit.head%'); if (!hasTemplateMarkers) return null; - const hasSvelteConfig = fs.existsSync(path.join(cwd, 'svelte.config.js')) - || fs.existsSync(path.join(cwd, 'svelte.config.mjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.cjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.ts')); - const hasKitPackage = packageHasSvelteKit(cwd); + const hasSvelteConfig = Boolean(firstExistingFile(cwd, [ + 'svelte.config.js', + 'svelte.config.mjs', + 'svelte.config.cjs', + 'svelte.config.ts', + ])); + const hasKitPackage = hasAnyDependency(cwd, [ + '@sveltejs/kit', + '@sveltejs/vite-plugin-svelte', + 'svelte', + ]); if (!hasSvelteConfig && !hasKitPackage) return null; return { @@ -260,36 +268,16 @@ function findSvelteKitAppHtml(cwd, config) { } function findSvelteKitLayout(cwd) { - const candidates = [ + return firstExistingFile(cwd, [ 'src/routes/+layout.svelte', 'src/routes/(app)/+layout.svelte', - ]; - for (const rel of candidates) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return 'src/routes/+layout.svelte'; + ]) || 'src/routes/+layout.svelte'; } function defaultSvelteLayout() { return `\n\n{@render children?.()}\n`; } -function packageHasSvelteKit(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return Boolean(deps['@sveltejs/kit'] || deps['@sveltejs/vite-plugin-svelte'] || deps.svelte); - } catch { - return false; - } -} - function fileIncludes(file, text) { try { return fs.readFileSync(file, 'utf-8').includes(text); diff --git a/.kiro/skills/impeccable/scripts/live/tanstack-adapter.mjs b/.kiro/skills/impeccable/scripts/live/tanstack-adapter.mjs index 4a1c81a97..b53978f4f 100644 --- a/.kiro/skills/impeccable/scripts/live/tanstack-adapter.mjs +++ b/.kiro/skills/impeccable/scripts/live/tanstack-adapter.mjs @@ -19,6 +19,8 @@ import fs from 'node:fs'; import path from 'node:path'; + +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; import { buildLiveScriptSrc } from './frameworks/script-src.mjs'; export const TANSTACK_MARKER_OPEN = '{/* impeccable-live-tanstack-start */}'; @@ -42,8 +44,8 @@ const START_PACKAGES = [ ]; export function detectTanStackStartProject(cwd = process.cwd()) { - if (!packageHasTanStackStart(cwd)) return null; - const rootRoute = findRootRouteFile(cwd); + if (!hasAnyDependency(cwd, START_PACKAGES)) return null; + const rootRoute = firstExistingFile(cwd, ROOT_ROUTE_CANDIDATES); if (!rootRoute) return null; const ext = path.extname(rootRoute); @@ -218,29 +220,6 @@ function isManagedComponent(content) { return String(content || '').includes('impeccable-live-tanstack'); } -function findRootRouteFile(cwd) { - for (const rel of ROOT_ROUTE_CANDIDATES) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return null; -} - -function packageHasTanStackStart(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return START_PACKAGES.some((name) => Boolean(deps[name])); - } catch { - return false; - } -} - function relativeImportSpecifier(fromFile, toFile) { const rel = path.posix.relative( path.posix.dirname(fromFile.split(path.sep).join('/')), diff --git a/.opencode/skills/impeccable/scripts/live-poll.mjs b/.opencode/skills/impeccable/scripts/live-poll.mjs index 3b2f08c9f..59e21b9d3 100644 --- a/.opencode/skills/impeccable/scripts/live-poll.mjs +++ b/.opencode/skills/impeccable/scripts/live-poll.mjs @@ -238,10 +238,9 @@ export async function completeAcceptHandling(event, base, token) { }); } catch (err) { event._completionAck = { ok: false, error: err.message }; + return event; } - if (!event._completionAck) { - event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); - } + event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); return event; } @@ -269,9 +268,11 @@ export function printPollEvent(event) { // Situational plumbing rides with the event itself: `_instructions` is the // authoritative next step, with real ids and paths substituted, so the // reference doc can stay lean and can never drift from script behavior. - if (event && typeof event === 'object' && !event._instructions) { + // A wire-supplied value must never win over the locally generated one. + if (event && typeof event === 'object') { const instructions = instructionsForEvent(event, { scriptsPath: SELF_DIR }); if (instructions) event._instructions = instructions; + else delete event._instructions; } console.log(JSON.stringify(event)); } diff --git a/.opencode/skills/impeccable/scripts/live-server.mjs b/.opencode/skills/impeccable/scripts/live-server.mjs index cba3a222e..dafa8bd0c 100644 --- a/.opencode/skills/impeccable/scripts/live-server.mjs +++ b/.opencode/skills/impeccable/scripts/live-server.mjs @@ -181,8 +181,16 @@ function chatAgentLikelyActive() { // cap at 10 MB to guard against runaway writes from a misbehaving client. const MAX_ANNOTATION_BYTES = 10 * 1024 * 1024; +const POLLER_OWNED_EVENT_FIELDS = ['_instructions', '_completionAck', '_acceptResult']; + +function stripPollerOwnedEventFields(event) { + if (!event || typeof event !== 'object') return; + for (const key of POLLER_OWNED_EVENT_FIELDS) delete event[key]; +} + function enqueueEvent(event) { if (!event) return; + stripPollerOwnedEventFields(event); // Dedupe by (session, type), except mount failures, which are per-variant: // variant 2 failing must not be swallowed because variant 1's failure is // still queued. @@ -1034,6 +1042,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) { res.end(JSON.stringify({ error })); return; } + stripPollerOwnedEventFields(msg); if (msg.type === 'agent_phase') { recordAgentPhase(msg.id, msg.phase, { ...(Number.isFinite(msg.durationMs) ? { durationMs: msg.durationMs } : {}), diff --git a/.opencode/skills/impeccable/scripts/live/sveltekit-adapter.mjs b/.opencode/skills/impeccable/scripts/live/sveltekit-adapter.mjs index e94c54f1e..b96e955a6 100644 --- a/.opencode/skills/impeccable/scripts/live/sveltekit-adapter.mjs +++ b/.opencode/skills/impeccable/scripts/live/sveltekit-adapter.mjs @@ -11,6 +11,8 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; + export const SVELTE_LIVE_ROOT_COMPONENT = 'src/lib/impeccable/ImpeccableLiveRoot.svelte'; export const SVELTE_LAYOUT_MARKER_OPEN = ''; export const SVELTE_LAYOUT_MARKER_CLOSE = ''; @@ -45,11 +47,17 @@ export function detectSvelteKitProject(cwd = process.cwd(), config = null) { && fileIncludes(path.join(cwd, appHtml), '%sveltekit.head%'); if (!hasTemplateMarkers) return null; - const hasSvelteConfig = fs.existsSync(path.join(cwd, 'svelte.config.js')) - || fs.existsSync(path.join(cwd, 'svelte.config.mjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.cjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.ts')); - const hasKitPackage = packageHasSvelteKit(cwd); + const hasSvelteConfig = Boolean(firstExistingFile(cwd, [ + 'svelte.config.js', + 'svelte.config.mjs', + 'svelte.config.cjs', + 'svelte.config.ts', + ])); + const hasKitPackage = hasAnyDependency(cwd, [ + '@sveltejs/kit', + '@sveltejs/vite-plugin-svelte', + 'svelte', + ]); if (!hasSvelteConfig && !hasKitPackage) return null; return { @@ -260,36 +268,16 @@ function findSvelteKitAppHtml(cwd, config) { } function findSvelteKitLayout(cwd) { - const candidates = [ + return firstExistingFile(cwd, [ 'src/routes/+layout.svelte', 'src/routes/(app)/+layout.svelte', - ]; - for (const rel of candidates) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return 'src/routes/+layout.svelte'; + ]) || 'src/routes/+layout.svelte'; } function defaultSvelteLayout() { return `\n\n{@render children?.()}\n`; } -function packageHasSvelteKit(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return Boolean(deps['@sveltejs/kit'] || deps['@sveltejs/vite-plugin-svelte'] || deps.svelte); - } catch { - return false; - } -} - function fileIncludes(file, text) { try { return fs.readFileSync(file, 'utf-8').includes(text); diff --git a/.opencode/skills/impeccable/scripts/live/tanstack-adapter.mjs b/.opencode/skills/impeccable/scripts/live/tanstack-adapter.mjs index 4a1c81a97..b53978f4f 100644 --- a/.opencode/skills/impeccable/scripts/live/tanstack-adapter.mjs +++ b/.opencode/skills/impeccable/scripts/live/tanstack-adapter.mjs @@ -19,6 +19,8 @@ import fs from 'node:fs'; import path from 'node:path'; + +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; import { buildLiveScriptSrc } from './frameworks/script-src.mjs'; export const TANSTACK_MARKER_OPEN = '{/* impeccable-live-tanstack-start */}'; @@ -42,8 +44,8 @@ const START_PACKAGES = [ ]; export function detectTanStackStartProject(cwd = process.cwd()) { - if (!packageHasTanStackStart(cwd)) return null; - const rootRoute = findRootRouteFile(cwd); + if (!hasAnyDependency(cwd, START_PACKAGES)) return null; + const rootRoute = firstExistingFile(cwd, ROOT_ROUTE_CANDIDATES); if (!rootRoute) return null; const ext = path.extname(rootRoute); @@ -218,29 +220,6 @@ function isManagedComponent(content) { return String(content || '').includes('impeccable-live-tanstack'); } -function findRootRouteFile(cwd) { - for (const rel of ROOT_ROUTE_CANDIDATES) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return null; -} - -function packageHasTanStackStart(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return START_PACKAGES.some((name) => Boolean(deps[name])); - } catch { - return false; - } -} - function relativeImportSpecifier(fromFile, toFile) { const rel = path.posix.relative( path.posix.dirname(fromFile.split(path.sep).join('/')), diff --git a/.pi/skills/impeccable/scripts/live-poll.mjs b/.pi/skills/impeccable/scripts/live-poll.mjs index 3b2f08c9f..59e21b9d3 100644 --- a/.pi/skills/impeccable/scripts/live-poll.mjs +++ b/.pi/skills/impeccable/scripts/live-poll.mjs @@ -238,10 +238,9 @@ export async function completeAcceptHandling(event, base, token) { }); } catch (err) { event._completionAck = { ok: false, error: err.message }; + return event; } - if (!event._completionAck) { - event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); - } + event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); return event; } @@ -269,9 +268,11 @@ export function printPollEvent(event) { // Situational plumbing rides with the event itself: `_instructions` is the // authoritative next step, with real ids and paths substituted, so the // reference doc can stay lean and can never drift from script behavior. - if (event && typeof event === 'object' && !event._instructions) { + // A wire-supplied value must never win over the locally generated one. + if (event && typeof event === 'object') { const instructions = instructionsForEvent(event, { scriptsPath: SELF_DIR }); if (instructions) event._instructions = instructions; + else delete event._instructions; } console.log(JSON.stringify(event)); } diff --git a/.pi/skills/impeccable/scripts/live-server.mjs b/.pi/skills/impeccable/scripts/live-server.mjs index cba3a222e..dafa8bd0c 100644 --- a/.pi/skills/impeccable/scripts/live-server.mjs +++ b/.pi/skills/impeccable/scripts/live-server.mjs @@ -181,8 +181,16 @@ function chatAgentLikelyActive() { // cap at 10 MB to guard against runaway writes from a misbehaving client. const MAX_ANNOTATION_BYTES = 10 * 1024 * 1024; +const POLLER_OWNED_EVENT_FIELDS = ['_instructions', '_completionAck', '_acceptResult']; + +function stripPollerOwnedEventFields(event) { + if (!event || typeof event !== 'object') return; + for (const key of POLLER_OWNED_EVENT_FIELDS) delete event[key]; +} + function enqueueEvent(event) { if (!event) return; + stripPollerOwnedEventFields(event); // Dedupe by (session, type), except mount failures, which are per-variant: // variant 2 failing must not be swallowed because variant 1's failure is // still queued. @@ -1034,6 +1042,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) { res.end(JSON.stringify({ error })); return; } + stripPollerOwnedEventFields(msg); if (msg.type === 'agent_phase') { recordAgentPhase(msg.id, msg.phase, { ...(Number.isFinite(msg.durationMs) ? { durationMs: msg.durationMs } : {}), diff --git a/.pi/skills/impeccable/scripts/live/sveltekit-adapter.mjs b/.pi/skills/impeccable/scripts/live/sveltekit-adapter.mjs index e94c54f1e..b96e955a6 100644 --- a/.pi/skills/impeccable/scripts/live/sveltekit-adapter.mjs +++ b/.pi/skills/impeccable/scripts/live/sveltekit-adapter.mjs @@ -11,6 +11,8 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; + export const SVELTE_LIVE_ROOT_COMPONENT = 'src/lib/impeccable/ImpeccableLiveRoot.svelte'; export const SVELTE_LAYOUT_MARKER_OPEN = ''; export const SVELTE_LAYOUT_MARKER_CLOSE = ''; @@ -45,11 +47,17 @@ export function detectSvelteKitProject(cwd = process.cwd(), config = null) { && fileIncludes(path.join(cwd, appHtml), '%sveltekit.head%'); if (!hasTemplateMarkers) return null; - const hasSvelteConfig = fs.existsSync(path.join(cwd, 'svelte.config.js')) - || fs.existsSync(path.join(cwd, 'svelte.config.mjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.cjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.ts')); - const hasKitPackage = packageHasSvelteKit(cwd); + const hasSvelteConfig = Boolean(firstExistingFile(cwd, [ + 'svelte.config.js', + 'svelte.config.mjs', + 'svelte.config.cjs', + 'svelte.config.ts', + ])); + const hasKitPackage = hasAnyDependency(cwd, [ + '@sveltejs/kit', + '@sveltejs/vite-plugin-svelte', + 'svelte', + ]); if (!hasSvelteConfig && !hasKitPackage) return null; return { @@ -260,36 +268,16 @@ function findSvelteKitAppHtml(cwd, config) { } function findSvelteKitLayout(cwd) { - const candidates = [ + return firstExistingFile(cwd, [ 'src/routes/+layout.svelte', 'src/routes/(app)/+layout.svelte', - ]; - for (const rel of candidates) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return 'src/routes/+layout.svelte'; + ]) || 'src/routes/+layout.svelte'; } function defaultSvelteLayout() { return `\n\n{@render children?.()}\n`; } -function packageHasSvelteKit(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return Boolean(deps['@sveltejs/kit'] || deps['@sveltejs/vite-plugin-svelte'] || deps.svelte); - } catch { - return false; - } -} - function fileIncludes(file, text) { try { return fs.readFileSync(file, 'utf-8').includes(text); diff --git a/.pi/skills/impeccable/scripts/live/tanstack-adapter.mjs b/.pi/skills/impeccable/scripts/live/tanstack-adapter.mjs index 4a1c81a97..b53978f4f 100644 --- a/.pi/skills/impeccable/scripts/live/tanstack-adapter.mjs +++ b/.pi/skills/impeccable/scripts/live/tanstack-adapter.mjs @@ -19,6 +19,8 @@ import fs from 'node:fs'; import path from 'node:path'; + +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; import { buildLiveScriptSrc } from './frameworks/script-src.mjs'; export const TANSTACK_MARKER_OPEN = '{/* impeccable-live-tanstack-start */}'; @@ -42,8 +44,8 @@ const START_PACKAGES = [ ]; export function detectTanStackStartProject(cwd = process.cwd()) { - if (!packageHasTanStackStart(cwd)) return null; - const rootRoute = findRootRouteFile(cwd); + if (!hasAnyDependency(cwd, START_PACKAGES)) return null; + const rootRoute = firstExistingFile(cwd, ROOT_ROUTE_CANDIDATES); if (!rootRoute) return null; const ext = path.extname(rootRoute); @@ -218,29 +220,6 @@ function isManagedComponent(content) { return String(content || '').includes('impeccable-live-tanstack'); } -function findRootRouteFile(cwd) { - for (const rel of ROOT_ROUTE_CANDIDATES) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return null; -} - -function packageHasTanStackStart(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return START_PACKAGES.some((name) => Boolean(deps[name])); - } catch { - return false; - } -} - function relativeImportSpecifier(fromFile, toFile) { const rel = path.posix.relative( path.posix.dirname(fromFile.split(path.sep).join('/')), diff --git a/.qoder/skills/impeccable/scripts/live-poll.mjs b/.qoder/skills/impeccable/scripts/live-poll.mjs index 3b2f08c9f..59e21b9d3 100644 --- a/.qoder/skills/impeccable/scripts/live-poll.mjs +++ b/.qoder/skills/impeccable/scripts/live-poll.mjs @@ -238,10 +238,9 @@ export async function completeAcceptHandling(event, base, token) { }); } catch (err) { event._completionAck = { ok: false, error: err.message }; + return event; } - if (!event._completionAck) { - event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); - } + event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); return event; } @@ -269,9 +268,11 @@ export function printPollEvent(event) { // Situational plumbing rides with the event itself: `_instructions` is the // authoritative next step, with real ids and paths substituted, so the // reference doc can stay lean and can never drift from script behavior. - if (event && typeof event === 'object' && !event._instructions) { + // A wire-supplied value must never win over the locally generated one. + if (event && typeof event === 'object') { const instructions = instructionsForEvent(event, { scriptsPath: SELF_DIR }); if (instructions) event._instructions = instructions; + else delete event._instructions; } console.log(JSON.stringify(event)); } diff --git a/.qoder/skills/impeccable/scripts/live-server.mjs b/.qoder/skills/impeccable/scripts/live-server.mjs index cba3a222e..dafa8bd0c 100644 --- a/.qoder/skills/impeccable/scripts/live-server.mjs +++ b/.qoder/skills/impeccable/scripts/live-server.mjs @@ -181,8 +181,16 @@ function chatAgentLikelyActive() { // cap at 10 MB to guard against runaway writes from a misbehaving client. const MAX_ANNOTATION_BYTES = 10 * 1024 * 1024; +const POLLER_OWNED_EVENT_FIELDS = ['_instructions', '_completionAck', '_acceptResult']; + +function stripPollerOwnedEventFields(event) { + if (!event || typeof event !== 'object') return; + for (const key of POLLER_OWNED_EVENT_FIELDS) delete event[key]; +} + function enqueueEvent(event) { if (!event) return; + stripPollerOwnedEventFields(event); // Dedupe by (session, type), except mount failures, which are per-variant: // variant 2 failing must not be swallowed because variant 1's failure is // still queued. @@ -1034,6 +1042,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) { res.end(JSON.stringify({ error })); return; } + stripPollerOwnedEventFields(msg); if (msg.type === 'agent_phase') { recordAgentPhase(msg.id, msg.phase, { ...(Number.isFinite(msg.durationMs) ? { durationMs: msg.durationMs } : {}), diff --git a/.qoder/skills/impeccable/scripts/live/sveltekit-adapter.mjs b/.qoder/skills/impeccable/scripts/live/sveltekit-adapter.mjs index e94c54f1e..b96e955a6 100644 --- a/.qoder/skills/impeccable/scripts/live/sveltekit-adapter.mjs +++ b/.qoder/skills/impeccable/scripts/live/sveltekit-adapter.mjs @@ -11,6 +11,8 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; + export const SVELTE_LIVE_ROOT_COMPONENT = 'src/lib/impeccable/ImpeccableLiveRoot.svelte'; export const SVELTE_LAYOUT_MARKER_OPEN = ''; export const SVELTE_LAYOUT_MARKER_CLOSE = ''; @@ -45,11 +47,17 @@ export function detectSvelteKitProject(cwd = process.cwd(), config = null) { && fileIncludes(path.join(cwd, appHtml), '%sveltekit.head%'); if (!hasTemplateMarkers) return null; - const hasSvelteConfig = fs.existsSync(path.join(cwd, 'svelte.config.js')) - || fs.existsSync(path.join(cwd, 'svelte.config.mjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.cjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.ts')); - const hasKitPackage = packageHasSvelteKit(cwd); + const hasSvelteConfig = Boolean(firstExistingFile(cwd, [ + 'svelte.config.js', + 'svelte.config.mjs', + 'svelte.config.cjs', + 'svelte.config.ts', + ])); + const hasKitPackage = hasAnyDependency(cwd, [ + '@sveltejs/kit', + '@sveltejs/vite-plugin-svelte', + 'svelte', + ]); if (!hasSvelteConfig && !hasKitPackage) return null; return { @@ -260,36 +268,16 @@ function findSvelteKitAppHtml(cwd, config) { } function findSvelteKitLayout(cwd) { - const candidates = [ + return firstExistingFile(cwd, [ 'src/routes/+layout.svelte', 'src/routes/(app)/+layout.svelte', - ]; - for (const rel of candidates) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return 'src/routes/+layout.svelte'; + ]) || 'src/routes/+layout.svelte'; } function defaultSvelteLayout() { return `\n\n{@render children?.()}\n`; } -function packageHasSvelteKit(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return Boolean(deps['@sveltejs/kit'] || deps['@sveltejs/vite-plugin-svelte'] || deps.svelte); - } catch { - return false; - } -} - function fileIncludes(file, text) { try { return fs.readFileSync(file, 'utf-8').includes(text); diff --git a/.qoder/skills/impeccable/scripts/live/tanstack-adapter.mjs b/.qoder/skills/impeccable/scripts/live/tanstack-adapter.mjs index 4a1c81a97..b53978f4f 100644 --- a/.qoder/skills/impeccable/scripts/live/tanstack-adapter.mjs +++ b/.qoder/skills/impeccable/scripts/live/tanstack-adapter.mjs @@ -19,6 +19,8 @@ import fs from 'node:fs'; import path from 'node:path'; + +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; import { buildLiveScriptSrc } from './frameworks/script-src.mjs'; export const TANSTACK_MARKER_OPEN = '{/* impeccable-live-tanstack-start */}'; @@ -42,8 +44,8 @@ const START_PACKAGES = [ ]; export function detectTanStackStartProject(cwd = process.cwd()) { - if (!packageHasTanStackStart(cwd)) return null; - const rootRoute = findRootRouteFile(cwd); + if (!hasAnyDependency(cwd, START_PACKAGES)) return null; + const rootRoute = firstExistingFile(cwd, ROOT_ROUTE_CANDIDATES); if (!rootRoute) return null; const ext = path.extname(rootRoute); @@ -218,29 +220,6 @@ function isManagedComponent(content) { return String(content || '').includes('impeccable-live-tanstack'); } -function findRootRouteFile(cwd) { - for (const rel of ROOT_ROUTE_CANDIDATES) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return null; -} - -function packageHasTanStackStart(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return START_PACKAGES.some((name) => Boolean(deps[name])); - } catch { - return false; - } -} - function relativeImportSpecifier(fromFile, toFile) { const rel = path.posix.relative( path.posix.dirname(fromFile.split(path.sep).join('/')), diff --git a/.rovodev/skills/impeccable/scripts/live-poll.mjs b/.rovodev/skills/impeccable/scripts/live-poll.mjs index 3b2f08c9f..59e21b9d3 100644 --- a/.rovodev/skills/impeccable/scripts/live-poll.mjs +++ b/.rovodev/skills/impeccable/scripts/live-poll.mjs @@ -238,10 +238,9 @@ export async function completeAcceptHandling(event, base, token) { }); } catch (err) { event._completionAck = { ok: false, error: err.message }; + return event; } - if (!event._completionAck) { - event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); - } + event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); return event; } @@ -269,9 +268,11 @@ export function printPollEvent(event) { // Situational plumbing rides with the event itself: `_instructions` is the // authoritative next step, with real ids and paths substituted, so the // reference doc can stay lean and can never drift from script behavior. - if (event && typeof event === 'object' && !event._instructions) { + // A wire-supplied value must never win over the locally generated one. + if (event && typeof event === 'object') { const instructions = instructionsForEvent(event, { scriptsPath: SELF_DIR }); if (instructions) event._instructions = instructions; + else delete event._instructions; } console.log(JSON.stringify(event)); } diff --git a/.rovodev/skills/impeccable/scripts/live-server.mjs b/.rovodev/skills/impeccable/scripts/live-server.mjs index cba3a222e..dafa8bd0c 100644 --- a/.rovodev/skills/impeccable/scripts/live-server.mjs +++ b/.rovodev/skills/impeccable/scripts/live-server.mjs @@ -181,8 +181,16 @@ function chatAgentLikelyActive() { // cap at 10 MB to guard against runaway writes from a misbehaving client. const MAX_ANNOTATION_BYTES = 10 * 1024 * 1024; +const POLLER_OWNED_EVENT_FIELDS = ['_instructions', '_completionAck', '_acceptResult']; + +function stripPollerOwnedEventFields(event) { + if (!event || typeof event !== 'object') return; + for (const key of POLLER_OWNED_EVENT_FIELDS) delete event[key]; +} + function enqueueEvent(event) { if (!event) return; + stripPollerOwnedEventFields(event); // Dedupe by (session, type), except mount failures, which are per-variant: // variant 2 failing must not be swallowed because variant 1's failure is // still queued. @@ -1034,6 +1042,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) { res.end(JSON.stringify({ error })); return; } + stripPollerOwnedEventFields(msg); if (msg.type === 'agent_phase') { recordAgentPhase(msg.id, msg.phase, { ...(Number.isFinite(msg.durationMs) ? { durationMs: msg.durationMs } : {}), diff --git a/.rovodev/skills/impeccable/scripts/live/sveltekit-adapter.mjs b/.rovodev/skills/impeccable/scripts/live/sveltekit-adapter.mjs index e94c54f1e..b96e955a6 100644 --- a/.rovodev/skills/impeccable/scripts/live/sveltekit-adapter.mjs +++ b/.rovodev/skills/impeccable/scripts/live/sveltekit-adapter.mjs @@ -11,6 +11,8 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; + export const SVELTE_LIVE_ROOT_COMPONENT = 'src/lib/impeccable/ImpeccableLiveRoot.svelte'; export const SVELTE_LAYOUT_MARKER_OPEN = ''; export const SVELTE_LAYOUT_MARKER_CLOSE = ''; @@ -45,11 +47,17 @@ export function detectSvelteKitProject(cwd = process.cwd(), config = null) { && fileIncludes(path.join(cwd, appHtml), '%sveltekit.head%'); if (!hasTemplateMarkers) return null; - const hasSvelteConfig = fs.existsSync(path.join(cwd, 'svelte.config.js')) - || fs.existsSync(path.join(cwd, 'svelte.config.mjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.cjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.ts')); - const hasKitPackage = packageHasSvelteKit(cwd); + const hasSvelteConfig = Boolean(firstExistingFile(cwd, [ + 'svelte.config.js', + 'svelte.config.mjs', + 'svelte.config.cjs', + 'svelte.config.ts', + ])); + const hasKitPackage = hasAnyDependency(cwd, [ + '@sveltejs/kit', + '@sveltejs/vite-plugin-svelte', + 'svelte', + ]); if (!hasSvelteConfig && !hasKitPackage) return null; return { @@ -260,36 +268,16 @@ function findSvelteKitAppHtml(cwd, config) { } function findSvelteKitLayout(cwd) { - const candidates = [ + return firstExistingFile(cwd, [ 'src/routes/+layout.svelte', 'src/routes/(app)/+layout.svelte', - ]; - for (const rel of candidates) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return 'src/routes/+layout.svelte'; + ]) || 'src/routes/+layout.svelte'; } function defaultSvelteLayout() { return `\n\n{@render children?.()}\n`; } -function packageHasSvelteKit(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return Boolean(deps['@sveltejs/kit'] || deps['@sveltejs/vite-plugin-svelte'] || deps.svelte); - } catch { - return false; - } -} - function fileIncludes(file, text) { try { return fs.readFileSync(file, 'utf-8').includes(text); diff --git a/.rovodev/skills/impeccable/scripts/live/tanstack-adapter.mjs b/.rovodev/skills/impeccable/scripts/live/tanstack-adapter.mjs index 4a1c81a97..b53978f4f 100644 --- a/.rovodev/skills/impeccable/scripts/live/tanstack-adapter.mjs +++ b/.rovodev/skills/impeccable/scripts/live/tanstack-adapter.mjs @@ -19,6 +19,8 @@ import fs from 'node:fs'; import path from 'node:path'; + +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; import { buildLiveScriptSrc } from './frameworks/script-src.mjs'; export const TANSTACK_MARKER_OPEN = '{/* impeccable-live-tanstack-start */}'; @@ -42,8 +44,8 @@ const START_PACKAGES = [ ]; export function detectTanStackStartProject(cwd = process.cwd()) { - if (!packageHasTanStackStart(cwd)) return null; - const rootRoute = findRootRouteFile(cwd); + if (!hasAnyDependency(cwd, START_PACKAGES)) return null; + const rootRoute = firstExistingFile(cwd, ROOT_ROUTE_CANDIDATES); if (!rootRoute) return null; const ext = path.extname(rootRoute); @@ -218,29 +220,6 @@ function isManagedComponent(content) { return String(content || '').includes('impeccable-live-tanstack'); } -function findRootRouteFile(cwd) { - for (const rel of ROOT_ROUTE_CANDIDATES) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return null; -} - -function packageHasTanStackStart(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return START_PACKAGES.some((name) => Boolean(deps[name])); - } catch { - return false; - } -} - function relativeImportSpecifier(fromFile, toFile) { const rel = path.posix.relative( path.posix.dirname(fromFile.split(path.sep).join('/')), diff --git a/.trae-cn/skills/impeccable/scripts/live-poll.mjs b/.trae-cn/skills/impeccable/scripts/live-poll.mjs index 3b2f08c9f..59e21b9d3 100644 --- a/.trae-cn/skills/impeccable/scripts/live-poll.mjs +++ b/.trae-cn/skills/impeccable/scripts/live-poll.mjs @@ -238,10 +238,9 @@ export async function completeAcceptHandling(event, base, token) { }); } catch (err) { event._completionAck = { ok: false, error: err.message }; + return event; } - if (!event._completionAck) { - event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); - } + event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); return event; } @@ -269,9 +268,11 @@ export function printPollEvent(event) { // Situational plumbing rides with the event itself: `_instructions` is the // authoritative next step, with real ids and paths substituted, so the // reference doc can stay lean and can never drift from script behavior. - if (event && typeof event === 'object' && !event._instructions) { + // A wire-supplied value must never win over the locally generated one. + if (event && typeof event === 'object') { const instructions = instructionsForEvent(event, { scriptsPath: SELF_DIR }); if (instructions) event._instructions = instructions; + else delete event._instructions; } console.log(JSON.stringify(event)); } diff --git a/.trae-cn/skills/impeccable/scripts/live-server.mjs b/.trae-cn/skills/impeccable/scripts/live-server.mjs index cba3a222e..dafa8bd0c 100644 --- a/.trae-cn/skills/impeccable/scripts/live-server.mjs +++ b/.trae-cn/skills/impeccable/scripts/live-server.mjs @@ -181,8 +181,16 @@ function chatAgentLikelyActive() { // cap at 10 MB to guard against runaway writes from a misbehaving client. const MAX_ANNOTATION_BYTES = 10 * 1024 * 1024; +const POLLER_OWNED_EVENT_FIELDS = ['_instructions', '_completionAck', '_acceptResult']; + +function stripPollerOwnedEventFields(event) { + if (!event || typeof event !== 'object') return; + for (const key of POLLER_OWNED_EVENT_FIELDS) delete event[key]; +} + function enqueueEvent(event) { if (!event) return; + stripPollerOwnedEventFields(event); // Dedupe by (session, type), except mount failures, which are per-variant: // variant 2 failing must not be swallowed because variant 1's failure is // still queued. @@ -1034,6 +1042,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) { res.end(JSON.stringify({ error })); return; } + stripPollerOwnedEventFields(msg); if (msg.type === 'agent_phase') { recordAgentPhase(msg.id, msg.phase, { ...(Number.isFinite(msg.durationMs) ? { durationMs: msg.durationMs } : {}), diff --git a/.trae-cn/skills/impeccable/scripts/live/sveltekit-adapter.mjs b/.trae-cn/skills/impeccable/scripts/live/sveltekit-adapter.mjs index e94c54f1e..b96e955a6 100644 --- a/.trae-cn/skills/impeccable/scripts/live/sveltekit-adapter.mjs +++ b/.trae-cn/skills/impeccable/scripts/live/sveltekit-adapter.mjs @@ -11,6 +11,8 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; + export const SVELTE_LIVE_ROOT_COMPONENT = 'src/lib/impeccable/ImpeccableLiveRoot.svelte'; export const SVELTE_LAYOUT_MARKER_OPEN = ''; export const SVELTE_LAYOUT_MARKER_CLOSE = ''; @@ -45,11 +47,17 @@ export function detectSvelteKitProject(cwd = process.cwd(), config = null) { && fileIncludes(path.join(cwd, appHtml), '%sveltekit.head%'); if (!hasTemplateMarkers) return null; - const hasSvelteConfig = fs.existsSync(path.join(cwd, 'svelte.config.js')) - || fs.existsSync(path.join(cwd, 'svelte.config.mjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.cjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.ts')); - const hasKitPackage = packageHasSvelteKit(cwd); + const hasSvelteConfig = Boolean(firstExistingFile(cwd, [ + 'svelte.config.js', + 'svelte.config.mjs', + 'svelte.config.cjs', + 'svelte.config.ts', + ])); + const hasKitPackage = hasAnyDependency(cwd, [ + '@sveltejs/kit', + '@sveltejs/vite-plugin-svelte', + 'svelte', + ]); if (!hasSvelteConfig && !hasKitPackage) return null; return { @@ -260,36 +268,16 @@ function findSvelteKitAppHtml(cwd, config) { } function findSvelteKitLayout(cwd) { - const candidates = [ + return firstExistingFile(cwd, [ 'src/routes/+layout.svelte', 'src/routes/(app)/+layout.svelte', - ]; - for (const rel of candidates) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return 'src/routes/+layout.svelte'; + ]) || 'src/routes/+layout.svelte'; } function defaultSvelteLayout() { return `\n\n{@render children?.()}\n`; } -function packageHasSvelteKit(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return Boolean(deps['@sveltejs/kit'] || deps['@sveltejs/vite-plugin-svelte'] || deps.svelte); - } catch { - return false; - } -} - function fileIncludes(file, text) { try { return fs.readFileSync(file, 'utf-8').includes(text); diff --git a/.trae-cn/skills/impeccable/scripts/live/tanstack-adapter.mjs b/.trae-cn/skills/impeccable/scripts/live/tanstack-adapter.mjs index 4a1c81a97..b53978f4f 100644 --- a/.trae-cn/skills/impeccable/scripts/live/tanstack-adapter.mjs +++ b/.trae-cn/skills/impeccable/scripts/live/tanstack-adapter.mjs @@ -19,6 +19,8 @@ import fs from 'node:fs'; import path from 'node:path'; + +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; import { buildLiveScriptSrc } from './frameworks/script-src.mjs'; export const TANSTACK_MARKER_OPEN = '{/* impeccable-live-tanstack-start */}'; @@ -42,8 +44,8 @@ const START_PACKAGES = [ ]; export function detectTanStackStartProject(cwd = process.cwd()) { - if (!packageHasTanStackStart(cwd)) return null; - const rootRoute = findRootRouteFile(cwd); + if (!hasAnyDependency(cwd, START_PACKAGES)) return null; + const rootRoute = firstExistingFile(cwd, ROOT_ROUTE_CANDIDATES); if (!rootRoute) return null; const ext = path.extname(rootRoute); @@ -218,29 +220,6 @@ function isManagedComponent(content) { return String(content || '').includes('impeccable-live-tanstack'); } -function findRootRouteFile(cwd) { - for (const rel of ROOT_ROUTE_CANDIDATES) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return null; -} - -function packageHasTanStackStart(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return START_PACKAGES.some((name) => Boolean(deps[name])); - } catch { - return false; - } -} - function relativeImportSpecifier(fromFile, toFile) { const rel = path.posix.relative( path.posix.dirname(fromFile.split(path.sep).join('/')), diff --git a/.trae/skills/impeccable/scripts/live-poll.mjs b/.trae/skills/impeccable/scripts/live-poll.mjs index 3b2f08c9f..59e21b9d3 100644 --- a/.trae/skills/impeccable/scripts/live-poll.mjs +++ b/.trae/skills/impeccable/scripts/live-poll.mjs @@ -238,10 +238,9 @@ export async function completeAcceptHandling(event, base, token) { }); } catch (err) { event._completionAck = { ok: false, error: err.message }; + return event; } - if (!event._completionAck) { - event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); - } + event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); return event; } @@ -269,9 +268,11 @@ export function printPollEvent(event) { // Situational plumbing rides with the event itself: `_instructions` is the // authoritative next step, with real ids and paths substituted, so the // reference doc can stay lean and can never drift from script behavior. - if (event && typeof event === 'object' && !event._instructions) { + // A wire-supplied value must never win over the locally generated one. + if (event && typeof event === 'object') { const instructions = instructionsForEvent(event, { scriptsPath: SELF_DIR }); if (instructions) event._instructions = instructions; + else delete event._instructions; } console.log(JSON.stringify(event)); } diff --git a/.trae/skills/impeccable/scripts/live-server.mjs b/.trae/skills/impeccable/scripts/live-server.mjs index cba3a222e..dafa8bd0c 100644 --- a/.trae/skills/impeccable/scripts/live-server.mjs +++ b/.trae/skills/impeccable/scripts/live-server.mjs @@ -181,8 +181,16 @@ function chatAgentLikelyActive() { // cap at 10 MB to guard against runaway writes from a misbehaving client. const MAX_ANNOTATION_BYTES = 10 * 1024 * 1024; +const POLLER_OWNED_EVENT_FIELDS = ['_instructions', '_completionAck', '_acceptResult']; + +function stripPollerOwnedEventFields(event) { + if (!event || typeof event !== 'object') return; + for (const key of POLLER_OWNED_EVENT_FIELDS) delete event[key]; +} + function enqueueEvent(event) { if (!event) return; + stripPollerOwnedEventFields(event); // Dedupe by (session, type), except mount failures, which are per-variant: // variant 2 failing must not be swallowed because variant 1's failure is // still queued. @@ -1034,6 +1042,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) { res.end(JSON.stringify({ error })); return; } + stripPollerOwnedEventFields(msg); if (msg.type === 'agent_phase') { recordAgentPhase(msg.id, msg.phase, { ...(Number.isFinite(msg.durationMs) ? { durationMs: msg.durationMs } : {}), diff --git a/.trae/skills/impeccable/scripts/live/sveltekit-adapter.mjs b/.trae/skills/impeccable/scripts/live/sveltekit-adapter.mjs index e94c54f1e..b96e955a6 100644 --- a/.trae/skills/impeccable/scripts/live/sveltekit-adapter.mjs +++ b/.trae/skills/impeccable/scripts/live/sveltekit-adapter.mjs @@ -11,6 +11,8 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; + export const SVELTE_LIVE_ROOT_COMPONENT = 'src/lib/impeccable/ImpeccableLiveRoot.svelte'; export const SVELTE_LAYOUT_MARKER_OPEN = ''; export const SVELTE_LAYOUT_MARKER_CLOSE = ''; @@ -45,11 +47,17 @@ export function detectSvelteKitProject(cwd = process.cwd(), config = null) { && fileIncludes(path.join(cwd, appHtml), '%sveltekit.head%'); if (!hasTemplateMarkers) return null; - const hasSvelteConfig = fs.existsSync(path.join(cwd, 'svelte.config.js')) - || fs.existsSync(path.join(cwd, 'svelte.config.mjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.cjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.ts')); - const hasKitPackage = packageHasSvelteKit(cwd); + const hasSvelteConfig = Boolean(firstExistingFile(cwd, [ + 'svelte.config.js', + 'svelte.config.mjs', + 'svelte.config.cjs', + 'svelte.config.ts', + ])); + const hasKitPackage = hasAnyDependency(cwd, [ + '@sveltejs/kit', + '@sveltejs/vite-plugin-svelte', + 'svelte', + ]); if (!hasSvelteConfig && !hasKitPackage) return null; return { @@ -260,36 +268,16 @@ function findSvelteKitAppHtml(cwd, config) { } function findSvelteKitLayout(cwd) { - const candidates = [ + return firstExistingFile(cwd, [ 'src/routes/+layout.svelte', 'src/routes/(app)/+layout.svelte', - ]; - for (const rel of candidates) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return 'src/routes/+layout.svelte'; + ]) || 'src/routes/+layout.svelte'; } function defaultSvelteLayout() { return `\n\n{@render children?.()}\n`; } -function packageHasSvelteKit(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return Boolean(deps['@sveltejs/kit'] || deps['@sveltejs/vite-plugin-svelte'] || deps.svelte); - } catch { - return false; - } -} - function fileIncludes(file, text) { try { return fs.readFileSync(file, 'utf-8').includes(text); diff --git a/.trae/skills/impeccable/scripts/live/tanstack-adapter.mjs b/.trae/skills/impeccable/scripts/live/tanstack-adapter.mjs index 4a1c81a97..b53978f4f 100644 --- a/.trae/skills/impeccable/scripts/live/tanstack-adapter.mjs +++ b/.trae/skills/impeccable/scripts/live/tanstack-adapter.mjs @@ -19,6 +19,8 @@ import fs from 'node:fs'; import path from 'node:path'; + +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; import { buildLiveScriptSrc } from './frameworks/script-src.mjs'; export const TANSTACK_MARKER_OPEN = '{/* impeccable-live-tanstack-start */}'; @@ -42,8 +44,8 @@ const START_PACKAGES = [ ]; export function detectTanStackStartProject(cwd = process.cwd()) { - if (!packageHasTanStackStart(cwd)) return null; - const rootRoute = findRootRouteFile(cwd); + if (!hasAnyDependency(cwd, START_PACKAGES)) return null; + const rootRoute = firstExistingFile(cwd, ROOT_ROUTE_CANDIDATES); if (!rootRoute) return null; const ext = path.extname(rootRoute); @@ -218,29 +220,6 @@ function isManagedComponent(content) { return String(content || '').includes('impeccable-live-tanstack'); } -function findRootRouteFile(cwd) { - for (const rel of ROOT_ROUTE_CANDIDATES) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return null; -} - -function packageHasTanStackStart(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return START_PACKAGES.some((name) => Boolean(deps[name])); - } catch { - return false; - } -} - function relativeImportSpecifier(fromFile, toFile) { const rel = path.posix.relative( path.posix.dirname(fromFile.split(path.sep).join('/')), diff --git a/.vibe/skills/impeccable/scripts/live-poll.mjs b/.vibe/skills/impeccable/scripts/live-poll.mjs index 3b2f08c9f..59e21b9d3 100644 --- a/.vibe/skills/impeccable/scripts/live-poll.mjs +++ b/.vibe/skills/impeccable/scripts/live-poll.mjs @@ -238,10 +238,9 @@ export async function completeAcceptHandling(event, base, token) { }); } catch (err) { event._completionAck = { ok: false, error: err.message }; + return event; } - if (!event._completionAck) { - event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); - } + event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); return event; } @@ -269,9 +268,11 @@ export function printPollEvent(event) { // Situational plumbing rides with the event itself: `_instructions` is the // authoritative next step, with real ids and paths substituted, so the // reference doc can stay lean and can never drift from script behavior. - if (event && typeof event === 'object' && !event._instructions) { + // A wire-supplied value must never win over the locally generated one. + if (event && typeof event === 'object') { const instructions = instructionsForEvent(event, { scriptsPath: SELF_DIR }); if (instructions) event._instructions = instructions; + else delete event._instructions; } console.log(JSON.stringify(event)); } diff --git a/.vibe/skills/impeccable/scripts/live-server.mjs b/.vibe/skills/impeccable/scripts/live-server.mjs index cba3a222e..dafa8bd0c 100644 --- a/.vibe/skills/impeccable/scripts/live-server.mjs +++ b/.vibe/skills/impeccable/scripts/live-server.mjs @@ -181,8 +181,16 @@ function chatAgentLikelyActive() { // cap at 10 MB to guard against runaway writes from a misbehaving client. const MAX_ANNOTATION_BYTES = 10 * 1024 * 1024; +const POLLER_OWNED_EVENT_FIELDS = ['_instructions', '_completionAck', '_acceptResult']; + +function stripPollerOwnedEventFields(event) { + if (!event || typeof event !== 'object') return; + for (const key of POLLER_OWNED_EVENT_FIELDS) delete event[key]; +} + function enqueueEvent(event) { if (!event) return; + stripPollerOwnedEventFields(event); // Dedupe by (session, type), except mount failures, which are per-variant: // variant 2 failing must not be swallowed because variant 1's failure is // still queued. @@ -1034,6 +1042,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) { res.end(JSON.stringify({ error })); return; } + stripPollerOwnedEventFields(msg); if (msg.type === 'agent_phase') { recordAgentPhase(msg.id, msg.phase, { ...(Number.isFinite(msg.durationMs) ? { durationMs: msg.durationMs } : {}), diff --git a/.vibe/skills/impeccable/scripts/live/sveltekit-adapter.mjs b/.vibe/skills/impeccable/scripts/live/sveltekit-adapter.mjs index e94c54f1e..b96e955a6 100644 --- a/.vibe/skills/impeccable/scripts/live/sveltekit-adapter.mjs +++ b/.vibe/skills/impeccable/scripts/live/sveltekit-adapter.mjs @@ -11,6 +11,8 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; + export const SVELTE_LIVE_ROOT_COMPONENT = 'src/lib/impeccable/ImpeccableLiveRoot.svelte'; export const SVELTE_LAYOUT_MARKER_OPEN = ''; export const SVELTE_LAYOUT_MARKER_CLOSE = ''; @@ -45,11 +47,17 @@ export function detectSvelteKitProject(cwd = process.cwd(), config = null) { && fileIncludes(path.join(cwd, appHtml), '%sveltekit.head%'); if (!hasTemplateMarkers) return null; - const hasSvelteConfig = fs.existsSync(path.join(cwd, 'svelte.config.js')) - || fs.existsSync(path.join(cwd, 'svelte.config.mjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.cjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.ts')); - const hasKitPackage = packageHasSvelteKit(cwd); + const hasSvelteConfig = Boolean(firstExistingFile(cwd, [ + 'svelte.config.js', + 'svelte.config.mjs', + 'svelte.config.cjs', + 'svelte.config.ts', + ])); + const hasKitPackage = hasAnyDependency(cwd, [ + '@sveltejs/kit', + '@sveltejs/vite-plugin-svelte', + 'svelte', + ]); if (!hasSvelteConfig && !hasKitPackage) return null; return { @@ -260,36 +268,16 @@ function findSvelteKitAppHtml(cwd, config) { } function findSvelteKitLayout(cwd) { - const candidates = [ + return firstExistingFile(cwd, [ 'src/routes/+layout.svelte', 'src/routes/(app)/+layout.svelte', - ]; - for (const rel of candidates) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return 'src/routes/+layout.svelte'; + ]) || 'src/routes/+layout.svelte'; } function defaultSvelteLayout() { return `\n\n{@render children?.()}\n`; } -function packageHasSvelteKit(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return Boolean(deps['@sveltejs/kit'] || deps['@sveltejs/vite-plugin-svelte'] || deps.svelte); - } catch { - return false; - } -} - function fileIncludes(file, text) { try { return fs.readFileSync(file, 'utf-8').includes(text); diff --git a/.vibe/skills/impeccable/scripts/live/tanstack-adapter.mjs b/.vibe/skills/impeccable/scripts/live/tanstack-adapter.mjs index 4a1c81a97..b53978f4f 100644 --- a/.vibe/skills/impeccable/scripts/live/tanstack-adapter.mjs +++ b/.vibe/skills/impeccable/scripts/live/tanstack-adapter.mjs @@ -19,6 +19,8 @@ import fs from 'node:fs'; import path from 'node:path'; + +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; import { buildLiveScriptSrc } from './frameworks/script-src.mjs'; export const TANSTACK_MARKER_OPEN = '{/* impeccable-live-tanstack-start */}'; @@ -42,8 +44,8 @@ const START_PACKAGES = [ ]; export function detectTanStackStartProject(cwd = process.cwd()) { - if (!packageHasTanStackStart(cwd)) return null; - const rootRoute = findRootRouteFile(cwd); + if (!hasAnyDependency(cwd, START_PACKAGES)) return null; + const rootRoute = firstExistingFile(cwd, ROOT_ROUTE_CANDIDATES); if (!rootRoute) return null; const ext = path.extname(rootRoute); @@ -218,29 +220,6 @@ function isManagedComponent(content) { return String(content || '').includes('impeccable-live-tanstack'); } -function findRootRouteFile(cwd) { - for (const rel of ROOT_ROUTE_CANDIDATES) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return null; -} - -function packageHasTanStackStart(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return START_PACKAGES.some((name) => Boolean(deps[name])); - } catch { - return false; - } -} - function relativeImportSpecifier(fromFile, toFile) { const rel = path.posix.relative( path.posix.dirname(fromFile.split(path.sep).join('/')), diff --git a/plugin/skills/impeccable/scripts/live-poll.mjs b/plugin/skills/impeccable/scripts/live-poll.mjs index 3b2f08c9f..59e21b9d3 100644 --- a/plugin/skills/impeccable/scripts/live-poll.mjs +++ b/plugin/skills/impeccable/scripts/live-poll.mjs @@ -238,10 +238,9 @@ export async function completeAcceptHandling(event, base, token) { }); } catch (err) { event._completionAck = { ok: false, error: err.message }; + return event; } - if (!event._completionAck) { - event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); - } + event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult); return event; } @@ -269,9 +268,11 @@ export function printPollEvent(event) { // Situational plumbing rides with the event itself: `_instructions` is the // authoritative next step, with real ids and paths substituted, so the // reference doc can stay lean and can never drift from script behavior. - if (event && typeof event === 'object' && !event._instructions) { + // A wire-supplied value must never win over the locally generated one. + if (event && typeof event === 'object') { const instructions = instructionsForEvent(event, { scriptsPath: SELF_DIR }); if (instructions) event._instructions = instructions; + else delete event._instructions; } console.log(JSON.stringify(event)); } diff --git a/plugin/skills/impeccable/scripts/live-server.mjs b/plugin/skills/impeccable/scripts/live-server.mjs index cba3a222e..dafa8bd0c 100644 --- a/plugin/skills/impeccable/scripts/live-server.mjs +++ b/plugin/skills/impeccable/scripts/live-server.mjs @@ -181,8 +181,16 @@ function chatAgentLikelyActive() { // cap at 10 MB to guard against runaway writes from a misbehaving client. const MAX_ANNOTATION_BYTES = 10 * 1024 * 1024; +const POLLER_OWNED_EVENT_FIELDS = ['_instructions', '_completionAck', '_acceptResult']; + +function stripPollerOwnedEventFields(event) { + if (!event || typeof event !== 'object') return; + for (const key of POLLER_OWNED_EVENT_FIELDS) delete event[key]; +} + function enqueueEvent(event) { if (!event) return; + stripPollerOwnedEventFields(event); // Dedupe by (session, type), except mount failures, which are per-variant: // variant 2 failing must not be swallowed because variant 1's failure is // still queued. @@ -1034,6 +1042,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) { res.end(JSON.stringify({ error })); return; } + stripPollerOwnedEventFields(msg); if (msg.type === 'agent_phase') { recordAgentPhase(msg.id, msg.phase, { ...(Number.isFinite(msg.durationMs) ? { durationMs: msg.durationMs } : {}), diff --git a/plugin/skills/impeccable/scripts/live/sveltekit-adapter.mjs b/plugin/skills/impeccable/scripts/live/sveltekit-adapter.mjs index e94c54f1e..b96e955a6 100644 --- a/plugin/skills/impeccable/scripts/live/sveltekit-adapter.mjs +++ b/plugin/skills/impeccable/scripts/live/sveltekit-adapter.mjs @@ -11,6 +11,8 @@ import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; + export const SVELTE_LIVE_ROOT_COMPONENT = 'src/lib/impeccable/ImpeccableLiveRoot.svelte'; export const SVELTE_LAYOUT_MARKER_OPEN = ''; export const SVELTE_LAYOUT_MARKER_CLOSE = ''; @@ -45,11 +47,17 @@ export function detectSvelteKitProject(cwd = process.cwd(), config = null) { && fileIncludes(path.join(cwd, appHtml), '%sveltekit.head%'); if (!hasTemplateMarkers) return null; - const hasSvelteConfig = fs.existsSync(path.join(cwd, 'svelte.config.js')) - || fs.existsSync(path.join(cwd, 'svelte.config.mjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.cjs')) - || fs.existsSync(path.join(cwd, 'svelte.config.ts')); - const hasKitPackage = packageHasSvelteKit(cwd); + const hasSvelteConfig = Boolean(firstExistingFile(cwd, [ + 'svelte.config.js', + 'svelte.config.mjs', + 'svelte.config.cjs', + 'svelte.config.ts', + ])); + const hasKitPackage = hasAnyDependency(cwd, [ + '@sveltejs/kit', + '@sveltejs/vite-plugin-svelte', + 'svelte', + ]); if (!hasSvelteConfig && !hasKitPackage) return null; return { @@ -260,36 +268,16 @@ function findSvelteKitAppHtml(cwd, config) { } function findSvelteKitLayout(cwd) { - const candidates = [ + return firstExistingFile(cwd, [ 'src/routes/+layout.svelte', 'src/routes/(app)/+layout.svelte', - ]; - for (const rel of candidates) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return 'src/routes/+layout.svelte'; + ]) || 'src/routes/+layout.svelte'; } function defaultSvelteLayout() { return `\n\n{@render children?.()}\n`; } -function packageHasSvelteKit(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return Boolean(deps['@sveltejs/kit'] || deps['@sveltejs/vite-plugin-svelte'] || deps.svelte); - } catch { - return false; - } -} - function fileIncludes(file, text) { try { return fs.readFileSync(file, 'utf-8').includes(text); diff --git a/plugin/skills/impeccable/scripts/live/tanstack-adapter.mjs b/plugin/skills/impeccable/scripts/live/tanstack-adapter.mjs index 4a1c81a97..b53978f4f 100644 --- a/plugin/skills/impeccable/scripts/live/tanstack-adapter.mjs +++ b/plugin/skills/impeccable/scripts/live/tanstack-adapter.mjs @@ -19,6 +19,8 @@ import fs from 'node:fs'; import path from 'node:path'; + +import { firstExistingFile, hasAnyDependency } from './frameworks/detect-utils.mjs'; import { buildLiveScriptSrc } from './frameworks/script-src.mjs'; export const TANSTACK_MARKER_OPEN = '{/* impeccable-live-tanstack-start */}'; @@ -42,8 +44,8 @@ const START_PACKAGES = [ ]; export function detectTanStackStartProject(cwd = process.cwd()) { - if (!packageHasTanStackStart(cwd)) return null; - const rootRoute = findRootRouteFile(cwd); + if (!hasAnyDependency(cwd, START_PACKAGES)) return null; + const rootRoute = firstExistingFile(cwd, ROOT_ROUTE_CANDIDATES); if (!rootRoute) return null; const ext = path.extname(rootRoute); @@ -218,29 +220,6 @@ function isManagedComponent(content) { return String(content || '').includes('impeccable-live-tanstack'); } -function findRootRouteFile(cwd) { - for (const rel of ROOT_ROUTE_CANDIDATES) { - if (fs.existsSync(path.join(cwd, rel))) return rel; - } - return null; -} - -function packageHasTanStackStart(cwd) { - const file = path.join(cwd, 'package.json'); - if (!fs.existsSync(file)) return false; - try { - const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); - const deps = { - ...(pkg.dependencies || {}), - ...(pkg.devDependencies || {}), - ...(pkg.peerDependencies || {}), - }; - return START_PACKAGES.some((name) => Boolean(deps[name])); - } catch { - return false; - } -} - function relativeImportSpecifier(fromFile, toFile) { const rel = path.posix.relative( path.posix.dirname(fromFile.split(path.sep).join('/')),