quiver-cli 0.8.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 +546 -458
  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
@@ -16,15 +16,59 @@
16
16
  import fs from 'node:fs';
17
17
  import path from 'node:path';
18
18
  import { isGeneratedFile } from './lib/is-generated.mjs';
19
+ import { getLiveDir, safeSessionId } from './lib/impeccable-paths.mjs';
20
+ import { resolveLiveTemplateExtensions } from './lib/template-extensions.mjs';
19
21
  import { readBuffer as readManualEditsBuffer, writeBuffer as writeManualEditsBuffer } from './live/manual-edits-buffer.mjs';
22
+ import { NEVER_SOURCE_DIRS, findSourceFile } from './live/source-search.mjs';
23
+ import { withSourceLockSync } from './live/source-lock.mjs';
20
24
  import {
21
25
  applyDeferredSvelteComponentAccepts,
22
26
  findSvelteComponentManifest,
23
27
  inlineSvelteComponentAccept,
24
28
  removeSvelteComponentSession,
25
29
  } from './live/svelte-component.mjs';
30
+ import { enterLiveRoot } from './live/roots.mjs';
26
31
 
27
- const EXTENSIONS = ['.html', '.jsx', '.tsx', '.vue', '.svelte', '.astro'];
32
+ const ACCEPT_LOCK_WAIT_MS = 1_000;
33
+ // Mirrors VARIANT_ID_PATTERN in live/event-validation.mjs, which gates the same
34
+ // value arriving over HTTP.
35
+ const VARIANT_NUM_PATTERN = /^[0-9]{1,3}$/;
36
+
37
+ /**
38
+ * A thrown accept/discard is a real failure, not a manual handoff.
39
+ *
40
+ * live/completion.mjs only classifies a result as `error` when it carries
41
+ * `mode: 'error'`; anything else unhandled falls through to `agent_done` with a
42
+ * successful ack, and reference/live.md then tells the agent to finish the edit
43
+ * by hand. That is right for the documented fallback paths and wrong here: a
44
+ * `source_locked` contention needs a retry (hand-editing races the publisher
45
+ * holding the lock), and a crash needs surfacing, not a hand-applied guess.
46
+ */
47
+ function operationFailure(err, extra = {}) {
48
+ return { handled: false, mode: 'error', error: err.message, ...extra };
49
+ }
50
+
51
+ /**
52
+ * Mark an unhandled preview-path result as a real failure.
53
+ *
54
+ * operationFailure only covers results built from a *thrown* error. The accept
55
+ * implementations also return `{handled: false, error}` for their own checks
56
+ * (variant missing, template empty, original text ambiguous), and those arrived
57
+ * without `mode`, so completion.mjs classified them as agent_done and
58
+ * reference/live.md routed the agent to "read file, find markers, edit".
59
+ *
60
+ * That handoff only makes sense for a plain wrapper session, which is the one
61
+ * shape with markers in the user's source to edit. Component and isolated
62
+ * artifact previews keep the source clean until Accept, so there is nothing to
63
+ * hand-edit and an unhandled result is always a failure. `previewMode` is
64
+ * exactly that discriminator: only the preview branches set it.
65
+ */
66
+ function markPreviewFailure(result) {
67
+ if (result?.handled === false && !result.mode && result.previewMode) {
68
+ return { ...result, mode: 'error' };
69
+ }
70
+ return result;
71
+ }
28
72
 
29
73
  // ---------------------------------------------------------------------------
30
74
  // CLI
@@ -63,7 +107,52 @@ Output (JSON):
63
107
  const isDiscard = args.includes('--discard');
64
108
 
65
109
  if (!id) { console.error('Missing --id'); process.exit(1); }
110
+ // `id` becomes a path segment (accept receipts, preview manifests, generated
111
+ // component dirs). Reject separators and traversal here so one check covers
112
+ // every downstream sink.
113
+ try { safeSessionId(id); } catch { console.error('Invalid --id'); process.exit(1); }
66
114
  if (!isDiscard && !variantNum) { console.error('Need --discard or --variant N'); process.exit(1); }
115
+ // `variantNum` is interpolated into a RegExp and into the markup written back
116
+ // to source. The browser and the /events schema both constrain it to digits;
117
+ // enforce the same here, or `--variant '.*'` matches the `original` block
118
+ // first and silently accepts the original while reporting success.
119
+ if (!isDiscard && !VARIANT_NUM_PATTERN.test(variantNum)) {
120
+ console.error('Invalid --variant');
121
+ process.exit(1);
122
+ }
123
+
124
+ const requestedOperation = isDiscard ? 'discard' : 'accept';
125
+ const priorReceipt = readAcceptReceipt(process.cwd(), id);
126
+ if (priorReceipt) {
127
+ const sameOperation = priorReceipt.operation === requestedOperation
128
+ && (isDiscard || String(priorReceipt.variantId) === String(variantNum));
129
+ console.log(JSON.stringify(sameOperation
130
+ ? { ...priorReceipt.result, handled: true, alreadyApplied: true }
131
+ : {
132
+ // mode: 'error' is what marks this a real failure rather than a manual
133
+ // handoff. Without it, live/completion.mjs classifies the reply as
134
+ // agent_done and reference/live.md tells the agent to "read file, find
135
+ // markers, edit" by hand — which would apply a second, conflicting
136
+ // accept on top of the one the receipt already recorded.
137
+ handled: false,
138
+ mode: 'error',
139
+ error: 'accept_receipt_conflict',
140
+ priorOperation: priorReceipt.operation,
141
+ priorVariantId: priorReceipt.variantId ?? null,
142
+ }));
143
+ return;
144
+ }
145
+ const emitResult = (rawResult) => {
146
+ const result = markPreviewFailure(rawResult);
147
+ if (result?.handled !== false) {
148
+ writeAcceptReceipt(process.cwd(), id, {
149
+ operation: requestedOperation,
150
+ variantId: isDiscard ? null : String(variantNum),
151
+ result,
152
+ });
153
+ }
154
+ console.log(JSON.stringify(result));
155
+ };
67
156
 
68
157
  let paramValues = null;
69
158
  if (paramValuesRaw) {
@@ -82,39 +171,55 @@ Output (JSON):
82
171
 
83
172
  if (svelteComponentManifest) {
84
173
  if (isDiscard) {
85
- removeSvelteComponentSession(id, process.cwd());
86
- console.log(JSON.stringify({
87
- handled: true,
174
+ let result;
175
+ try {
176
+ result = withSourceLockSync(
177
+ path.resolve(process.cwd(), svelteComponentManifest.sourceFile),
178
+ 'discard:' + id,
179
+ () => {
180
+ removeSvelteComponentSession(id, process.cwd());
181
+ return { handled: true };
182
+ },
183
+ { waitMs: ACCEPT_LOCK_WAIT_MS },
184
+ );
185
+ } catch (err) {
186
+ result = operationFailure(err);
187
+ }
188
+ emitResult({
189
+ ...result,
88
190
  file: svelteComponentManifest.sourceFile,
89
191
  carbonize: false,
90
192
  previewMode: 'svelte-component',
91
193
  componentDir: svelteComponentManifest.componentDir,
92
- }));
194
+ });
93
195
  return;
94
196
  }
95
197
 
96
198
  let result;
97
199
  try {
98
- result = inlineSvelteComponentAccept(
99
- svelteComponentManifest,
100
- variantNum,
101
- paramValues,
102
- process.cwd(),
200
+ result = withSourceLockSync(
201
+ path.resolve(process.cwd(), svelteComponentManifest.sourceFile),
202
+ 'accept:' + id,
203
+ () => inlineSvelteComponentAccept(
204
+ svelteComponentManifest,
205
+ variantNum,
206
+ paramValues,
207
+ process.cwd(),
208
+ ),
209
+ { waitMs: ACCEPT_LOCK_WAIT_MS },
103
210
  );
104
211
  } catch (err) {
105
- result = {
106
- handled: false,
107
- error: err.message,
212
+ result = operationFailure(err, {
108
213
  file: svelteComponentManifest.sourceFile,
109
214
  sourceFile: svelteComponentManifest.sourceFile,
110
215
  previewMode: 'svelte-component',
111
216
  componentDir: svelteComponentManifest.componentDir,
112
- };
217
+ });
113
218
  }
114
219
  if (result.carbonize) {
115
220
  result.todo = 'REQUIRED before next poll: carbonize cleanup in ' + result.file + '. See reference/live.md "Required after accept".';
116
221
  }
117
- console.log(JSON.stringify({ handled: result.handled !== false, ...result }));
222
+ emitResult({ handled: result.handled !== false, ...result });
118
223
  return;
119
224
  }
120
225
 
@@ -145,10 +250,25 @@ Output (JSON):
145
250
  }
146
251
 
147
252
  if (isDiscard) {
148
- const result = handleDiscard(id, lines, targetFile);
149
- console.log(JSON.stringify({ handled: true, file: relFile, carbonize: false, ...result }));
253
+ let result;
254
+ // handleDiscard takes the source lock, which throws SOURCE_LOCKED under
255
+ // contention. Without this catch the CLI exits non-zero with empty stdout
256
+ // and the agent gets no JSON to act on.
257
+ try {
258
+ result = handleDiscard(id, lines, targetFile);
259
+ } catch (err) {
260
+ emitResult(operationFailure(err, { file: relFile }));
261
+ return;
262
+ }
263
+ emitResult({ handled: true, file: relFile, carbonize: false, ...result });
150
264
  } else {
151
- const result = handleAccept(id, variantNum, lines, targetFile, paramValues);
265
+ let result;
266
+ try {
267
+ result = handleAccept(id, variantNum, lines, targetFile, paramValues);
268
+ } catch (err) {
269
+ emitResult(operationFailure(err, { file: relFile }));
270
+ return;
271
+ }
152
272
  const acceptedOriginalText = result.acceptedOriginalText || '';
153
273
  delete result.acceptedOriginalText;
154
274
  // Single-line attention-grabber when cleanup is required. The full
@@ -167,7 +287,7 @@ Output (JSON):
167
287
  // Non-fatal; the buffer stays as-is and the user can discard later.
168
288
  }
169
289
  }
170
- console.log(JSON.stringify({ handled: true, file: relFile, ...result }));
290
+ emitResult({ handled: true, file: relFile, ...result });
171
291
  }
172
292
  }
173
293
 
@@ -235,7 +355,14 @@ function scrubManualEditsAgainstFile(_targetFile, cwd = process.cwd(), originalB
235
355
  // Discard
236
356
  // ---------------------------------------------------------------------------
237
357
 
238
- function handleDiscard(id, lines, targetFile) {
358
+ function handleDiscard(id, _lines, targetFile) {
359
+ return withSourceLockSync(targetFile, 'discard:' + id, () => {
360
+ const lines = fs.readFileSync(targetFile, 'utf-8').split('\n');
361
+ return handleDiscardUnlocked(id, lines, targetFile);
362
+ }, { waitMs: ACCEPT_LOCK_WAIT_MS });
363
+ }
364
+
365
+ function handleDiscardUnlocked(id, lines, targetFile) {
239
366
  const block = findMarkerBlock(id, lines);
240
367
  if (!block) return { handled: false, error: 'Markers not found' };
241
368
 
@@ -330,7 +457,24 @@ function reindentContent(contentLines, fromIndent, toIndent) {
330
457
  });
331
458
  }
332
459
 
333
- function handleAccept(id, variantNum, lines, targetFile, paramValues) {
460
+ function handleAccept(id, variantNum, _lines, targetFile, paramValues) {
461
+ return withSourceLockSync(targetFile, 'accept:' + id, () => {
462
+ const lines = fs.readFileSync(targetFile, 'utf-8').split('\n');
463
+ return handleAcceptUnlocked(id, variantNum, lines, targetFile, paramValues);
464
+ }, { waitMs: ACCEPT_LOCK_WAIT_MS });
465
+ }
466
+
467
+ function handleAcceptUnlocked(id, variantNum, lines, targetFile, paramValues) {
468
+ const built = buildAcceptedWrappedSource(id, variantNum, lines, targetFile, paramValues);
469
+ if (built.handled === false) return built;
470
+ fs.writeFileSync(targetFile, built.content, 'utf-8');
471
+ return {
472
+ carbonize: built.carbonize,
473
+ acceptedOriginalText: built.acceptedOriginalText,
474
+ };
475
+ }
476
+
477
+ function buildAcceptedWrappedSource(id, variantNum, lines, targetFile, paramValues) {
334
478
  const block = findMarkerBlock(id, lines);
335
479
  if (!block) return { handled: false, error: 'Markers not found' };
336
480
 
@@ -375,11 +519,14 @@ function handleAccept(id, variantNum, lines, targetFile, paramValues) {
375
519
  ...replacement,
376
520
  ...lines.slice(replaceRange.end + 1),
377
521
  ];
378
- fs.writeFileSync(targetFile, newLines.join('\n'), 'utf-8');
379
-
380
- return { carbonize: needsCarbonize, acceptedOriginalText: originalContent.join('\n') };
522
+ return {
523
+ content: newLines.join('\n'),
524
+ carbonize: needsCarbonize,
525
+ acceptedOriginalText: originalContent.join('\n'),
526
+ };
381
527
  }
382
528
 
529
+
383
530
  function readSourceShadowPreviewMeta(content, id) {
384
531
  const escaped = escapeRegExp(id);
385
532
  const wrapperRe = new RegExp('<[^>]+data-impeccable-variants=(["\'])' + escaped + '\\1[^>]*>');
@@ -746,57 +893,51 @@ function detectCommentSyntax(filePath) {
746
893
  // File search (find the file containing session markers)
747
894
  // ---------------------------------------------------------------------------
748
895
 
896
+ /**
897
+ * Accept also skips `dist` / `build` outright, where wrap descends into them so
898
+ * its `includeGenerated` second pass can report a `generatedMatch`. Accept has
899
+ * no such pass: a marker found in build output is only ever a stale copy of the
900
+ * marker in source.
901
+ */
902
+ const SEARCH_SKIP_DIRS = [...NEVER_SOURCE_DIRS, 'dist', 'build'];
903
+
749
904
  function findSessionFile(id, cwd) {
750
- const marker = 'impeccable-variants-start ' + id;
751
- const searchDirs = ['src', 'app', 'pages', 'components', 'public', 'views', 'templates', '.'];
752
- const seen = new Set();
753
-
754
- for (const dir of searchDirs) {
755
- const absDir = path.join(cwd, dir);
756
- if (!fs.existsSync(absDir)) continue;
757
- const result = searchDir(absDir, marker, seen, 0);
758
- if (result) {
759
- const content = fs.readFileSync(result, 'utf-8');
760
- return { file: result, content, lines: content.split('\n') };
761
- }
762
- }
763
- return null;
905
+ const result = findSourceFile({
906
+ query: 'impeccable-variants-start ' + id,
907
+ cwd,
908
+ extensions: resolveLiveTemplateExtensions(cwd),
909
+ skipDirs: SEARCH_SKIP_DIRS,
910
+ });
911
+ if (!result) return null;
912
+ const content = fs.readFileSync(result, 'utf-8');
913
+ return { file: result, content, lines: content.split('\n') };
764
914
  }
765
915
 
766
- function searchDir(dir, query, seen, depth) {
767
- if (depth > 5) return null;
768
- let realDir;
769
- try { realDir = fs.realpathSync(dir); } catch { return null; }
770
- if (seen.has(realDir)) return null;
771
- seen.add(realDir);
772
-
773
- let entries;
774
- try { entries = fs.readdirSync(dir, { withFileTypes: true }); }
775
- catch { return null; }
776
-
777
- for (const entry of entries) {
778
- if (!entry.isFile()) continue;
779
- if (!EXTENSIONS.includes(path.extname(entry.name).toLowerCase())) continue;
780
- const filePath = path.join(dir, entry.name);
781
- try {
782
- const content = fs.readFileSync(filePath, 'utf-8');
783
- if (content.includes(query)) return filePath;
784
- } catch { /* skip */ }
785
- }
916
+ // ---------------------------------------------------------------------------
917
+ // Utilities
918
+ // ---------------------------------------------------------------------------
786
919
 
787
- for (const entry of entries) {
788
- if (!entry.isDirectory()) continue;
789
- if (['node_modules', '.git', 'dist', 'build'].includes(entry.name)) continue;
790
- const result = searchDir(path.join(dir, entry.name), query, seen, depth + 1);
791
- if (result) return result;
792
- }
920
+ function acceptReceiptPath(cwd, id) {
921
+ return path.join(getLiveDir(cwd), 'accept-receipts', `${safeSessionId(id)}.json`);
922
+ }
793
923
 
794
- return null;
924
+ function readAcceptReceipt(cwd, id) {
925
+ try { return JSON.parse(fs.readFileSync(acceptReceiptPath(cwd, id), 'utf-8')); } catch { return null; }
795
926
  }
796
927
 
797
- // ---------------------------------------------------------------------------
798
- // Utilities
799
- // ---------------------------------------------------------------------------
928
+ function writeAcceptReceipt(cwd, id, receipt) {
929
+ const file = acceptReceiptPath(cwd, id);
930
+ fs.mkdirSync(path.dirname(file), { recursive: true });
931
+ const value = {
932
+ id,
933
+ ...receipt,
934
+ completedAt: new Date().toISOString(),
935
+ };
936
+ const temporary = `${file}.${process.pid}.${Date.now()}.tmp`;
937
+ fs.writeFileSync(temporary, JSON.stringify(value, null, 2) + '\n', 'utf-8');
938
+ fs.renameSync(temporary, file);
939
+ return value;
940
+ }
800
941
 
801
942
  function argVal(args, flag) {
802
943
  const idx = args.indexOf(flag);
@@ -806,6 +947,7 @@ function argVal(args, flag) {
806
947
  // Auto-execute when run directly
807
948
  const _running = process.argv[1];
808
949
  if (_running?.endsWith('live-accept.mjs') || _running?.endsWith('live-accept.mjs/')) {
950
+ enterLiveRoot();
809
951
  acceptCli();
810
952
  }
811
953