quiver-cli 0.7.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -45
- package/bin/quiver-cli.mjs +3 -1
- package/dist/cli.js +733 -499
- package/package.json +2 -2
- package/template/.agents/AGENTS.md +4 -3
- package/template/.agents/config.json +7 -0
- package/template/.agents/plugins/opencode/rtk.ts +34 -0
- package/template/.agents/skills/agent-browser/SKILL.md +1 -0
- package/template/.agents/skills/apps/skybridge/SKILL.md +4 -0
- package/template/.agents/skills/design/impeccable/SKILL.md +36 -118
- package/template/.agents/skills/design/impeccable/reference/adapt.md +1 -0
- package/template/.agents/skills/design/impeccable/reference/adapt.native.md +58 -0
- package/template/.agents/skills/design/impeccable/reference/android.md +40 -0
- package/template/.agents/skills/design/impeccable/reference/animate.md +73 -188
- package/template/.agents/skills/design/impeccable/reference/audit.md +12 -9
- package/template/.agents/skills/design/impeccable/reference/audit.native.md +139 -0
- package/template/.agents/skills/design/impeccable/reference/bolder.md +19 -101
- package/template/.agents/skills/design/impeccable/reference/clarify.md +59 -253
- package/template/.agents/skills/design/impeccable/reference/colorize.md +51 -222
- package/template/.agents/skills/design/impeccable/reference/craft-floor.md +42 -0
- package/template/.agents/skills/design/impeccable/reference/craft.md +3 -121
- package/template/.agents/skills/design/impeccable/reference/critique.md +44 -23
- package/template/.agents/skills/design/impeccable/reference/degraded/asset-producer.md +90 -0
- package/template/.agents/skills/design/impeccable/reference/degraded/documenter.md +24 -0
- package/template/.agents/skills/design/impeccable/reference/degraded/finish-reviewer.md +37 -0
- package/template/.agents/skills/design/impeccable/reference/degraded/manual-edit-applier.md +92 -0
- package/template/.agents/skills/design/impeccable/reference/delight.md +47 -279
- package/template/.agents/skills/design/impeccable/reference/distill.md +2 -2
- package/template/.agents/skills/design/impeccable/reference/doctor.md +53 -0
- package/template/.agents/skills/design/impeccable/reference/document.md +60 -73
- package/template/.agents/skills/design/impeccable/reference/harden.md +1 -12
- package/template/.agents/skills/design/impeccable/reference/hooks.md +20 -5
- package/template/.agents/skills/design/impeccable/reference/init.md +72 -119
- package/template/.agents/skills/design/impeccable/reference/ios.md +45 -0
- package/template/.agents/skills/design/impeccable/reference/layout.md +54 -131
- package/template/.agents/skills/design/impeccable/reference/live-setup.md +102 -0
- package/template/.agents/skills/design/impeccable/reference/live.md +116 -511
- package/template/.agents/skills/design/impeccable/reference/new-work.md +105 -0
- package/template/.agents/skills/design/impeccable/reference/{product.md → operate.md} +6 -5
- package/template/.agents/skills/design/impeccable/reference/optimize.md +4 -4
- package/template/.agents/skills/design/impeccable/reference/overdrive.md +1 -4
- package/template/.agents/skills/design/impeccable/reference/polish.md +68 -212
- package/template/.agents/skills/design/impeccable/reference/quieter.md +3 -3
- package/template/.agents/skills/design/impeccable/reference/routing.md +18 -0
- package/template/.agents/skills/design/impeccable/reference/shape.md +38 -144
- package/template/.agents/skills/design/impeccable/reference/typeset.md +51 -250
- package/template/.agents/skills/design/impeccable/reference/visualize.md +47 -0
- package/template/.agents/skills/design/impeccable/scripts/command-metadata.json +1 -1
- package/template/.agents/skills/design/impeccable/scripts/concept-seed.mjs +558 -0
- package/template/.agents/skills/design/impeccable/scripts/context-signals.mjs +119 -10
- package/template/.agents/skills/design/impeccable/scripts/context.mjs +534 -45
- package/template/.agents/skills/design/impeccable/scripts/critique-storage.mjs +18 -47
- package/template/.agents/skills/design/impeccable/scripts/detector/browser/injected/index.mjs +96 -10
- package/template/.agents/skills/design/impeccable/scripts/detector/cli/main.mjs +174 -26
- package/template/.agents/skills/design/impeccable/scripts/detector/design-system.mjs +233 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns-browser.js +3348 -203
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/browser/detect-url.mjs +102 -7
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/regex/detect-text.mjs +297 -97
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +187 -16
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/detect-html.mjs +41 -11
- package/template/.agents/skills/design/impeccable/scripts/detector/findings.mjs +7 -1
- package/template/.agents/skills/design/impeccable/scripts/detector/node/file-system.mjs +16 -2
- package/template/.agents/skills/design/impeccable/scripts/detector/registry/antipatterns.mjs +207 -38
- package/template/.agents/skills/design/impeccable/scripts/detector/rules/checks.mjs +3082 -173
- package/template/.agents/skills/design/impeccable/scripts/detector/shared/constants.mjs +11 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/shared/fonts.mjs +30 -0
- package/template/.agents/skills/design/impeccable/scripts/doctor.mjs +336 -0
- package/template/.agents/skills/design/impeccable/scripts/embed-prompt.mjs +133 -0
- package/template/.agents/skills/design/impeccable/scripts/generate-image.mjs +240 -0
- package/template/.agents/skills/design/impeccable/scripts/hook-admin.mjs +98 -18
- package/template/.agents/skills/design/impeccable/scripts/hook-before-edit.mjs +46 -6
- package/template/.agents/skills/design/impeccable/scripts/hook-lib.mjs +518 -50
- package/template/.agents/skills/design/impeccable/scripts/hook.mjs +25 -8
- package/template/.agents/skills/design/impeccable/scripts/lib/artifact-schema.mjs +93 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/composition-catalog.mjs +200 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/concept-catalog.mjs +357 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/impeccable-config.mjs +27 -7
- package/template/.agents/skills/design/impeccable/scripts/lib/impeccable-paths.mjs +17 -8
- package/template/.agents/skills/design/impeccable/scripts/lib/provider.mjs +5 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/roll-selection.mjs +362 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/staleness-deep.mjs +457 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/staleness-notice.mjs +169 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/staleness.mjs +457 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/surface-briefs.mjs +151 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/target-slug.mjs +33 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/template-extensions.mjs +146 -0
- package/template/.agents/skills/design/impeccable/scripts/live/accept-css.mjs +617 -0
- package/template/.agents/skills/design/impeccable/scripts/live/accept-verify.mjs +60 -0
- package/template/.agents/skills/design/impeccable/scripts/live/browser-script-parts.mjs +7 -1
- package/template/.agents/skills/design/impeccable/scripts/live/completion.mjs +10 -1
- package/template/.agents/skills/design/impeccable/scripts/live/event-validation.mjs +67 -5
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/astro.mjs +47 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/detect-utils.mjs +73 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/index.mjs +143 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/journal.mjs +197 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/nextjs.mjs +49 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/nuxt.mjs +161 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/script-src.mjs +17 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/static-html.mjs +26 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/sveltekit.mjs +71 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/tag-strategy.mjs +247 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/tanstack-start.mjs +70 -0
- package/template/.agents/skills/design/impeccable/scripts/live/frameworks/vite-generic.mjs +42 -0
- package/template/.agents/skills/design/impeccable/scripts/live/generation-preflight.mjs +149 -0
- package/template/.agents/skills/design/impeccable/scripts/live/instructions.mjs +142 -0
- package/template/.agents/skills/design/impeccable/scripts/live/poll-lanes.mjs +14 -0
- package/template/.agents/skills/design/impeccable/scripts/live/roots.mjs +508 -0
- package/template/.agents/skills/design/impeccable/scripts/live/session-store.mjs +324 -50
- package/template/.agents/skills/design/impeccable/scripts/live/source-lock.mjs +105 -0
- package/template/.agents/skills/design/impeccable/scripts/live/source-search.mjs +105 -0
- package/template/.agents/skills/design/impeccable/scripts/live/svelte-ast.mjs +961 -0
- package/template/.agents/skills/design/impeccable/scripts/live/svelte-component.mjs +588 -72
- package/template/.agents/skills/design/impeccable/scripts/live/sveltekit-adapter.mjs +59 -17
- package/template/.agents/skills/design/impeccable/scripts/live/tanstack-adapter.mjs +280 -0
- package/template/.agents/skills/design/impeccable/scripts/live/vocabulary.mjs +135 -0
- package/template/.agents/skills/design/impeccable/scripts/live-accept.mjs +210 -68
- package/template/.agents/skills/design/impeccable/scripts/live-browser.js +1562 -223
- package/template/.agents/skills/design/impeccable/scripts/live-commit-manual-edits.mjs +3 -0
- package/template/.agents/skills/design/impeccable/scripts/live-complete.mjs +33 -1
- package/template/.agents/skills/design/impeccable/scripts/live-inject.mjs +175 -255
- package/template/.agents/skills/design/impeccable/scripts/live-insert.mjs +26 -6
- package/template/.agents/skills/design/impeccable/scripts/live-manual-edit-evidence.mjs +6 -1
- package/template/.agents/skills/design/impeccable/scripts/live-poll.mjs +61 -16
- package/template/.agents/skills/design/impeccable/scripts/live-resume.mjs +39 -10
- package/template/.agents/skills/design/impeccable/scripts/live-server.mjs +573 -47
- package/template/.agents/skills/design/impeccable/scripts/live-status.mjs +17 -7
- package/template/.agents/skills/design/impeccable/scripts/live-wrap.mjs +124 -91
- package/template/.agents/skills/design/impeccable/scripts/live.mjs +88 -26
- package/template/.agents/skills/design/impeccable/scripts/palette.mjs +76 -81
- package/template/.agents/skills/design/impeccable/scripts/pin.mjs +18 -11
- package/template/.agents/skills/design/impeccable/scripts/serve-question.mjs +932 -0
- package/template/.agents/skills/design/impeccable/scripts/surface-brief.mjs +74 -0
- package/template/.agents/skills/design/shadcn/SKILL.md +46 -11
- package/template/.agents/skills/design/shadcn/cli.md +49 -16
- package/template/.agents/skills/design/shadcn/customization.md +14 -7
- package/template/.agents/skills/design/shadcn/evals/evals.json +30 -0
- package/template/.agents/skills/design/shadcn/mcp.md +27 -16
- package/template/.agents/skills/design/shadcn/registry.md +277 -0
- package/template/.agents/skills/design/shadcn/rules/chat.md +224 -0
- package/template/.agents/skills/design/shadcn/rules/composition.md +20 -2
- package/template/.agents/skills/design/shadcn/rules/styling.md +23 -0
- package/template/.agents/skills/find-skills/SKILL.md +2 -3
- package/template/.agents/skills/hono/SKILL.md +579 -0
- package/template/.agents/skills/integrations/langfuse/SKILL.md +10 -6
- package/template/.agents/skills/integrations/langfuse/references/ci-cd.md +41 -0
- package/template/.agents/skills/integrations/langfuse/references/cli.md +8 -0
- package/template/.agents/skills/integrations/langfuse/references/error-analysis.md +15 -27
- package/template/.agents/skills/integrations/langfuse/references/instrumentation.md +39 -52
- package/template/.agents/skills/integrations/langfuse/references/judge-calibration.md +5 -2
- package/template/.agents/skills/integrations/langfuse/references/prompt-engineering.md +35 -0
- package/template/.agents/skills/integrations/langfuse/references/prompt-migration.md +41 -196
- package/template/.agents/skills/integrations/langfuse/references/skill-feedback.md +3 -0
- package/template/.agents/skills/integrations/langfuse/references/trace-evaluator-upgrade.md +76 -0
- package/template/.agents/skills/integrations/langfuse/references/user-feedback.md +4 -0
- package/template/.agents/skills/integrations/langfuse/references/v4-project-migration.md +73 -0
- package/template/.agents/skills/supabase/CHANGELOG.md +71 -0
- package/template/.agents/skills/supabase/SKILL.md +145 -0
- package/template/.agents/skills/supabase/assets/feedback-issue-template.md +17 -0
- package/template/.agents/skills/supabase/references/skill-feedback.md +17 -0
- package/template/.agents/skills/supabase-postgres-best-practices/CHANGELOG.md +73 -0
- package/template/.agents/skills/supabase-postgres-best-practices/SKILL.md +64 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/_contributing.md +170 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/_sections.md +39 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/_template.md +34 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +55 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +49 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +46 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/conn-limits.md +44 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/conn-pooling.md +41 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +46 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/data-batch-inserts.md +54 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/data-n-plus-one.md +53 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/data-pagination.md +50 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/data-upsert.md +50 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/lock-advisory.md +56 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +68 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/lock-short-transactions.md +50 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/lock-skip-locked.md +54 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +45 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +55 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +55 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/query-composite-indexes.md +44 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/query-covering-indexes.md +40 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/query-index-types.md +48 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/query-missing-indexes.md +43 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/query-partial-indexes.md +45 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/schema-constraints.md +80 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/schema-data-types.md +46 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +59 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +55 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/schema-partitioning.md +55 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/schema-primary-keys.md +61 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/security-privileges.md +54 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/security-rls-basics.md +50 -0
- package/template/.agents/skills/supabase-postgres-best-practices/references/security-rls-performance.md +63 -0
- package/template/.agents/upstreams.json +34 -12
- package/template/.agents/skills/design/impeccable/reference/brand.md +0 -108
- package/template/.agents/skills/design/impeccable/reference/codex.md +0 -105
- package/template/.agents/skills/design/impeccable/reference/interaction-design.md +0 -189
- package/template/.agents/skills/integrations/langfuse/references/sdk-upgrade.md +0 -175
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
import { createLiveSessionStore } from './live/session-store.mjs';
|
|
7
7
|
import { readLiveServerInfo } from './lib/impeccable-paths.mjs';
|
|
8
|
-
import { manualApplyResumeHint } from './live-resume.mjs';
|
|
8
|
+
import { manualApplyResumeHint, mountFailureAction, renderSummary } from './live-resume.mjs';
|
|
9
|
+
import { enterLiveRoot } from './live/roots.mjs';
|
|
9
10
|
|
|
10
11
|
function readServerInfo() {
|
|
11
12
|
return readLiveServerInfo(process.cwd())?.info || null;
|
|
@@ -28,6 +29,8 @@ export async function statusCli() {
|
|
|
28
29
|
const store = createLiveSessionStore({ cwd: process.cwd() });
|
|
29
30
|
const activeSessions = store.listActiveSessions();
|
|
30
31
|
const manualApply = findPendingManualApply(server, activeSessions);
|
|
32
|
+
const sessions = server?.activeSessions || activeSessions;
|
|
33
|
+
const renderFailure = sessions.find((session) => session?.renderState === 'failed') || null;
|
|
31
34
|
const payload = {
|
|
32
35
|
liveServer: server ? {
|
|
33
36
|
status: server.status,
|
|
@@ -36,16 +39,22 @@ export async function statusCli() {
|
|
|
36
39
|
agentPolling: server.agentPolling,
|
|
37
40
|
pendingEvents: server.pendingEvents,
|
|
38
41
|
} : null,
|
|
39
|
-
activeSessions:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
: server
|
|
43
|
-
? 'Run live-poll.mjs to continue pending work, or live-complete.mjs --id <session> after manual cleanup.'
|
|
44
|
-
: 'Start live-server.mjs to requeue pending durable events, then run live-poll.mjs.',
|
|
42
|
+
activeSessions: sessions,
|
|
43
|
+
render: sessions.map((session) => ({ id: session?.id ?? null, ...renderSummary(session) })),
|
|
44
|
+
recoveryHint: recoveryHint({ server, manualApply, renderFailure }),
|
|
45
45
|
};
|
|
46
46
|
console.log(JSON.stringify(payload, null, 2));
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
function recoveryHint({ server, manualApply, renderFailure }) {
|
|
50
|
+
if (manualApply) return manualApplyResumeHint(manualApply);
|
|
51
|
+
if (renderFailure) return mountFailureAction(renderFailure);
|
|
52
|
+
if (server) {
|
|
53
|
+
return 'Run live-poll.mjs to continue pending work, or live-complete.mjs --id <session> after manual cleanup.';
|
|
54
|
+
}
|
|
55
|
+
return 'Start live-server.mjs to requeue pending durable events, then run live-poll.mjs.';
|
|
56
|
+
}
|
|
57
|
+
|
|
49
58
|
function findPendingManualApply(server, activeSessions) {
|
|
50
59
|
const fromServer = server?.pendingEvents?.find((event) => event?.type === 'manual_edit_apply');
|
|
51
60
|
if (fromServer) return fromServer;
|
|
@@ -57,5 +66,6 @@ function findPendingManualApply(server, activeSessions) {
|
|
|
57
66
|
|
|
58
67
|
const _running = process.argv[1];
|
|
59
68
|
if (_running?.endsWith('live-status.mjs') || _running?.endsWith('live-status.mjs/')) {
|
|
69
|
+
enterLiveRoot();
|
|
60
70
|
statusCli();
|
|
61
71
|
}
|
|
@@ -14,14 +14,16 @@
|
|
|
14
14
|
import fs from 'node:fs';
|
|
15
15
|
import path from 'node:path';
|
|
16
16
|
import { isGeneratedFile } from './lib/is-generated.mjs';
|
|
17
|
+
import { resolveLiveTemplateExtensions } from './lib/template-extensions.mjs';
|
|
17
18
|
import { readBuffer as readManualEditsBuffer } from './live/manual-edits-buffer.mjs';
|
|
19
|
+
import { findSourceFile } from './live/source-search.mjs';
|
|
20
|
+
import { resolveSourceTraits } from './live/frameworks/index.mjs';
|
|
18
21
|
import {
|
|
19
22
|
buildSvelteComponentCssAuthoring,
|
|
20
23
|
scaffoldSvelteComponentSession,
|
|
21
24
|
shouldUseSvelteComponentInjection,
|
|
22
25
|
} from './live/svelte-component.mjs';
|
|
23
|
-
|
|
24
|
-
const EXTENSIONS = ['.html', '.jsx', '.tsx', '.vue', '.svelte', '.astro'];
|
|
26
|
+
import { enterLiveRoot } from './live/roots.mjs';
|
|
25
27
|
|
|
26
28
|
export async function wrapCli() {
|
|
27
29
|
const args = process.argv.slice(2);
|
|
@@ -68,6 +70,13 @@ The agent should insert variant HTML at insertLine.`);
|
|
|
68
70
|
const filePath = argVal(args, '--file');
|
|
69
71
|
const text = argVal(args, '--text');
|
|
70
72
|
const pageUrl = argVal(args, '--page-url');
|
|
73
|
+
// Preflight passes this for source-preview targets. It computes the scaffold
|
|
74
|
+
// (element location + wrapper text) but does NOT write it into source. The
|
|
75
|
+
// agent then writes the wrapper + all variants in one atomic edit. The
|
|
76
|
+
// premature server-side write full-reloaded the framework mid-generate and
|
|
77
|
+
// stranded the browser at 0/N (live-server.mjs missed-completion note). It is
|
|
78
|
+
// a no-op on the svelte-component path, which never writes the route source.
|
|
79
|
+
const deferSourceWrite = args.includes('--defer-source-write');
|
|
71
80
|
|
|
72
81
|
if (!id) { console.error('Missing --id'); process.exit(1); }
|
|
73
82
|
if (!elementId && !classes && !query) {
|
|
@@ -160,11 +169,29 @@ The agent should insert variant HTML at insertLine.`);
|
|
|
160
169
|
if (filtered.length === 1) {
|
|
161
170
|
match = filtered[0];
|
|
162
171
|
} else if (filtered.length === 0) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
172
|
+
const normalizedText = String(text).replace(/\s+/g, ' ').trim();
|
|
173
|
+
if (normalizedText.length < 8) {
|
|
174
|
+
// Very short labels cannot disambiguate siblings reliably. Preserve
|
|
175
|
+
// the legacy behavior for these low-information picker events.
|
|
176
|
+
match = candidates[0];
|
|
177
|
+
} else {
|
|
178
|
+
// Rendered text that is absent from every candidate usually means
|
|
179
|
+
// the source uses expressions or component props. Picking the first
|
|
180
|
+
// same-class sibling silently edits the wrong instance (observed on
|
|
181
|
+
// Astro result cards), so stop and surface every candidate instead.
|
|
182
|
+
console.error(JSON.stringify({
|
|
183
|
+
error: 'element_ambiguous',
|
|
184
|
+
fallback: 'agent-driven',
|
|
185
|
+
reason: 'rendered_text_not_in_source',
|
|
186
|
+
file: path.relative(process.cwd(), targetFile),
|
|
187
|
+
candidates: candidates.map((c) => ({
|
|
188
|
+
startLine: c.startLine + 1,
|
|
189
|
+
endLine: c.endLine + 1,
|
|
190
|
+
})),
|
|
191
|
+
hint: 'Rendered text does not occur in any matching source branch. The element may use dynamic props or expressions; inspect the candidates and wrap the intended instance manually.',
|
|
192
|
+
}));
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
168
195
|
} else {
|
|
169
196
|
// Multiple candidates ALSO match the text. Truly ambiguous — refuse
|
|
170
197
|
// rather than pick wrong, and hand the agent the candidate locations
|
|
@@ -268,7 +295,10 @@ The agent should insert variant HTML at insertLine.`);
|
|
|
268
295
|
.join('\n');
|
|
269
296
|
const originalIndented = reindentOriginal(' ');
|
|
270
297
|
const relTargetFile = path.relative(process.cwd(), targetFile).split(path.sep).join('/');
|
|
271
|
-
|
|
298
|
+
// The registry says which files get component preview; the svelte-component
|
|
299
|
+
// module keeps the env escape hatch that turns it off.
|
|
300
|
+
const useSvelteComponent = resolveSourceTraits(targetFile).preview === 'component'
|
|
301
|
+
&& shouldUseSvelteComponentInjection(targetFile);
|
|
272
302
|
|
|
273
303
|
// Wrapper attributes differ by syntax. HTML allows plain string attrs;
|
|
274
304
|
// JSX requires object-literal style and parses string attrs as HTML (which
|
|
@@ -288,7 +318,7 @@ The agent should insert variant HTML at insertLine.`);
|
|
|
288
318
|
// replacement range to include the wrapper's `<div>` open / close lines
|
|
289
319
|
// so the entire scaffold gets removed cleanly.
|
|
290
320
|
const wrapperLines = isJsx ? [
|
|
291
|
-
indent + '<div data-impeccable-variants="' + id + '" data-impeccable-variant-count="' + count + '" ' + styleContents + '>',
|
|
321
|
+
indent + '<div data-impeccable-variants="' + id + '" data-impeccable-variant-count="' + count + '"' + ' ' + styleContents + '>',
|
|
292
322
|
indent + ' ' + commentSyntax.open + ' impeccable-variants-start ' + id + ' ' + commentSyntax.close,
|
|
293
323
|
indent + ' ' + commentSyntax.open + ' Original ' + commentSyntax.close,
|
|
294
324
|
indent + ' <div data-impeccable-variant="original">',
|
|
@@ -299,7 +329,7 @@ The agent should insert variant HTML at insertLine.`);
|
|
|
299
329
|
indent + '</div>',
|
|
300
330
|
] : [
|
|
301
331
|
indent + commentSyntax.open + ' impeccable-variants-start ' + id + ' ' + commentSyntax.close,
|
|
302
|
-
indent + '<div data-impeccable-variants="' + id + '" data-impeccable-variant-count="' + count + '" ' + styleContents + '>',
|
|
332
|
+
indent + '<div data-impeccable-variants="' + id + '" data-impeccable-variant-count="' + count + '"' + ' ' + styleContents + '>',
|
|
303
333
|
indent + ' ' + commentSyntax.open + ' Original ' + commentSyntax.close,
|
|
304
334
|
indent + ' <div data-impeccable-variant="original">',
|
|
305
335
|
originalIndented,
|
|
@@ -315,13 +345,20 @@ The agent should insert variant HTML at insertLine.`);
|
|
|
315
345
|
let outputEndLine = startLine + wrapperLines.length + (originalLines.length - 1);
|
|
316
346
|
let insertLine;
|
|
317
347
|
let svelteSession = null;
|
|
348
|
+
let deferredWrapper = null;
|
|
318
349
|
|
|
350
|
+
let sveltePreviewFallback = null;
|
|
319
351
|
if (useSvelteComponent) {
|
|
320
352
|
// Svelte/SvelteKit resets component-local state on markup HMR updates.
|
|
321
353
|
// Keep generation source-neutral: agents write real variant components
|
|
322
354
|
// under the generated componentDir, the browser mounts them into the live
|
|
323
355
|
// DOM, and live-accept.mjs inlines the accepted variant back into the route.
|
|
324
|
-
|
|
356
|
+
//
|
|
357
|
+
// The scaffold is AST-based and refuses markup a detached preview cannot
|
|
358
|
+
// support (component tags, bind:/use:, await blocks, bound nested each).
|
|
359
|
+
// Refusal falls back to the plain source-preview wrapper below: an
|
|
360
|
+
// HMR-resetting but CORRECT preview beats a detached wrong one.
|
|
361
|
+
const scaffolded = scaffoldSvelteComponentSession({
|
|
325
362
|
id,
|
|
326
363
|
count,
|
|
327
364
|
sourceFile: relTargetFile,
|
|
@@ -330,10 +367,32 @@ The agent should insert variant HTML at insertLine.`);
|
|
|
330
367
|
originalLines,
|
|
331
368
|
cwd: process.cwd(),
|
|
332
369
|
});
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
370
|
+
if (scaffolded && scaffolded.fallback === 'source-preview') {
|
|
371
|
+
sveltePreviewFallback = scaffolded.reason || 'unsupported markup';
|
|
372
|
+
} else {
|
|
373
|
+
svelteSession = scaffolded;
|
|
374
|
+
outputFile = path.resolve(process.cwd(), svelteSession.manifestFile);
|
|
375
|
+
outputStartLine = 1;
|
|
376
|
+
outputEndLine = 1;
|
|
377
|
+
insertLine = 1;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (svelteSession) {
|
|
381
|
+
// component preview: outputs already set above
|
|
382
|
+
} else if (deferSourceWrite) {
|
|
383
|
+
// Deferred source write: compute the scaffold text but leave source
|
|
384
|
+
// untouched. The agent replaces the picked element's source range with
|
|
385
|
+
// `wrapperBlock` (variants spliced at the marker) in one edit. Writing the
|
|
386
|
+
// scaffold here first would reload the framework before the agent's write
|
|
387
|
+
// lands, and a browser caught mid-reload misses the `done` and sits at 0/N.
|
|
388
|
+
deferredWrapper = {
|
|
389
|
+
block: wrapperLines.join('\n'),
|
|
390
|
+
replaceStartLine: startLine + 1, // 1-indexed picked-element range the
|
|
391
|
+
replaceEndLine: endLine + 1, // agent's wrapper block replaces
|
|
392
|
+
};
|
|
393
|
+
// insertLine matches the final file position the wrapper occupies once the
|
|
394
|
+
// agent replaces the picked range, so downstream consumers stay consistent.
|
|
395
|
+
insertLine = startLine + 6 + (originalLines.length - 1) + 1;
|
|
337
396
|
} else {
|
|
338
397
|
// Replace the original element with the wrapper
|
|
339
398
|
const newLines = [
|
|
@@ -355,16 +414,31 @@ The agent should insert variant HTML at insertLine.`);
|
|
|
355
414
|
|
|
356
415
|
const outputRelFile = path.relative(process.cwd(), outputFile).split(path.sep).join('/');
|
|
357
416
|
|
|
358
|
-
const
|
|
417
|
+
const componentPreviewActive = !!svelteSession;
|
|
418
|
+
const svelteComponentAuthoring = componentPreviewActive ? buildSvelteComponentCssAuthoring(count) : null;
|
|
419
|
+
const componentSession = svelteSession;
|
|
420
|
+
const componentPreviewMode = componentPreviewActive ? 'svelte-component' : undefined;
|
|
421
|
+
const previewMode = componentPreviewMode;
|
|
359
422
|
|
|
360
423
|
console.log(JSON.stringify({
|
|
361
424
|
file: outputRelFile,
|
|
362
|
-
sourceFile:
|
|
363
|
-
previewMode
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
425
|
+
sourceFile: componentPreviewActive ? relTargetFile : undefined,
|
|
426
|
+
previewMode,
|
|
427
|
+
previewFallback: sveltePreviewFallback
|
|
428
|
+
? { from: 'svelte-component', reason: sveltePreviewFallback }
|
|
429
|
+
: undefined,
|
|
430
|
+
// Deferred source write: the wrapper is NOT yet in source. The agent
|
|
431
|
+
// replaces [replaceStartLine, replaceEndLine] with `wrapperBlock` (variants
|
|
432
|
+
// spliced at the "insert below this line" marker) in one atomic edit.
|
|
433
|
+
sourceWritten: deferredWrapper ? false : undefined,
|
|
434
|
+
wrapperBlock: deferredWrapper ? deferredWrapper.block : undefined,
|
|
435
|
+
replaceStartLine: deferredWrapper ? deferredWrapper.replaceStartLine : undefined,
|
|
436
|
+
replaceEndLine: deferredWrapper ? deferredWrapper.replaceEndLine : undefined,
|
|
437
|
+
componentDir: componentSession?.componentDir,
|
|
438
|
+
propContract: componentSession?.propContract,
|
|
439
|
+
componentStubMarkup: componentSession?.stubMarkup,
|
|
440
|
+
sourceStartLine: componentPreviewActive ? startLine + 1 : undefined,
|
|
441
|
+
sourceEndLine: componentPreviewActive ? endLine + 1 : undefined,
|
|
368
442
|
startLine: outputStartLine, // 1-indexed for the agent
|
|
369
443
|
// wrapperLines is an array but one element (the original-content slot)
|
|
370
444
|
// is a `\n`-joined multi-line string, so the actual file-row count is
|
|
@@ -374,10 +448,10 @@ The agent should insert variant HTML at insertLine.`);
|
|
|
374
448
|
endLine: outputEndLine, // 1-indexed
|
|
375
449
|
insertLine, // 1-indexed: where variants go
|
|
376
450
|
commentSyntax: commentSyntax,
|
|
377
|
-
styleMode:
|
|
378
|
-
styleTag:
|
|
379
|
-
cssSelectorPrefixExamples:
|
|
380
|
-
cssAuthoring:
|
|
451
|
+
styleMode: componentPreviewMode || styleMode.mode,
|
|
452
|
+
styleTag: componentPreviewActive ? null : styleMode.styleTag,
|
|
453
|
+
cssSelectorPrefixExamples: componentPreviewActive ? [] : buildCssSelectorPrefixExamples(styleMode.mode, count),
|
|
454
|
+
cssAuthoring: svelteComponentAuthoring || buildCssAuthoring(styleMode, count),
|
|
381
455
|
originalLineCount: originalLines.length,
|
|
382
456
|
}));
|
|
383
457
|
}
|
|
@@ -579,27 +653,22 @@ function attrEscapeDouble(str) {
|
|
|
579
653
|
.replace(/>/g, '>');
|
|
580
654
|
}
|
|
581
655
|
|
|
656
|
+
/**
|
|
657
|
+
* Comment syntax, style mode, and preview strategy all come from the framework
|
|
658
|
+
* registry, keyed on the target file's extension: `.jsx`/`.tsx` author JSX
|
|
659
|
+
* comments, `.astro` needs global-prefixed preview CSS because Astro scopes
|
|
660
|
+
* component styles away from the generated wrappers, `.svelte` gets component
|
|
661
|
+
* preview. See live/frameworks/index.mjs for why extension and not project.
|
|
662
|
+
*/
|
|
582
663
|
function detectCommentSyntax(filePath) {
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
}
|
|
587
|
-
// HTML, Vue, Svelte, Astro all use HTML comments
|
|
588
|
-
return { open: '<!--', close: '-->' };
|
|
664
|
+
return resolveSourceTraits(filePath).commentSyntax === 'jsx'
|
|
665
|
+
? { open: '{/*', close: '*/}' }
|
|
666
|
+
: { open: '<!--', close: '-->' };
|
|
589
667
|
}
|
|
590
668
|
|
|
591
669
|
function detectStyleMode(filePath) {
|
|
592
|
-
const
|
|
593
|
-
|
|
594
|
-
return {
|
|
595
|
-
mode: 'astro-global-prefixed',
|
|
596
|
-
styleTag: '<style is:inline data-impeccable-css="SESSION_ID">',
|
|
597
|
-
};
|
|
598
|
-
}
|
|
599
|
-
return {
|
|
600
|
-
mode: 'scoped',
|
|
601
|
-
styleTag: '<style data-impeccable-css="SESSION_ID">',
|
|
602
|
-
};
|
|
670
|
+
const traits = resolveSourceTraits(filePath);
|
|
671
|
+
return { mode: traits.styleMode, styleTag: traits.styleTag };
|
|
603
672
|
}
|
|
604
673
|
|
|
605
674
|
function buildCssSelectorPrefixExamples(styleMode, count) {
|
|
@@ -650,56 +719,19 @@ function buildCssAuthoring(styleMode, count) {
|
|
|
650
719
|
/**
|
|
651
720
|
* Search project files for the query string (class name, ID, etc.)
|
|
652
721
|
* Returns the first matching file path, or null.
|
|
722
|
+
*
|
|
723
|
+
* Only `node_modules`, `.git`, and `.impeccable` are skipped outright.
|
|
724
|
+
* dist/build/out are left to the isGeneratedFile guard so the
|
|
725
|
+
* `includeGenerated` second pass can still find the element there and report
|
|
726
|
+
* `generatedMatch`.
|
|
653
727
|
*/
|
|
654
728
|
function findFileWithQuery(query, cwd, genOpts = {}) {
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
const result = searchDir(absDir, query, seen, 0, genOpts);
|
|
662
|
-
if (result) return result;
|
|
663
|
-
}
|
|
664
|
-
return null;
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
function searchDir(dir, query, seen, depth, genOpts) {
|
|
668
|
-
if (depth > 5) return null; // don't go too deep
|
|
669
|
-
const realDir = fs.realpathSync(dir);
|
|
670
|
-
if (seen.has(realDir)) return null;
|
|
671
|
-
seen.add(realDir);
|
|
672
|
-
|
|
673
|
-
let entries;
|
|
674
|
-
try { entries = fs.readdirSync(dir, { withFileTypes: true }); }
|
|
675
|
-
catch { return null; }
|
|
676
|
-
|
|
677
|
-
// Check files first
|
|
678
|
-
for (const entry of entries) {
|
|
679
|
-
if (!entry.isFile()) continue;
|
|
680
|
-
const ext = path.extname(entry.name).toLowerCase();
|
|
681
|
-
if (!EXTENSIONS.includes(ext)) continue;
|
|
682
|
-
|
|
683
|
-
const filePath = path.join(dir, entry.name);
|
|
684
|
-
if (!genOpts.includeGenerated && isGeneratedFile(filePath, genOpts)) continue;
|
|
685
|
-
try {
|
|
686
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
687
|
-
if (content.includes(query)) return filePath;
|
|
688
|
-
} catch { /* skip unreadable files */ }
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
// Then recurse into directories. Always skip node_modules and .git (never
|
|
692
|
-
// project content). dist/build/out are left to the isGeneratedFile guard so
|
|
693
|
-
// the includeGenerated second-pass can still find the element there and
|
|
694
|
-
// report `generatedMatch`.
|
|
695
|
-
for (const entry of entries) {
|
|
696
|
-
if (!entry.isDirectory()) continue;
|
|
697
|
-
if (entry.name === 'node_modules' || entry.name === '.git') continue;
|
|
698
|
-
const result = searchDir(path.join(dir, entry.name), query, seen, depth + 1, genOpts);
|
|
699
|
-
if (result) return result;
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
return null;
|
|
729
|
+
return findSourceFile({
|
|
730
|
+
query,
|
|
731
|
+
cwd,
|
|
732
|
+
extensions: resolveLiveTemplateExtensions(cwd),
|
|
733
|
+
fileFilter: (filePath) => genOpts.includeGenerated || !isGeneratedFile(filePath, genOpts),
|
|
734
|
+
});
|
|
703
735
|
}
|
|
704
736
|
|
|
705
737
|
/**
|
|
@@ -876,6 +908,7 @@ function findClosingLine(lines, start) {
|
|
|
876
908
|
// Auto-execute when run directly (node live-wrap.mjs ...)
|
|
877
909
|
const _running = process.argv[1];
|
|
878
910
|
if (_running?.endsWith('live-wrap.mjs') || _running?.endsWith('live-wrap.mjs/')) {
|
|
911
|
+
enterLiveRoot();
|
|
879
912
|
wrapCli();
|
|
880
913
|
}
|
|
881
914
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* After this, the agent's only remaining steps are:
|
|
12
12
|
* - Open the project's live dev/preview URL in the browser (optional, if browser automation exists)—not `serverPort`; that port is the Impeccable helper for /live.js and /poll
|
|
13
|
-
* - Enter the poll loop: `node live-poll.mjs`
|
|
13
|
+
* - Enter the harness-native poll loop: `node live-poll.mjs`
|
|
14
14
|
*
|
|
15
15
|
* Usage:
|
|
16
16
|
* node live.mjs # Prepare everything, print JSON, exit
|
|
@@ -21,10 +21,13 @@ import { execSync } from 'node:child_process';
|
|
|
21
21
|
import fs from 'node:fs';
|
|
22
22
|
import path from 'node:path';
|
|
23
23
|
import { fileURLToPath } from 'node:url';
|
|
24
|
-
import {
|
|
24
|
+
import { resolveTargetSelection } from './context.mjs';
|
|
25
25
|
import { resolveFiles } from './live-inject.mjs';
|
|
26
26
|
import { readLiveServerInfo } from './lib/impeccable-paths.mjs';
|
|
27
|
+
import { resolveSurfaceBrief } from './lib/surface-briefs.mjs';
|
|
27
28
|
import { resolveLiveTarget } from './live-target.mjs';
|
|
29
|
+
import { bootInstructions } from './live/instructions.mjs';
|
|
30
|
+
import { resolveRoots, writeRootsManifest } from './live/roots.mjs';
|
|
28
31
|
|
|
29
32
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
30
33
|
|
|
@@ -40,6 +43,7 @@ Prepare everything for live variant mode in a single command:
|
|
|
40
43
|
- Starts (or reuses) the live server in the background
|
|
41
44
|
- Injects the browser script tag
|
|
42
45
|
- Reads PRODUCT.md / DESIGN.md for project context
|
|
46
|
+
- Prepares the harness-native foreground/background poll loop
|
|
43
47
|
- In monorepos, choose a child app first; --target <path> is the fallback/manual path
|
|
44
48
|
|
|
45
49
|
On success, prints a JSON blob with:
|
|
@@ -59,6 +63,8 @@ The agent should then:
|
|
|
59
63
|
process.exit(0);
|
|
60
64
|
}
|
|
61
65
|
|
|
66
|
+
// Legacy workspace-monorepo selection first: it carries richer candidate
|
|
67
|
+
// metadata (context inheritance status) than the roots scan.
|
|
62
68
|
const targetSelection = resolveTargetSelection(liveTarget.originalCwd, liveTarget.targetOptions);
|
|
63
69
|
if (targetSelection) {
|
|
64
70
|
console.log(JSON.stringify({
|
|
@@ -70,11 +76,31 @@ The agent should then:
|
|
|
70
76
|
process.exit(0);
|
|
71
77
|
}
|
|
72
78
|
|
|
73
|
-
const
|
|
74
|
-
|
|
79
|
+
const rootsResult = resolveRoots({
|
|
80
|
+
cwd: liveTarget.originalCwd,
|
|
81
|
+
targetPath: liveTarget.absoluteTargetPath,
|
|
82
|
+
});
|
|
83
|
+
if (rootsResult.selection) {
|
|
84
|
+
console.log(JSON.stringify({
|
|
85
|
+
ok: false,
|
|
86
|
+
error: 'target_selection_required',
|
|
87
|
+
targetCandidates: rootsResult.selection.candidates,
|
|
88
|
+
hint: 'Several apps with a dev-server config exist. Ask the user which one to use, then rerun with --target <path into that app>.',
|
|
89
|
+
}, null, 2));
|
|
90
|
+
process.exit(0);
|
|
91
|
+
}
|
|
92
|
+
const roots = rootsResult.manifest;
|
|
93
|
+
const activeCwd = roots.appRoot;
|
|
75
94
|
const outputTargetPath = liveTarget.targetPath || null;
|
|
76
95
|
|
|
77
|
-
|
|
96
|
+
// Gate on readable CONTENT, not path existence, so an empty or unreadable
|
|
97
|
+
// PRODUCT.md routes to init instead of passing the gate and then reporting
|
|
98
|
+
// hasProduct: false in the same payload.
|
|
99
|
+
const product = safeRead(roots.productPath);
|
|
100
|
+
const design = safeRead(roots.designPath);
|
|
101
|
+
const missingContext = [];
|
|
102
|
+
if (!product) missingContext.push('PRODUCT.md');
|
|
103
|
+
if (!design) missingContext.push('DESIGN.md');
|
|
78
104
|
if (missingContext.length > 0) {
|
|
79
105
|
console.log(JSON.stringify({
|
|
80
106
|
ok: false,
|
|
@@ -82,14 +108,18 @@ The agent should then:
|
|
|
82
108
|
missing: missingContext,
|
|
83
109
|
nextCommand: missingContext.includes('PRODUCT.md') ? 'init' : 'document',
|
|
84
110
|
targetPath: outputTargetPath,
|
|
85
|
-
projectRoot:
|
|
86
|
-
repoRoot:
|
|
87
|
-
productPath:
|
|
88
|
-
designPath:
|
|
111
|
+
projectRoot: roots.appRoot,
|
|
112
|
+
repoRoot: roots.repoRoot,
|
|
113
|
+
productPath: relOrNull(liveTarget.originalCwd, roots.productPath),
|
|
114
|
+
designPath: relOrNull(liveTarget.originalCwd, roots.designPath),
|
|
89
115
|
}, null, 2));
|
|
90
116
|
process.exit(0);
|
|
91
117
|
}
|
|
92
118
|
|
|
119
|
+
// Persist the decision before anything else spawns, so every helper the
|
|
120
|
+
// agent runs later (from any cwd inside the repo) lands on the same roots.
|
|
121
|
+
writeRootsManifest(roots);
|
|
122
|
+
|
|
93
123
|
// 1. Check config (fail fast if missing — no point starting anything else)
|
|
94
124
|
const checkOut = runScript('live-inject.mjs', ['--check'], { cwd: activeCwd });
|
|
95
125
|
const checkResult = safeParse(checkOut);
|
|
@@ -97,8 +127,8 @@ The agent should then:
|
|
|
97
127
|
console.log(JSON.stringify({
|
|
98
128
|
...(checkResult || { ok: false, error: 'check_failed', raw: checkOut }),
|
|
99
129
|
targetPath: outputTargetPath,
|
|
100
|
-
projectRoot:
|
|
101
|
-
repoRoot:
|
|
130
|
+
projectRoot: roots.appRoot,
|
|
131
|
+
repoRoot: roots.repoRoot,
|
|
102
132
|
}));
|
|
103
133
|
process.exit(0);
|
|
104
134
|
}
|
|
@@ -111,7 +141,11 @@ The agent should then:
|
|
|
111
141
|
}
|
|
112
142
|
|
|
113
143
|
// 3. Inject the script tag at the current port
|
|
114
|
-
const injectOut = runScript(
|
|
144
|
+
const injectOut = runScript(
|
|
145
|
+
'live-inject.mjs',
|
|
146
|
+
['--port', String(serverInfo.port), '--token', String(serverInfo.token)],
|
|
147
|
+
{ cwd: activeCwd },
|
|
148
|
+
);
|
|
115
149
|
const injectResult = safeParse(injectOut);
|
|
116
150
|
if (!injectResult || !injectResult.ok) {
|
|
117
151
|
console.log(JSON.stringify({
|
|
@@ -129,7 +163,28 @@ The agent should then:
|
|
|
129
163
|
const resolvedFiles = resolveFiles(activeCwd, checkResult.config);
|
|
130
164
|
const drift = scanForDrift(activeCwd, resolvedFiles, checkResult.config);
|
|
131
165
|
|
|
132
|
-
// 5. Emit everything the agent needs
|
|
166
|
+
// 5. Emit everything the agent needs. The surface brief rides along so the
|
|
167
|
+
// agent does not spend three more tool calls (and a --help miss) on
|
|
168
|
+
// surface-brief.mjs before the first poll.
|
|
169
|
+
let surfaceBrief = null;
|
|
170
|
+
let surfaceBriefPath = null;
|
|
171
|
+
try {
|
|
172
|
+
// Briefs live under .impeccable/surfaces, which in a nested-app repo sits
|
|
173
|
+
// at the CONTEXT or repo root, not the app root; context.mjs already finds
|
|
174
|
+
// them there, and live must not report "no brief" for the same project.
|
|
175
|
+
const briefRoots = [roots.appRoot, roots.contextRoot, roots.repoRoot]
|
|
176
|
+
.filter(Boolean)
|
|
177
|
+
.filter((dir, i, arr) => arr.findIndex((other) => path.resolve(other) === path.resolve(dir)) === i);
|
|
178
|
+
for (const briefRoot of briefRoots) {
|
|
179
|
+
const resolvedBrief = resolveSurfaceBrief(briefRoot, liveTarget.absoluteTargetPath || null);
|
|
180
|
+
if (!resolvedBrief?.brief) continue;
|
|
181
|
+
surfaceBrief = resolvedBrief.brief.text ?? safeRead(resolvedBrief.brief.path);
|
|
182
|
+
surfaceBriefPath = resolvedBrief.brief.path
|
|
183
|
+
? path.relative(liveTarget.originalCwd, resolvedBrief.brief.path)
|
|
184
|
+
: null;
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
} catch { /* briefs are optional context */ }
|
|
133
188
|
console.log(JSON.stringify({
|
|
134
189
|
ok: true,
|
|
135
190
|
serverPort: serverInfo.port,
|
|
@@ -138,22 +193,29 @@ The agent should then:
|
|
|
138
193
|
liveConfigPath: checkResult.path,
|
|
139
194
|
configDrift: drift,
|
|
140
195
|
targetPath: outputTargetPath,
|
|
141
|
-
projectRoot:
|
|
142
|
-
repoRoot:
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
196
|
+
projectRoot: roots.appRoot,
|
|
197
|
+
repoRoot: roots.repoRoot,
|
|
198
|
+
roots,
|
|
199
|
+
hasProduct: !!product,
|
|
200
|
+
product,
|
|
201
|
+
productPath: relOrNull(liveTarget.originalCwd, roots.productPath),
|
|
202
|
+
hasDesign: !!design,
|
|
203
|
+
design,
|
|
204
|
+
designPath: relOrNull(liveTarget.originalCwd, roots.designPath),
|
|
205
|
+
hasSurfaceBrief: !!surfaceBrief,
|
|
206
|
+
surfaceBrief,
|
|
207
|
+
surfaceBriefPath,
|
|
208
|
+
_instructions: bootInstructions({ scriptsPath: __dirname }),
|
|
149
209
|
}, null, 2));
|
|
150
210
|
}
|
|
151
211
|
|
|
152
|
-
function
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
212
|
+
function safeRead(p) {
|
|
213
|
+
if (!p) return null;
|
|
214
|
+
try { return fs.readFileSync(p, 'utf-8'); } catch { return null; }
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function relOrNull(base, p) {
|
|
218
|
+
return p ? path.relative(base, p) : null;
|
|
157
219
|
}
|
|
158
220
|
|
|
159
221
|
/**
|