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.
Files changed (200) hide show
  1. package/README.md +110 -45
  2. package/bin/quiver-cli.mjs +3 -1
  3. package/dist/cli.js +733 -499
  4. package/package.json +2 -2
  5. package/template/.agents/AGENTS.md +4 -3
  6. package/template/.agents/config.json +7 -0
  7. package/template/.agents/plugins/opencode/rtk.ts +34 -0
  8. package/template/.agents/skills/agent-browser/SKILL.md +1 -0
  9. package/template/.agents/skills/apps/skybridge/SKILL.md +4 -0
  10. package/template/.agents/skills/design/impeccable/SKILL.md +36 -118
  11. package/template/.agents/skills/design/impeccable/reference/adapt.md +1 -0
  12. package/template/.agents/skills/design/impeccable/reference/adapt.native.md +58 -0
  13. package/template/.agents/skills/design/impeccable/reference/android.md +40 -0
  14. package/template/.agents/skills/design/impeccable/reference/animate.md +73 -188
  15. package/template/.agents/skills/design/impeccable/reference/audit.md +12 -9
  16. package/template/.agents/skills/design/impeccable/reference/audit.native.md +139 -0
  17. package/template/.agents/skills/design/impeccable/reference/bolder.md +19 -101
  18. package/template/.agents/skills/design/impeccable/reference/clarify.md +59 -253
  19. package/template/.agents/skills/design/impeccable/reference/colorize.md +51 -222
  20. package/template/.agents/skills/design/impeccable/reference/craft-floor.md +42 -0
  21. package/template/.agents/skills/design/impeccable/reference/craft.md +3 -121
  22. package/template/.agents/skills/design/impeccable/reference/critique.md +44 -23
  23. package/template/.agents/skills/design/impeccable/reference/degraded/asset-producer.md +90 -0
  24. package/template/.agents/skills/design/impeccable/reference/degraded/documenter.md +24 -0
  25. package/template/.agents/skills/design/impeccable/reference/degraded/finish-reviewer.md +37 -0
  26. package/template/.agents/skills/design/impeccable/reference/degraded/manual-edit-applier.md +92 -0
  27. package/template/.agents/skills/design/impeccable/reference/delight.md +47 -279
  28. package/template/.agents/skills/design/impeccable/reference/distill.md +2 -2
  29. package/template/.agents/skills/design/impeccable/reference/doctor.md +53 -0
  30. package/template/.agents/skills/design/impeccable/reference/document.md +60 -73
  31. package/template/.agents/skills/design/impeccable/reference/harden.md +1 -12
  32. package/template/.agents/skills/design/impeccable/reference/hooks.md +20 -5
  33. package/template/.agents/skills/design/impeccable/reference/init.md +72 -119
  34. package/template/.agents/skills/design/impeccable/reference/ios.md +45 -0
  35. package/template/.agents/skills/design/impeccable/reference/layout.md +54 -131
  36. package/template/.agents/skills/design/impeccable/reference/live-setup.md +102 -0
  37. package/template/.agents/skills/design/impeccable/reference/live.md +116 -511
  38. package/template/.agents/skills/design/impeccable/reference/new-work.md +105 -0
  39. package/template/.agents/skills/design/impeccable/reference/{product.md → operate.md} +6 -5
  40. package/template/.agents/skills/design/impeccable/reference/optimize.md +4 -4
  41. package/template/.agents/skills/design/impeccable/reference/overdrive.md +1 -4
  42. package/template/.agents/skills/design/impeccable/reference/polish.md +68 -212
  43. package/template/.agents/skills/design/impeccable/reference/quieter.md +3 -3
  44. package/template/.agents/skills/design/impeccable/reference/routing.md +18 -0
  45. package/template/.agents/skills/design/impeccable/reference/shape.md +38 -144
  46. package/template/.agents/skills/design/impeccable/reference/typeset.md +51 -250
  47. package/template/.agents/skills/design/impeccable/reference/visualize.md +47 -0
  48. package/template/.agents/skills/design/impeccable/scripts/command-metadata.json +1 -1
  49. package/template/.agents/skills/design/impeccable/scripts/concept-seed.mjs +558 -0
  50. package/template/.agents/skills/design/impeccable/scripts/context-signals.mjs +119 -10
  51. package/template/.agents/skills/design/impeccable/scripts/context.mjs +534 -45
  52. package/template/.agents/skills/design/impeccable/scripts/critique-storage.mjs +18 -47
  53. package/template/.agents/skills/design/impeccable/scripts/detector/browser/injected/index.mjs +96 -10
  54. package/template/.agents/skills/design/impeccable/scripts/detector/cli/main.mjs +174 -26
  55. package/template/.agents/skills/design/impeccable/scripts/detector/design-system.mjs +233 -0
  56. package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns-browser.js +3348 -203
  57. package/template/.agents/skills/design/impeccable/scripts/detector/engines/browser/detect-url.mjs +102 -7
  58. package/template/.agents/skills/design/impeccable/scripts/detector/engines/regex/detect-text.mjs +297 -97
  59. package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +187 -16
  60. package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/detect-html.mjs +41 -11
  61. package/template/.agents/skills/design/impeccable/scripts/detector/findings.mjs +7 -1
  62. package/template/.agents/skills/design/impeccable/scripts/detector/node/file-system.mjs +16 -2
  63. package/template/.agents/skills/design/impeccable/scripts/detector/registry/antipatterns.mjs +207 -38
  64. package/template/.agents/skills/design/impeccable/scripts/detector/rules/checks.mjs +3082 -173
  65. package/template/.agents/skills/design/impeccable/scripts/detector/shared/constants.mjs +11 -0
  66. package/template/.agents/skills/design/impeccable/scripts/detector/shared/fonts.mjs +30 -0
  67. package/template/.agents/skills/design/impeccable/scripts/doctor.mjs +336 -0
  68. package/template/.agents/skills/design/impeccable/scripts/embed-prompt.mjs +133 -0
  69. package/template/.agents/skills/design/impeccable/scripts/generate-image.mjs +240 -0
  70. package/template/.agents/skills/design/impeccable/scripts/hook-admin.mjs +98 -18
  71. package/template/.agents/skills/design/impeccable/scripts/hook-before-edit.mjs +46 -6
  72. package/template/.agents/skills/design/impeccable/scripts/hook-lib.mjs +518 -50
  73. package/template/.agents/skills/design/impeccable/scripts/hook.mjs +25 -8
  74. package/template/.agents/skills/design/impeccable/scripts/lib/artifact-schema.mjs +93 -0
  75. package/template/.agents/skills/design/impeccable/scripts/lib/composition-catalog.mjs +200 -0
  76. package/template/.agents/skills/design/impeccable/scripts/lib/concept-catalog.mjs +357 -0
  77. package/template/.agents/skills/design/impeccable/scripts/lib/impeccable-config.mjs +27 -7
  78. package/template/.agents/skills/design/impeccable/scripts/lib/impeccable-paths.mjs +17 -8
  79. package/template/.agents/skills/design/impeccable/scripts/lib/provider.mjs +5 -0
  80. package/template/.agents/skills/design/impeccable/scripts/lib/roll-selection.mjs +362 -0
  81. package/template/.agents/skills/design/impeccable/scripts/lib/staleness-deep.mjs +457 -0
  82. package/template/.agents/skills/design/impeccable/scripts/lib/staleness-notice.mjs +169 -0
  83. package/template/.agents/skills/design/impeccable/scripts/lib/staleness.mjs +457 -0
  84. package/template/.agents/skills/design/impeccable/scripts/lib/surface-briefs.mjs +151 -0
  85. package/template/.agents/skills/design/impeccable/scripts/lib/target-slug.mjs +33 -0
  86. package/template/.agents/skills/design/impeccable/scripts/lib/template-extensions.mjs +146 -0
  87. package/template/.agents/skills/design/impeccable/scripts/live/accept-css.mjs +617 -0
  88. package/template/.agents/skills/design/impeccable/scripts/live/accept-verify.mjs +60 -0
  89. package/template/.agents/skills/design/impeccable/scripts/live/browser-script-parts.mjs +7 -1
  90. package/template/.agents/skills/design/impeccable/scripts/live/completion.mjs +10 -1
  91. package/template/.agents/skills/design/impeccable/scripts/live/event-validation.mjs +67 -5
  92. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/astro.mjs +47 -0
  93. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/detect-utils.mjs +73 -0
  94. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/index.mjs +143 -0
  95. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/journal.mjs +197 -0
  96. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/nextjs.mjs +49 -0
  97. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/nuxt.mjs +161 -0
  98. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/script-src.mjs +17 -0
  99. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/static-html.mjs +26 -0
  100. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/sveltekit.mjs +71 -0
  101. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/tag-strategy.mjs +247 -0
  102. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/tanstack-start.mjs +70 -0
  103. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/vite-generic.mjs +42 -0
  104. package/template/.agents/skills/design/impeccable/scripts/live/generation-preflight.mjs +149 -0
  105. package/template/.agents/skills/design/impeccable/scripts/live/instructions.mjs +142 -0
  106. package/template/.agents/skills/design/impeccable/scripts/live/poll-lanes.mjs +14 -0
  107. package/template/.agents/skills/design/impeccable/scripts/live/roots.mjs +508 -0
  108. package/template/.agents/skills/design/impeccable/scripts/live/session-store.mjs +324 -50
  109. package/template/.agents/skills/design/impeccable/scripts/live/source-lock.mjs +105 -0
  110. package/template/.agents/skills/design/impeccable/scripts/live/source-search.mjs +105 -0
  111. package/template/.agents/skills/design/impeccable/scripts/live/svelte-ast.mjs +961 -0
  112. package/template/.agents/skills/design/impeccable/scripts/live/svelte-component.mjs +588 -72
  113. package/template/.agents/skills/design/impeccable/scripts/live/sveltekit-adapter.mjs +59 -17
  114. package/template/.agents/skills/design/impeccable/scripts/live/tanstack-adapter.mjs +280 -0
  115. package/template/.agents/skills/design/impeccable/scripts/live/vocabulary.mjs +135 -0
  116. package/template/.agents/skills/design/impeccable/scripts/live-accept.mjs +210 -68
  117. package/template/.agents/skills/design/impeccable/scripts/live-browser.js +1562 -223
  118. package/template/.agents/skills/design/impeccable/scripts/live-commit-manual-edits.mjs +3 -0
  119. package/template/.agents/skills/design/impeccable/scripts/live-complete.mjs +33 -1
  120. package/template/.agents/skills/design/impeccable/scripts/live-inject.mjs +175 -255
  121. package/template/.agents/skills/design/impeccable/scripts/live-insert.mjs +26 -6
  122. package/template/.agents/skills/design/impeccable/scripts/live-manual-edit-evidence.mjs +6 -1
  123. package/template/.agents/skills/design/impeccable/scripts/live-poll.mjs +61 -16
  124. package/template/.agents/skills/design/impeccable/scripts/live-resume.mjs +39 -10
  125. package/template/.agents/skills/design/impeccable/scripts/live-server.mjs +573 -47
  126. package/template/.agents/skills/design/impeccable/scripts/live-status.mjs +17 -7
  127. package/template/.agents/skills/design/impeccable/scripts/live-wrap.mjs +124 -91
  128. package/template/.agents/skills/design/impeccable/scripts/live.mjs +88 -26
  129. package/template/.agents/skills/design/impeccable/scripts/palette.mjs +76 -81
  130. package/template/.agents/skills/design/impeccable/scripts/pin.mjs +18 -11
  131. package/template/.agents/skills/design/impeccable/scripts/serve-question.mjs +932 -0
  132. package/template/.agents/skills/design/impeccable/scripts/surface-brief.mjs +74 -0
  133. package/template/.agents/skills/design/shadcn/SKILL.md +46 -11
  134. package/template/.agents/skills/design/shadcn/cli.md +49 -16
  135. package/template/.agents/skills/design/shadcn/customization.md +14 -7
  136. package/template/.agents/skills/design/shadcn/evals/evals.json +30 -0
  137. package/template/.agents/skills/design/shadcn/mcp.md +27 -16
  138. package/template/.agents/skills/design/shadcn/registry.md +277 -0
  139. package/template/.agents/skills/design/shadcn/rules/chat.md +224 -0
  140. package/template/.agents/skills/design/shadcn/rules/composition.md +20 -2
  141. package/template/.agents/skills/design/shadcn/rules/styling.md +23 -0
  142. package/template/.agents/skills/find-skills/SKILL.md +2 -3
  143. package/template/.agents/skills/hono/SKILL.md +579 -0
  144. package/template/.agents/skills/integrations/langfuse/SKILL.md +10 -6
  145. package/template/.agents/skills/integrations/langfuse/references/ci-cd.md +41 -0
  146. package/template/.agents/skills/integrations/langfuse/references/cli.md +8 -0
  147. package/template/.agents/skills/integrations/langfuse/references/error-analysis.md +15 -27
  148. package/template/.agents/skills/integrations/langfuse/references/instrumentation.md +39 -52
  149. package/template/.agents/skills/integrations/langfuse/references/judge-calibration.md +5 -2
  150. package/template/.agents/skills/integrations/langfuse/references/prompt-engineering.md +35 -0
  151. package/template/.agents/skills/integrations/langfuse/references/prompt-migration.md +41 -196
  152. package/template/.agents/skills/integrations/langfuse/references/skill-feedback.md +3 -0
  153. package/template/.agents/skills/integrations/langfuse/references/trace-evaluator-upgrade.md +76 -0
  154. package/template/.agents/skills/integrations/langfuse/references/user-feedback.md +4 -0
  155. package/template/.agents/skills/integrations/langfuse/references/v4-project-migration.md +73 -0
  156. package/template/.agents/skills/supabase/CHANGELOG.md +71 -0
  157. package/template/.agents/skills/supabase/SKILL.md +145 -0
  158. package/template/.agents/skills/supabase/assets/feedback-issue-template.md +17 -0
  159. package/template/.agents/skills/supabase/references/skill-feedback.md +17 -0
  160. package/template/.agents/skills/supabase-postgres-best-practices/CHANGELOG.md +73 -0
  161. package/template/.agents/skills/supabase-postgres-best-practices/SKILL.md +64 -0
  162. package/template/.agents/skills/supabase-postgres-best-practices/references/_contributing.md +170 -0
  163. package/template/.agents/skills/supabase-postgres-best-practices/references/_sections.md +39 -0
  164. package/template/.agents/skills/supabase-postgres-best-practices/references/_template.md +34 -0
  165. package/template/.agents/skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +55 -0
  166. package/template/.agents/skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +49 -0
  167. package/template/.agents/skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +46 -0
  168. package/template/.agents/skills/supabase-postgres-best-practices/references/conn-limits.md +44 -0
  169. package/template/.agents/skills/supabase-postgres-best-practices/references/conn-pooling.md +41 -0
  170. package/template/.agents/skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +46 -0
  171. package/template/.agents/skills/supabase-postgres-best-practices/references/data-batch-inserts.md +54 -0
  172. package/template/.agents/skills/supabase-postgres-best-practices/references/data-n-plus-one.md +53 -0
  173. package/template/.agents/skills/supabase-postgres-best-practices/references/data-pagination.md +50 -0
  174. package/template/.agents/skills/supabase-postgres-best-practices/references/data-upsert.md +50 -0
  175. package/template/.agents/skills/supabase-postgres-best-practices/references/lock-advisory.md +56 -0
  176. package/template/.agents/skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +68 -0
  177. package/template/.agents/skills/supabase-postgres-best-practices/references/lock-short-transactions.md +50 -0
  178. package/template/.agents/skills/supabase-postgres-best-practices/references/lock-skip-locked.md +54 -0
  179. package/template/.agents/skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +45 -0
  180. package/template/.agents/skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +55 -0
  181. package/template/.agents/skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +55 -0
  182. package/template/.agents/skills/supabase-postgres-best-practices/references/query-composite-indexes.md +44 -0
  183. package/template/.agents/skills/supabase-postgres-best-practices/references/query-covering-indexes.md +40 -0
  184. package/template/.agents/skills/supabase-postgres-best-practices/references/query-index-types.md +48 -0
  185. package/template/.agents/skills/supabase-postgres-best-practices/references/query-missing-indexes.md +43 -0
  186. package/template/.agents/skills/supabase-postgres-best-practices/references/query-partial-indexes.md +45 -0
  187. package/template/.agents/skills/supabase-postgres-best-practices/references/schema-constraints.md +80 -0
  188. package/template/.agents/skills/supabase-postgres-best-practices/references/schema-data-types.md +46 -0
  189. package/template/.agents/skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +59 -0
  190. package/template/.agents/skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +55 -0
  191. package/template/.agents/skills/supabase-postgres-best-practices/references/schema-partitioning.md +55 -0
  192. package/template/.agents/skills/supabase-postgres-best-practices/references/schema-primary-keys.md +61 -0
  193. package/template/.agents/skills/supabase-postgres-best-practices/references/security-privileges.md +54 -0
  194. package/template/.agents/skills/supabase-postgres-best-practices/references/security-rls-basics.md +50 -0
  195. package/template/.agents/skills/supabase-postgres-best-practices/references/security-rls-performance.md +63 -0
  196. package/template/.agents/upstreams.json +34 -12
  197. package/template/.agents/skills/design/impeccable/reference/brand.md +0 -108
  198. package/template/.agents/skills/design/impeccable/reference/codex.md +0 -105
  199. package/template/.agents/skills/design/impeccable/reference/interaction-design.md +0 -189
  200. package/template/.agents/skills/integrations/langfuse/references/sdk-upgrade.md +0 -175
@@ -26,6 +26,7 @@ import {
26
26
  scaffoldSvelteComponentInsertSession,
27
27
  shouldUseSvelteComponentInjection,
28
28
  } from './live/svelte-component.mjs';
29
+ import { enterLiveRoot } from './live/roots.mjs';
29
30
 
30
31
  const INSERT_POSITIONS = new Set(['before', 'after']);
31
32
 
@@ -131,6 +132,9 @@ Output (JSON):
131
132
  const query = argVal(args, '--query');
132
133
  const filePath = argVal(args, '--file');
133
134
  const text = argVal(args, '--text');
135
+ // See live-wrap.mjs: preflight computes the scaffold but leaves source
136
+ // untouched so the agent's single edit is the only framework reload.
137
+ const deferSourceWrite = args.includes('--defer-source-write');
134
138
 
135
139
  if (!id) { console.error('Missing --id'); process.exit(1); }
136
140
  if (!position) { console.error('Missing --position (before | after)'); process.exit(1); }
@@ -244,12 +248,23 @@ Output (JSON):
244
248
  isJsx,
245
249
  });
246
250
 
247
- const newLines = [
248
- ...lines.slice(0, spliceIndex),
249
- ...wrapperLines,
250
- ...lines.slice(spliceIndex),
251
- ];
252
- fs.writeFileSync(targetFile, newLines.join('\n'), 'utf-8');
251
+ let deferredWrapper = null;
252
+ if (deferSourceWrite) {
253
+ // Insert-as-empty-range: the agent inserts `wrapperBlock` (variants spliced
254
+ // at the marker) at spliceIndex without removing any source line.
255
+ deferredWrapper = {
256
+ block: wrapperLines.join('\n'),
257
+ replaceStartLine: spliceIndex + 1,
258
+ replaceEndLine: spliceIndex, // empty range (endLine < startLine) => insertion
259
+ };
260
+ } else {
261
+ const newLines = [
262
+ ...lines.slice(0, spliceIndex),
263
+ ...wrapperLines,
264
+ ...lines.slice(spliceIndex),
265
+ ];
266
+ fs.writeFileSync(targetFile, newLines.join('\n'), 'utf-8');
267
+ }
253
268
 
254
269
  const insertLine = spliceIndex + 3;
255
270
 
@@ -257,6 +272,10 @@ Output (JSON):
257
272
  mode: 'insert',
258
273
  position,
259
274
  file: relTargetFile,
275
+ sourceWritten: deferredWrapper ? false : undefined,
276
+ wrapperBlock: deferredWrapper ? deferredWrapper.block : undefined,
277
+ replaceStartLine: deferredWrapper ? deferredWrapper.replaceStartLine : undefined,
278
+ replaceEndLine: deferredWrapper ? deferredWrapper.replaceEndLine : undefined,
260
279
  insertLine: insertLine + 1,
261
280
  commentSyntax,
262
281
  styleMode: styleMode.mode,
@@ -268,5 +287,6 @@ Output (JSON):
268
287
 
269
288
  const _running = process.argv[1];
270
289
  if (_running?.endsWith('live-insert.mjs') || _running?.endsWith('live-insert.mjs/')) {
290
+ enterLiveRoot();
271
291
  insertCli();
272
292
  }
@@ -14,7 +14,12 @@ import { isGeneratedFile } from './lib/is-generated.mjs';
14
14
  import { readBuffer, getBufferPath } from './live/manual-edits-buffer.mjs';
15
15
 
16
16
  const EVIDENCE_VERSION = 1;
17
- const TEXT_EXTENSIONS = new Set(['.html', '.jsx', '.tsx', '.vue', '.svelte', '.astro', '.js', '.mjs', '.ts']);
17
+ const TEXT_EXTENSIONS = new Set([
18
+ '.html', '.jsx', '.tsx', '.vue', '.svelte', '.astro', '.js', '.mjs', '.ts',
19
+ // Phoenix keeps `~H"""` markup in .ex alongside standalone .heex/.eex
20
+ // templates, so copy edits land in all three.
21
+ '.ex', '.heex', '.eex',
22
+ ]);
18
23
  const SEARCH_DIRS = ['src', 'app', 'pages', 'components', 'public', 'views', 'templates', 'site', 'lib', 'data'];
19
24
  const STRONG_LITERAL_MATCH_LIMIT = 8;
20
25
  const WEAK_LITERAL_MATCH_LIMIT = 4;
@@ -14,6 +14,8 @@ import path from 'node:path';
14
14
  import { fileURLToPath } from 'node:url';
15
15
  import { completionAckForAcceptResult, completionTypeForAcceptResult } from './live/completion.mjs';
16
16
  import { readLiveServerInfo } from './lib/impeccable-paths.mjs';
17
+ import { enterLiveRoot } from './live/roots.mjs';
18
+ import { instructionsForEvent } from './live/instructions.mjs';
17
19
 
18
20
  // Absolute path to a sibling script in this skill's scripts dir, so runtime
19
21
  // error hints print a directly-runnable command instead of a placeholder.
@@ -27,7 +29,7 @@ const scriptCmd = (name) => `node "${path.join(SELF_DIR, name)}"`;
27
29
  export const PER_REQUEST_TIMEOUT_MS = 270_000;
28
30
  export const DEFAULT_EVENT_LEASE_MS = 600_000;
29
31
 
30
- const EVENT_TYPES_NEEDING_AGENT_REPLY = new Set(['generate', 'steer', 'manual_edit_apply']);
32
+ const EVENT_TYPES_NEEDING_AGENT_REPLY = new Set(['generate', 'steer', 'manual_edit_apply', 'carbonize_cleanup', 'variant_mount_failed']);
31
33
 
32
34
  function readServerInfo() {
33
35
  const record = readLiveServerInfo(process.cwd());
@@ -38,8 +40,8 @@ function readServerInfo() {
38
40
  return record.info;
39
41
  }
40
42
 
41
- export function buildPollReplyPayload(token, { id, type, message, file, data }) {
42
- return { token, id, type, message, file, data };
43
+ export function buildPollReplyPayload(token, { id, type, message, file, data, sourceEventType }) {
44
+ return { token, id, type, message, file, data, sourceEventType };
43
45
  }
44
46
 
45
47
  export function manualApplyPollBanner(event = {}) {
@@ -117,8 +119,11 @@ export async function postReply(base, token, reply) {
117
119
  });
118
120
  if (!res.ok) {
119
121
  const body = await res.json().catch(() => ({}));
120
- const parts = [body.error || res.statusText, body.reason, body.hint].filter(Boolean);
121
- throw new Error(parts.join(': '));
122
+ const failureLines = Array.isArray(body.failures)
123
+ ? body.failures.map((f) => ` ${f.file}${f.line != null ? `:${f.line}` : ''} ${f.message}`).join('\n')
124
+ : null;
125
+ const parts = [body.error || res.statusText, body.reason, body.hint, failureLines, body._instructions].filter(Boolean);
126
+ throw new Error(parts.join('\n'));
122
127
  }
123
128
  }
124
129
 
@@ -152,7 +157,14 @@ export async function waitForEventAck(base, token, eventId, {
152
157
  return false;
153
158
  }
154
159
 
155
- export async function fetchNextEvent(base, token, { totalDeadline } = {}) {
160
+ export async function fetchNextEvent(base, token, {
161
+ totalDeadline,
162
+ types,
163
+ resolveTypes,
164
+ perRequestTimeoutMs = PER_REQUEST_TIMEOUT_MS,
165
+ leaseMs = DEFAULT_EVENT_LEASE_MS,
166
+ signal,
167
+ } = {}) {
156
168
  while (true) {
157
169
  if (totalDeadline && Date.now() >= totalDeadline) {
158
170
  return { type: 'timeout' };
@@ -161,8 +173,15 @@ export async function fetchNextEvent(base, token, { totalDeadline } = {}) {
161
173
  const remaining = totalDeadline
162
174
  ? totalDeadline - Date.now()
163
175
  : PER_REQUEST_TIMEOUT_MS;
164
- const slice = Math.min(Math.max(remaining, 1000), PER_REQUEST_TIMEOUT_MS);
165
- const res = await fetch(`${base}/poll?token=${token}&timeout=${slice}&leaseMs=${DEFAULT_EVENT_LEASE_MS}`);
176
+ const slice = Math.min(Math.max(remaining, 1000), perRequestTimeoutMs);
177
+ const query = new URLSearchParams({
178
+ token,
179
+ timeout: String(slice),
180
+ leaseMs: String(leaseMs),
181
+ });
182
+ const normalizedTypes = normalizePollTypes(resolveTypes ? await resolveTypes() : types);
183
+ if (normalizedTypes.length > 0) query.set('types', normalizedTypes.join(','));
184
+ const res = await fetch(`${base}/poll?${query}`, { signal });
166
185
 
167
186
  if (res.status === 401) {
168
187
  const err = new Error('Authentication failed. The server token may have changed.');
@@ -202,11 +221,17 @@ export async function augmentEventWithAcceptHandling(event, base, token) {
202
221
  event._acceptResult = { handled: false, mode: 'error', error: err.message };
203
222
  }
204
223
 
224
+ await completeAcceptHandling(event, base, token);
225
+ return event;
226
+ }
227
+
228
+ export async function completeAcceptHandling(event, base, token) {
205
229
  const completionType = completionTypeForAcceptResult(event.type, event._acceptResult);
206
230
  try {
207
231
  await postReply(base, token, {
208
232
  id: event.id,
209
233
  type: completionType,
234
+ sourceEventType: event.type,
210
235
  message: event._acceptResult?.error,
211
236
  file: event._acceptResult?.file,
212
237
  data: event._acceptResult?.carbonize === true ? { carbonize: true } : undefined,
@@ -217,7 +242,6 @@ export async function augmentEventWithAcceptHandling(event, base, token) {
217
242
  if (!event._completionAck) {
218
243
  event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult);
219
244
  }
220
-
221
245
  return event;
222
246
  }
223
247
 
@@ -242,12 +266,19 @@ export function writeCarbonizeBanner(event) {
242
266
  }
243
267
 
244
268
  export function printPollEvent(event) {
269
+ // Situational plumbing rides with the event itself: `_instructions` is the
270
+ // authoritative next step, with real ids and paths substituted, so the
271
+ // reference doc can stay lean and can never drift from script behavior.
272
+ if (event && typeof event === 'object' && !event._instructions) {
273
+ const instructions = instructionsForEvent(event, { scriptsPath: SELF_DIR });
274
+ if (instructions) event._instructions = instructions;
275
+ }
245
276
  console.log(JSON.stringify(event));
246
277
  }
247
278
 
248
- export async function runPollOnce(base, token, { totalTimeout = 600_000 } = {}) {
279
+ export async function runPollOnce(base, token, { totalTimeout = 600_000, types, resolveTypes, perRequestTimeoutMs } = {}) {
249
280
  const deadline = Date.now() + totalTimeout;
250
- const event = await fetchNextEvent(base, token, { totalDeadline: deadline });
281
+ const event = await fetchNextEvent(base, token, { totalDeadline: deadline, types, resolveTypes, perRequestTimeoutMs });
251
282
  await augmentEventWithAcceptHandling(event, base, token);
252
283
  writeCarbonizeBanner(event);
253
284
  printPollEvent(event);
@@ -258,11 +289,14 @@ export async function runPollStream(base, token, {
258
289
  ackTimeoutMs = 600_000,
259
290
  ackPollIntervalMs = 400,
260
291
  shouldContinue = () => true,
292
+ types,
293
+ resolveTypes,
294
+ perRequestTimeoutMs,
261
295
  } = {}) {
262
296
  process.stderr.write('[impeccable-poll] stream mode: one JSON object per line on stdout; use --reply while this process stays running\n');
263
297
 
264
298
  while (shouldContinue()) {
265
- const event = await fetchNextEvent(base, token);
299
+ const event = await fetchNextEvent(base, token, { types, resolveTypes, perRequestTimeoutMs });
266
300
  await augmentEventWithAcceptHandling(event, base, token);
267
301
  writeCarbonizeBanner(event);
268
302
  printPollEvent(event);
@@ -322,14 +356,17 @@ Modes:
322
356
 
323
357
  Options:
324
358
  --timeout=MS One-shot poll timeout in ms (default: 600000). Ignored in --stream mode
359
+ --types=A,B Lease only these event types
325
360
  --ack-timeout=MS Stream mode: max wait for --reply after generate/steer (default: 600000)
326
361
  --file PATH Attach a source file path to the reply (generate/steer flow)
327
362
  --data JSON Attach a JSON result object to the reply (manual_edit_apply flow). Must be valid JSON
328
363
  --help Show this help message
329
364
 
330
365
  Harness note:
331
- Default one-shot mode is the portable contract for Claude Code, Codex, and Cursor.
332
- --stream is experimental for harnesses with fast incremental stdout; do not use on Cursor.`);
366
+ Default one-shot mode is the primary contract, including Codex foreground polling.
367
+ Claude Code may run it as a background task; Cursor uses a background terminal with exit notification.
368
+ --stream is retained for harnesses with measured, reliable incremental stdout.
369
+ Do not use --stream on Cursor.`);
333
370
  process.exit(0);
334
371
  }
335
372
 
@@ -360,25 +397,33 @@ Harness note:
360
397
  }
361
398
 
362
399
  const streamMode = args.includes('--stream');
400
+ const typesArg = args.find((a) => a.startsWith('--types='));
401
+ const types = normalizePollTypes(typesArg ? typesArg.slice('--types='.length) : null);
363
402
  const ackTimeoutArg = args.find((a) => a.startsWith('--ack-timeout='));
364
403
  const ackTimeoutMs = ackTimeoutArg ? parseInt(ackTimeoutArg.split('=')[1], 10) : 600_000;
365
404
 
366
405
  try {
367
406
  if (streamMode) {
368
- await runPollStream(base, info.token, { ackTimeoutMs });
407
+ await runPollStream(base, info.token, { ackTimeoutMs, types });
369
408
  return;
370
409
  }
371
410
 
372
411
  const timeoutArg = args.find((a) => a.startsWith('--timeout='));
373
412
  const totalTimeout = timeoutArg ? parseInt(timeoutArg.split('=')[1], 10) : 600_000;
374
- await runPollOnce(base, info.token, { totalTimeout });
413
+ await runPollOnce(base, info.token, { totalTimeout, types });
375
414
  } catch (err) {
376
415
  handlePollError(err);
377
416
  }
378
417
  }
379
418
 
419
+ export function normalizePollTypes(value) {
420
+ const values = Array.isArray(value) ? value : String(value || '').split(',');
421
+ return [...new Set(values.map((type) => String(type).trim()).filter(Boolean))];
422
+ }
423
+
380
424
  // Auto-execute when run directly
381
425
  const _running = process.argv[1];
382
426
  if (_running?.endsWith('live-poll.mjs') || _running?.endsWith('live-poll.mjs/')) {
427
+ enterLiveRoot();
383
428
  pollCli();
384
429
  }
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { createLiveSessionStore } from './live/session-store.mjs';
7
+ import { enterLiveRoot } from './live/roots.mjs';
7
8
 
8
9
  function manualApplyReplyCommand(eventOrId = 'EVENT_ID') {
9
10
  const id = typeof eventOrId === 'string' ? eventOrId : eventOrId?.id || 'EVENT_ID';
@@ -49,6 +50,28 @@ function collectManualApplyFiles(batch) {
49
50
  return [...new Set(files.filter((file) => typeof file === 'string' && file.length > 0))].sort();
50
51
  }
51
52
 
53
+ /**
54
+ * The browser's render truth, folded into a small block the agent reads before
55
+ * it decides what to do. `arrivedVariants` only says the agent published;
56
+ * `renderState` says whether any of it reached a screen.
57
+ */
58
+ export function renderSummary(snapshot = {}) {
59
+ return {
60
+ renderState: snapshot.renderState ?? null,
61
+ mountedVariants: Array.isArray(snapshot.mountedVariants) ? snapshot.mountedVariants : [],
62
+ mountFailures: Array.isArray(snapshot.mountFailures) ? snapshot.mountFailures : [],
63
+ };
64
+ }
65
+
66
+ export function mountFailureAction(snapshot = {}) {
67
+ const failures = Array.isArray(snapshot.mountFailures) ? snapshot.mountFailures : [];
68
+ const latest = failures[failures.length - 1];
69
+ if (!latest) return null;
70
+ const where = latest.url ? ` from ${latest.url}` : '';
71
+ const why = latest.error ? ` (${latest.error})` : '';
72
+ return `The browser failed to mount variant ${latest.variant}${where}${why}; nothing is on screen. Fix the variant files, then reply with live-poll.mjs --reply ${snapshot?.pendingEvent?.id || snapshot?.id || 'SESSION_ID'} done --file <manifest or source path> for the queued variant_mount_failed event (or republish) so the browser retries.`;
73
+ }
74
+
52
75
  function parseArgs(argv) {
53
76
  const out = { id: null };
54
77
  for (let i = 0; i < argv.length; i++) {
@@ -75,20 +98,26 @@ export async function resumeCli() {
75
98
  }
76
99
 
77
100
  const pending = snapshot.pendingEvent || null;
78
- const nextAction = pending
79
- ? pending.type === 'manual_edit_apply'
80
- ? manualApplyResumeHint(pending)
81
- : `Run live-poll.mjs, handle ${pending.type} ${pending.id}, then acknowledge with live-poll.mjs --reply ${pending.id} done.`
82
- : snapshot.phase === 'carbonize_required'
83
- ? `Finish carbonize cleanup${snapshot.sourceFile ? ` in ${snapshot.sourceFile}` : ''}, then run live-complete.mjs --id ${snapshot.id}.`
84
- : snapshot.phase === 'accept_requested'
85
- ? `Run live-complete.mjs --id ${snapshot.id} after verifying the accepted variant is written.`
86
- : `Inspect ${snapshot.id}; no pending agent event is currently queued.`;
101
+ const render = renderSummary(snapshot);
102
+ // A failed render outranks the generic pending-event hint: the agent needs to
103
+ // know the user is staring at an error card, not at variants. A leased manual
104
+ // Apply still outranks both, because abandoning that lease loses user edits.
105
+ const mountAction = render.renderState === 'failed' ? mountFailureAction(snapshot) : null;
106
+ const nextAction = pending?.type === 'manual_edit_apply'
107
+ ? manualApplyResumeHint(pending)
108
+ : mountAction || (pending
109
+ ? `Run live-poll.mjs, handle ${pending.type} ${pending.id}, then acknowledge with live-poll.mjs --reply ${pending.id} done.`
110
+ : snapshot.phase === 'carbonize_required'
111
+ ? `Finish carbonize cleanup${snapshot.sourceFile ? ` in ${snapshot.sourceFile}` : ''}, then run live-complete.mjs --id ${snapshot.id}.`
112
+ : snapshot.phase === 'accept_requested'
113
+ ? `Run live-complete.mjs --id ${snapshot.id} after verifying the accepted variant is written.`
114
+ : `Inspect ${snapshot.id}; no pending agent event is currently queued.`);
87
115
 
88
- console.log(JSON.stringify({ active: true, snapshot, pendingEvent: pending, nextAction }, null, 2));
116
+ console.log(JSON.stringify({ active: true, snapshot, pendingEvent: pending, render, nextAction }, null, 2));
89
117
  }
90
118
 
91
119
  const _running = process.argv[1];
92
120
  if (_running?.endsWith('live-resume.mjs') || _running?.endsWith('live-resume.mjs/')) {
121
+ enterLiveRoot();
93
122
  resumeCli();
94
123
  }