quiver-cli 1.2.0 → 1.3.1

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 (121) hide show
  1. package/dist/cli.js +237 -10
  2. package/package.json +1 -1
  3. package/template/.agents/skills/agent-browser/SKILL.md +1 -0
  4. package/template/.agents/skills/apps/skybridge/SKILL.md +2 -0
  5. package/template/.agents/skills/data/prisma-cli/SKILL.md +25 -7
  6. package/template/.agents/skills/data/prisma-cli/references/agent-safety.md +27 -0
  7. package/template/.agents/skills/data/prisma-cli/references/complete.md +22 -0
  8. package/template/.agents/skills/data/prisma-cli/references/db-push.md +2 -0
  9. package/template/.agents/skills/data/prisma-cli/references/init.md +3 -0
  10. package/template/.agents/skills/data/prisma-cli/references/mcp.md +2 -1
  11. package/template/.agents/skills/data/prisma-cli/references/migrate-dev.md +1 -1
  12. package/template/.agents/skills/data/prisma-cli/references/migrate-reset.md +2 -0
  13. package/template/.agents/skills/data/prisma-client-api/SKILL.md +1 -1
  14. package/template/.agents/skills/data/prisma-client-api/references/constructor.md +13 -0
  15. package/template/.agents/skills/data/prisma-client-api/references/raw-queries.md +4 -0
  16. package/template/.agents/skills/design/impeccable/SKILL.md +5 -5
  17. package/template/.agents/skills/design/impeccable/reference/android.md +6 -0
  18. package/template/.agents/skills/design/impeccable/reference/animate.md +3 -0
  19. package/template/.agents/skills/design/impeccable/reference/bolder.md +3 -1
  20. package/template/.agents/skills/design/impeccable/reference/craft-floor.md +2 -0
  21. package/template/.agents/skills/design/impeccable/reference/critique.md +23 -5
  22. package/template/.agents/skills/design/impeccable/reference/degraded/asset-producer.md +15 -68
  23. package/template/.agents/skills/design/impeccable/reference/degraded/finish-reviewer.md +13 -12
  24. package/template/.agents/skills/design/impeccable/reference/distill.md +1 -1
  25. package/template/.agents/skills/design/impeccable/reference/doctor.md +1 -0
  26. package/template/.agents/skills/design/impeccable/reference/document.md +1 -1
  27. package/template/.agents/skills/design/impeccable/reference/extract.md +1 -1
  28. package/template/.agents/skills/design/impeccable/reference/hooks.md +17 -11
  29. package/template/.agents/skills/design/impeccable/reference/init.md +9 -3
  30. package/template/.agents/skills/design/impeccable/reference/ios.md +6 -0
  31. package/template/.agents/skills/design/impeccable/reference/new-work.md +69 -29
  32. package/template/.agents/skills/design/impeccable/reference/overdrive.md +1 -1
  33. package/template/.agents/skills/design/impeccable/reference/polish.md +13 -5
  34. package/template/.agents/skills/design/impeccable/reference/quieter.md +1 -1
  35. package/template/.agents/skills/design/impeccable/reference/routing.md +1 -1
  36. package/template/.agents/skills/design/impeccable/reference/visualize.md +21 -22
  37. package/template/.agents/skills/design/impeccable/scripts/build-phase.mjs +1022 -0
  38. package/template/.agents/skills/design/impeccable/scripts/comp-diff.mjs +391 -0
  39. package/template/.agents/skills/design/impeccable/scripts/comp-spec.mjs +513 -0
  40. package/template/.agents/skills/design/impeccable/scripts/concept-seed.mjs +297 -41
  41. package/template/.agents/skills/design/impeccable/scripts/context-signals.mjs +10 -19
  42. package/template/.agents/skills/design/impeccable/scripts/context.mjs +124 -9
  43. package/template/.agents/skills/design/impeccable/scripts/critique-storage.mjs +279 -19
  44. package/template/.agents/skills/design/impeccable/scripts/data/font-index-failures.json +121 -0
  45. package/template/.agents/skills/design/impeccable/scripts/data/font-index.json +1 -0
  46. package/template/.agents/skills/design/impeccable/scripts/detect.mjs +9 -0
  47. package/template/.agents/skills/design/impeccable/scripts/detector/browser/injected/index.mjs +192 -11
  48. package/template/.agents/skills/design/impeccable/scripts/detector/cli/main.mjs +10 -16
  49. package/template/.agents/skills/design/impeccable/scripts/detector/design-system.mjs +339 -11
  50. package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns-browser.js +1482 -722
  51. package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns.mjs +1 -0
  52. package/template/.agents/skills/design/impeccable/scripts/detector/engines/browser/detect-url.mjs +64 -2
  53. package/template/.agents/skills/design/impeccable/scripts/detector/engines/regex/detect-text.mjs +580 -29
  54. package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +62 -7
  55. package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/detect-html.mjs +31 -7
  56. package/template/.agents/skills/design/impeccable/scripts/detector/node/file-system.mjs +23 -22
  57. package/template/.agents/skills/design/impeccable/scripts/detector/registry/antipatterns.mjs +18 -0
  58. package/template/.agents/skills/design/impeccable/scripts/detector/rules/checks.mjs +471 -370
  59. package/template/.agents/skills/design/impeccable/scripts/detector/shared/color.mjs +474 -2
  60. package/template/.agents/skills/design/impeccable/scripts/detector/shared/constants.mjs +17 -2
  61. package/template/.agents/skills/design/impeccable/scripts/doctor.mjs +14 -21
  62. package/template/.agents/skills/design/impeccable/scripts/embed-prompt.mjs +81 -48
  63. package/template/.agents/skills/design/impeccable/scripts/font-match.mjs +457 -0
  64. package/template/.agents/skills/design/impeccable/scripts/generate-image.mjs +219 -12
  65. package/template/.agents/skills/design/impeccable/scripts/hook-admin.mjs +93 -15
  66. package/template/.agents/skills/design/impeccable/scripts/hook-before-edit.mjs +41 -19
  67. package/template/.agents/skills/design/impeccable/scripts/hook-lib.mjs +507 -117
  68. package/template/.agents/skills/design/impeccable/scripts/hook.mjs +10 -9
  69. package/template/.agents/skills/design/impeccable/scripts/lib/concept-catalog.mjs +40 -1
  70. package/template/.agents/skills/design/impeccable/scripts/lib/design-parser.mjs +120 -82
  71. package/template/.agents/skills/design/impeccable/scripts/lib/font-fingerprint.mjs +564 -0
  72. package/template/.agents/skills/design/impeccable/scripts/lib/font-index.mjs +130 -0
  73. package/template/.agents/skills/design/impeccable/scripts/lib/hero-checks.mjs +246 -0
  74. package/template/.agents/skills/design/impeccable/scripts/lib/image-metrics.mjs +306 -0
  75. package/template/.agents/skills/design/impeccable/scripts/lib/impeccable-config.mjs +41 -59
  76. package/template/.agents/skills/design/impeccable/scripts/lib/is-generated.mjs +5 -2
  77. package/template/.agents/skills/design/impeccable/scripts/lib/live-path-globs.mjs +37 -0
  78. package/template/.agents/skills/design/impeccable/scripts/lib/open-system-browser.mjs +26 -0
  79. package/template/.agents/skills/design/impeccable/scripts/lib/png.mjs +281 -0
  80. package/template/.agents/skills/design/impeccable/scripts/lib/raster.mjs +194 -0
  81. package/template/.agents/skills/design/impeccable/scripts/lib/roll-selection.mjs +26 -19
  82. package/template/.agents/skills/design/impeccable/scripts/lib/staleness-deep.mjs +31 -3
  83. package/template/.agents/skills/design/impeccable/scripts/lib/staleness.mjs +93 -17
  84. package/template/.agents/skills/design/impeccable/scripts/lib/surface-briefs.mjs +9 -11
  85. package/template/.agents/skills/design/impeccable/scripts/live/browser-script-parts.mjs +31 -2
  86. package/template/.agents/skills/design/impeccable/scripts/live/project-ignores.mjs +139 -0
  87. package/template/.agents/skills/design/impeccable/scripts/live/svelte-ast.mjs +10 -2
  88. package/template/.agents/skills/design/impeccable/scripts/live/svelte-component.mjs +26 -2
  89. package/template/.agents/skills/design/impeccable/scripts/live/sveltekit-adapter.mjs +15 -27
  90. package/template/.agents/skills/design/impeccable/scripts/live/tanstack-adapter.mjs +4 -25
  91. package/template/.agents/skills/design/impeccable/scripts/live/ui-surfaces.mjs +75 -0
  92. package/template/.agents/skills/design/impeccable/scripts/live-accept.mjs +21 -37
  93. package/template/.agents/skills/design/impeccable/scripts/live-browser-ignores.js +242 -0
  94. package/template/.agents/skills/design/impeccable/scripts/live-browser.js +62 -23
  95. package/template/.agents/skills/design/impeccable/scripts/live-commit-manual-edits.mjs +68 -112
  96. package/template/.agents/skills/design/impeccable/scripts/live-copy-edit-agent.mjs +132 -15
  97. package/template/.agents/skills/design/impeccable/scripts/live-inject.mjs +2 -42
  98. package/template/.agents/skills/design/impeccable/scripts/live-poll.mjs +5 -4
  99. package/template/.agents/skills/design/impeccable/scripts/live-server.mjs +56 -19
  100. package/template/.agents/skills/design/impeccable/scripts/live.mjs +12 -37
  101. package/template/.agents/skills/design/impeccable/scripts/pin.mjs +8 -5
  102. package/template/.agents/skills/design/impeccable/scripts/serve-question.mjs +1006 -155
  103. package/template/.agents/skills/design/shadcn/rules/chat.md +26 -0
  104. package/template/.agents/skills/hono/SKILL.md +17 -3
  105. package/template/.agents/skills/integrations/langfuse/SKILL.md +8 -4
  106. package/template/.agents/skills/integrations/langfuse/references/cli.md +1 -1
  107. package/template/.agents/skills/integrations/langfuse/references/create-dataset.md +35 -0
  108. package/template/.agents/skills/integrations/langfuse/references/error-analysis.md +9 -10
  109. package/template/.agents/skills/integrations/langfuse/references/judge-calibration.md +8 -6
  110. package/template/.agents/skills/integrations/langfuse/references/sdk-upgrade.md +34 -0
  111. package/template/.agents/skills/integrations/langfuse/references/setting-up-evals.md +65 -0
  112. package/template/.agents/skills/integrations/langfuse/references/skill-feedback.md +20 -40
  113. package/template/.agents/skills/integrations/langfuse/references/user-feedback.md +4 -30
  114. package/template/.agents/skills/integrations/langfuse/references/v4-project-migration.md +3 -1
  115. package/template/.agents/skills/supabase/CHANGELOG.md +7 -0
  116. package/template/.agents/skills/supabase/SKILL.md +5 -1
  117. package/template/.agents/skills/writing/humanizer/LICENSE +21 -0
  118. package/template/.agents/skills/writing/humanizer/README.md +209 -0
  119. package/template/.agents/skills/writing/humanizer/SKILL.md +173 -338
  120. package/template/.agents/upstreams.json +21 -22
  121. package/template/.agents/skills/design/impeccable/scripts/live/ui-core.mjs +0 -180
@@ -944,8 +944,42 @@ export async function commitManualEdits({
944
944
  };
945
945
  }
946
946
 
947
+ const repairContext = {
948
+ batch,
949
+ cwd,
950
+ pageUrl,
951
+ count,
952
+ provider,
953
+ env,
954
+ timeoutMs,
955
+ applyBatchToSource,
956
+ chatAvailable,
957
+ transactionId,
958
+ };
959
+
947
960
  const baseRollbackScope = collectApplyOwnedFiles(batch, cwd);
948
961
  const rollbackSnapshot = snapshotRollbackFiles(cwd, baseRollbackScope);
962
+ const failWithRollback = ({
963
+ scope = baseRollbackScope,
964
+ extraFiles = [],
965
+ failed,
966
+ files = [],
967
+ details = {},
968
+ }) => {
969
+ const rollback = rollbackChangedFiles(cwd, rollbackSnapshot, extraFiles, scope);
970
+ return {
971
+ applied: [],
972
+ failed,
973
+ files,
974
+ cleared: 0,
975
+ count,
976
+ pageUrl,
977
+ ...details,
978
+ rolledBackFiles: rollback.rolledBackFiles,
979
+ rollbackFailures: rollback.rollbackFailures,
980
+ ...countByPage(cwd),
981
+ };
982
+ };
949
983
  let result;
950
984
  try {
951
985
  result = repairOnly
@@ -965,42 +999,27 @@ export async function commitManualEdits({
965
999
  chatAvailable,
966
1000
  });
967
1001
  } catch (err) {
968
- const rollback = rollbackChangedFiles(cwd, rollbackSnapshot, [], baseRollbackScope);
969
- return {
970
- applied: [],
1002
+ return failWithRollback({
971
1003
  failed: batch.entries.map((entry) => ({
972
1004
  id: entry.id,
973
1005
  reason: err.message || String(err),
974
1006
  candidates: candidatesForEntry(batch, entry.id),
975
1007
  })),
976
- files: [],
977
- cleared: 0,
978
- count,
979
- pageUrl,
980
- rolledBackFiles: rollback.rolledBackFiles,
981
- rollbackFailures: rollback.rollbackFailures,
982
- ...countByPage(cwd),
983
- };
1008
+ });
984
1009
  }
985
1010
 
986
1011
  if (result.status === 'error') {
987
1012
  const rollbackScope = collectApplyOwnedFiles(batch, cwd, result.files || []);
988
- const rollback = rollbackChangedFiles(cwd, rollbackSnapshot, result.files || [], rollbackScope);
989
1013
  const failed = normalizeFailedEntries(batch, result, result.message || 'AI copy edit failed');
990
- return {
991
- applied: [],
1014
+ return failWithRollback({
1015
+ scope: rollbackScope,
1016
+ extraFiles: result.files || [],
992
1017
  failed: failed.length > 0
993
1018
  ? failed
994
1019
  : verificationFailuresForEntries(batch, batch.entries, result.message || 'AI copy edit failed'),
995
1020
  files: result.files || [],
996
- cleared: 0,
997
- count,
998
- pageUrl,
999
- notes: result.notes || [],
1000
- rolledBackFiles: rollback.rolledBackFiles,
1001
- rollbackFailures: rollback.rollbackFailures,
1002
- ...countByPage(cwd),
1003
- };
1021
+ details: { notes: result.notes || [] },
1022
+ });
1004
1023
  }
1005
1024
 
1006
1025
  const reportedAppliedIds = uniqueStrings(result.appliedEntryIds || []);
@@ -1013,72 +1032,44 @@ export async function commitManualEdits({
1013
1032
  const conflictingAppliedIds = reportedAppliedIds.filter((id) => failedIds.has(id));
1014
1033
 
1015
1034
  if (conflictingAppliedIds.length > 0) {
1016
- const rollback = rollbackChangedFiles(cwd, rollbackSnapshot, result.files || [], rollbackScope);
1017
1035
  const conflictingEntries = batch.entries.filter((entry) => conflictingAppliedIds.includes(entry.id));
1018
- return {
1019
- applied: [],
1036
+ return failWithRollback({
1037
+ scope: rollbackScope,
1038
+ extraFiles: result.files || [],
1020
1039
  failed: [
1021
1040
  ...verificationFailuresForEntries(batch, conflictingEntries, 'conflicting_apply_result'),
1022
1041
  ...aiFailed.filter((item) => !conflictingAppliedIds.includes(item.id)),
1023
1042
  ],
1024
1043
  files: result.files || [],
1025
- cleared: 0,
1026
- count,
1027
- pageUrl,
1028
- notes: result.notes || [],
1029
- rolledBackFiles: rollback.rolledBackFiles,
1030
- rollbackFailures: rollback.rollbackFailures,
1031
- ...countByPage(cwd),
1032
- };
1044
+ details: { notes: result.notes || [] },
1045
+ });
1033
1046
  }
1034
1047
 
1035
1048
  const unreportedFiles = unreportedChangedFiles(cwd, rollbackSnapshot, result.files || [], rollbackScope);
1036
1049
  if (unreportedFiles.length > 0) {
1037
- const rollback = rollbackChangedFiles(cwd, rollbackSnapshot, result.files || [], [...rollbackScope, ...unreportedFiles]);
1038
- return {
1039
- applied: [],
1050
+ return failWithRollback({
1051
+ scope: [...rollbackScope, ...unreportedFiles],
1052
+ extraFiles: result.files || [],
1040
1053
  failed: verificationFailuresForEntries(batch, batch.entries, 'unreported_source_changes', { files: unreportedFiles }),
1041
1054
  files: result.files || [],
1042
- unreportedFiles,
1043
- cleared: 0,
1044
- count,
1045
- pageUrl,
1046
- notes: result.notes || [],
1047
- rolledBackFiles: rollback.rolledBackFiles,
1048
- rollbackFailures: rollback.rollbackFailures,
1049
- ...countByPage(cwd),
1050
- };
1055
+ details: { unreportedFiles, notes: result.notes || [] },
1056
+ });
1051
1057
  }
1052
1058
 
1053
1059
  if (result.status === 'done' && reportedAppliedIds.length === 0) {
1054
- const rollback = rollbackChangedFiles(cwd, rollbackSnapshot, result.files || [], rollbackScope);
1055
- return {
1056
- applied: [],
1060
+ return failWithRollback({
1061
+ scope: rollbackScope,
1062
+ extraFiles: result.files || [],
1057
1063
  failed: verificationFailuresForEntries(batch, batch.entries, 'missing_applied_entry_ids'),
1058
1064
  files: result.files || [],
1059
- cleared: 0,
1060
- count,
1061
- pageUrl,
1062
- notes: result.notes || [],
1063
- rolledBackFiles: rollback.rolledBackFiles,
1064
- rollbackFailures: rollback.rollbackFailures,
1065
- ...countByPage(cwd),
1066
- };
1065
+ details: { notes: result.notes || [] },
1066
+ });
1067
1067
  }
1068
1068
 
1069
1069
  const reportedAppliedEntries = batch.entries.filter((entry) => reportedAppliedIds.includes(entry.id));
1070
1070
  if (reportedAppliedIds.length > 0 && reportedFiles.length === 0) {
1071
1071
  return repairPostApplyValidation({
1072
- batch,
1073
- cwd,
1074
- pageUrl,
1075
- count,
1076
- provider,
1077
- env,
1078
- timeoutMs,
1079
- applyBatchToSource,
1080
- chatAvailable,
1081
- transactionId,
1072
+ ...repairContext,
1082
1073
  appliedEntryIds: reportedAppliedIds,
1083
1074
  files: result.files || [],
1084
1075
  failed: aiFailed,
@@ -1089,21 +1080,10 @@ export async function commitManualEdits({
1089
1080
  });
1090
1081
  }
1091
1082
 
1092
- const verifiedAppliedIds = [];
1093
- const verificationFailed = [];
1094
- for (const entry of reportedAppliedEntries) {
1095
- const failures = verifyAppliedEntry({ batch, entry, reportedFiles, cwd });
1096
- if (failures.length === 0) {
1097
- verifiedAppliedIds.push(entry.id);
1098
- } else {
1099
- verificationFailed.push({
1100
- id: entry.id,
1101
- reason: 'source_verification_failed',
1102
- failures,
1103
- candidates: candidatesForEntry(batch, entry.id),
1104
- });
1105
- }
1106
- }
1083
+ const {
1084
+ verifiedIds: verifiedAppliedIds,
1085
+ failed: verificationFailed,
1086
+ } = verifyEntriesAfterRepair({ batch, appliedEntryIds: reportedAppliedIds, files: reportedFiles, cwd });
1107
1087
  const unreportedEntries = result.status === 'done' || result.status === 'partial'
1108
1088
  ? batch.entries.filter((entry) => !reportedAppliedIds.includes(entry.id) && !aiFailed.some((item) => item.id === entry.id))
1109
1089
  : [];
@@ -1133,37 +1113,22 @@ export async function commitManualEdits({
1133
1113
  reason: 'rolled_back_due_to_failed_entry_source_changed',
1134
1114
  candidates: candidatesForEntry(batch, entry.id),
1135
1115
  }));
1136
- const rollback = rollbackChangedFiles(cwd, rollbackSnapshot, result.files || [], rollbackScope);
1137
- return {
1138
- applied: [],
1116
+ return failWithRollback({
1117
+ scope: rollbackScope,
1118
+ extraFiles: result.files || [],
1139
1119
  failed: [
1140
1120
  ...leakedUnapplied,
1141
1121
  ...failed.filter((item) => !leakedIds.has(item.id)),
1142
1122
  ...rolledBackVerified,
1143
1123
  ],
1144
1124
  files: result.files || [],
1145
- cleared: 0,
1146
- count,
1147
- pageUrl,
1148
- rolledBackFiles: rollback.rolledBackFiles,
1149
- rollbackFailures: rollback.rollbackFailures,
1150
- notes: result.notes || [],
1151
- ...countByPage(cwd),
1152
- };
1125
+ details: { notes: result.notes || [] },
1126
+ });
1153
1127
  }
1154
1128
 
1155
1129
  if (verificationFailed.length > 0) {
1156
1130
  return repairPostApplyValidation({
1157
- batch,
1158
- cwd,
1159
- pageUrl,
1160
- count,
1161
- provider,
1162
- env,
1163
- timeoutMs,
1164
- applyBatchToSource,
1165
- chatAvailable,
1166
- transactionId,
1131
+ ...repairContext,
1167
1132
  appliedEntryIds: reportedAppliedIds,
1168
1133
  files: result.files || [],
1169
1134
  failed: nonRepairFailed,
@@ -1180,16 +1145,7 @@ export async function commitManualEdits({
1180
1145
  ? reportedAppliedEntries.filter((entry) => verifiedAppliedIds.includes(entry.id))
1181
1146
  : batch.entries;
1182
1147
  return repairPostApplyValidation({
1183
- batch,
1184
- cwd,
1185
- pageUrl,
1186
- count,
1187
- provider,
1188
- env,
1189
- timeoutMs,
1190
- applyBatchToSource,
1191
- chatAvailable,
1192
- transactionId,
1148
+ ...repairContext,
1193
1149
  appliedEntryIds: verifiedAppliedIds.length > 0
1194
1150
  ? verifiedAppliedIds
1195
1151
  : postCheckEntries.map((entry) => entry.id).filter(Boolean),
@@ -14,10 +14,12 @@ import path from 'node:path';
14
14
  import { createRequire } from 'node:module';
15
15
 
16
16
  const DEFAULT_TIMEOUT_MS = 60_000;
17
+ const BATCH_OP_TEXT_LIMIT = 240;
17
18
  const require = createRequire(import.meta.url);
18
19
 
19
20
  export function buildCopyEditBatchPrompt(batch, { cwd = process.cwd() } = {}) {
20
- const repairLines = batch?.repair ? [
21
+ const compactBatch = compactBatchForPrompt(batch);
22
+ const repairLines = compactBatch.repair ? [
21
23
  '',
22
24
  'Repair mode:',
23
25
  '- The previous Apply attempt changed source, but validation failed.',
@@ -28,7 +30,7 @@ export function buildCopyEditBatchPrompt(batch, { cwd = process.cwd() } = {}) {
28
30
  '- If failures or candidates show edited text is also a lookup key, update coupled count, animation, icon, image, asset, style, or metadata keys in the current source, or fail that entry without partial edits.',
29
31
  '- Keep failed and notes as arrays.',
30
32
  '- Return the same canonical JSON shape after repair.',
31
- JSON.stringify(batch.repair, null, 2),
33
+ JSON.stringify(compactBatch.repair, null, 2),
32
34
  ] : [];
33
35
  return [
34
36
  'You are the Impeccable staged copy-edit batch applier.',
@@ -80,7 +82,7 @@ export function buildCopyEditBatchPrompt(batch, { cwd = process.cwd() } = {}) {
80
82
  ...repairLines,
81
83
  '',
82
84
  'Staged copy-edit batch:',
83
- JSON.stringify(compactBatchForPrompt(batch), null, 2),
85
+ JSON.stringify(compactBatch, null, 2),
84
86
  ].join('\n');
85
87
  }
86
88
 
@@ -292,7 +294,7 @@ function readManualEditValidationScript(cwd) {
292
294
  function compactBatchForPrompt(batch) {
293
295
  return {
294
296
  pageUrl: batch?.pageUrl || null,
295
- repair: batch?.repair || undefined,
297
+ repair: compactBatchRepair(batch?.repair),
296
298
  entries: (batch?.entries || []).map((entry) => ({
297
299
  id: entry.id,
298
300
  pageUrl: entry.pageUrl,
@@ -300,7 +302,71 @@ function compactBatchForPrompt(batch) {
300
302
  element: compactContextForBatch(entry.element),
301
303
  ops: (entry.ops || []).map(compactBatchOp),
302
304
  })),
303
- candidates: batch?.candidates || [],
305
+ candidates: compactBatchCandidates(batch?.candidates),
306
+ };
307
+ }
308
+
309
+ function compactBatchRepair(repair) {
310
+ if (!repair || typeof repair !== 'object') return undefined;
311
+ return {
312
+ status: compactBatchString(repair.status),
313
+ attempt: normalizeOptionalBatchNumber(repair.attempt),
314
+ attempts: normalizeOptionalBatchNumber(repair.attempts),
315
+ maxAttempts: normalizeOptionalBatchNumber(repair.maxAttempts),
316
+ reason: compactBatchString(repair.reason),
317
+ transactionId: compactBatchString(repair.transactionId),
318
+ pageUrl: compactBatchString(repair.pageUrl),
319
+ failures: compactBatchDiagnostics(repair.failures),
320
+ files: compactBatchStringList(repair.files, 20),
321
+ };
322
+ }
323
+
324
+ function compactBatchDiagnostics(items, depth = 0) {
325
+ if (!Array.isArray(items)) return undefined;
326
+ return items.slice(0, 12).map((item) => ({
327
+ entryId: compactBatchString(item?.entryId || item?.id),
328
+ reason: compactBatchString(item?.reason || item?.kind),
329
+ detail: compactBatchString(item?.detail),
330
+ message: compactBatchString(item?.message),
331
+ file: compactBatchString(item?.file || item?.relativeFile),
332
+ line: normalizeOptionalBatchNumber(item?.line),
333
+ ref: compactBatchString(item?.ref),
334
+ marker: compactBatchString(item?.marker),
335
+ files: compactBatchStringList(item?.files, 8),
336
+ candidates: depth < 2 ? compactBatchSourceMatches(item?.candidates, 8) : undefined,
337
+ failures: depth < 2 ? compactBatchDiagnostics(item?.failures, depth + 1) : undefined,
338
+ checks: depth < 2 ? compactBatchDiagnostics(item?.checks, depth + 1) : undefined,
339
+ }));
340
+ }
341
+
342
+ function compactBatchCandidates(candidates) {
343
+ return (Array.isArray(candidates) ? candidates : [])
344
+ .slice(0, 24)
345
+ .map((candidate) => ({
346
+ entryId: compactBatchString(candidate?.entryId),
347
+ ref: compactBatchString(candidate?.ref),
348
+ sourceHint: compactBatchSourceMatch(candidate?.sourceHint),
349
+ textMatches: compactBatchSourceMatches(candidate?.textMatches, 8),
350
+ objectKeyMatches: compactBatchSourceMatches(candidate?.objectKeyMatches, 8),
351
+ contextTextMatches: compactBatchSourceMatches(candidate?.contextTextMatches, 8),
352
+ locatorMatches: compactBatchSourceMatches(candidate?.locatorMatches, 6),
353
+ }));
354
+ }
355
+
356
+ function compactBatchSourceMatches(matches, limit) {
357
+ if (!Array.isArray(matches)) return undefined;
358
+ return matches.slice(0, limit).map(compactBatchSourceMatch).filter(Boolean);
359
+ }
360
+
361
+ function compactBatchSourceMatch(match) {
362
+ if (!match || typeof match !== 'object') return null;
363
+ return {
364
+ file: compactBatchString(match.relativeFile || match.file),
365
+ line: normalizeBatchNumber(match.line),
366
+ column: normalizeBatchNumber(match.column),
367
+ kind: compactBatchString(match.kind),
368
+ reason: compactBatchString(match.reason || match.kind),
369
+ status: compactBatchString(match.status),
304
370
  };
305
371
  }
306
372
 
@@ -311,25 +377,77 @@ function compactBatchOp(op) {
311
377
  contextRef: op.contextRef,
312
378
  tag: op.tag,
313
379
  elementId: op.elementId,
314
- classes: op.classes,
380
+ classes: compactBatchStringList(op.classes, 24),
315
381
  originalText: op.originalText,
316
382
  newText: op.newText,
317
383
  deleted: op.deleted === true || undefined,
318
- sourceHint: op.sourceHint,
384
+ sourceHint: normalizeBatchSourceHint(op.sourceHint),
319
385
  leaf: compactContextForBatch(op.leaf),
320
- nearbyEditableTexts: Array.isArray(op.nearbyEditableTexts) ? op.nearbyEditableTexts.slice(0, 8) : [],
386
+ nearbyEditableTexts: compactNearbyBatchTexts(op.nearbyEditableTexts),
321
387
  container: compactContextForBatch(op.container),
322
- contextHints: Array.isArray(op.contextHints) ? op.contextHints.slice(0, 12) : [],
388
+ contextHints: compactBatchStringList(op.contextHints, 12),
323
389
  };
324
390
  }
325
391
 
392
+ function normalizeBatchSourceHint(hint) {
393
+ if (!hint || typeof hint !== 'object') return null;
394
+ let line = normalizeBatchNumber(hint.line);
395
+ let column = normalizeBatchNumber(hint.column);
396
+ if ((line === null || column === null) && typeof hint.loc === 'string') {
397
+ const match = hint.loc.match(/^(\d+)(?::(\d+))?/);
398
+ if (match) {
399
+ line = Number(match[1]);
400
+ if (match[2]) column = Number(match[2]);
401
+ }
402
+ }
403
+ return {
404
+ file: compactBatchString(hint.file) || '',
405
+ loc: compactBatchString(hint.loc) || '',
406
+ line,
407
+ column,
408
+ };
409
+ }
410
+
411
+ function normalizeBatchNumber(value) {
412
+ if (value === null || value === undefined || value === '') return null;
413
+ const number = Number(value);
414
+ return Number.isFinite(number) ? number : null;
415
+ }
416
+
417
+ function normalizeOptionalBatchNumber(value) {
418
+ const number = normalizeBatchNumber(value);
419
+ return number === null ? undefined : number;
420
+ }
421
+
422
+ function compactNearbyBatchTexts(items) {
423
+ return (Array.isArray(items) ? items : [])
424
+ .slice(0, 8)
425
+ .map((item) => typeof item === 'string' ? { text: truncate(item, BATCH_OP_TEXT_LIMIT) } : {
426
+ ref: compactBatchString(item?.ref),
427
+ tag: compactBatchString(item?.tag),
428
+ classes: compactBatchStringList(item?.classes, 24),
429
+ text: compactBatchString(item?.text),
430
+ });
431
+ }
432
+
433
+ function compactBatchStringList(items, limit) {
434
+ return (Array.isArray(items) ? items : [])
435
+ .slice(0, limit)
436
+ .filter((item) => typeof item === 'string')
437
+ .map((item) => truncate(item, BATCH_OP_TEXT_LIMIT));
438
+ }
439
+
440
+ function compactBatchString(value) {
441
+ return typeof value === 'string' ? truncate(value, BATCH_OP_TEXT_LIMIT) : undefined;
442
+ }
443
+
326
444
  function compactContextForBatch(value) {
327
445
  if (!value || typeof value !== 'object') return value || null;
328
446
  return {
329
- ref: value.ref,
330
- tagName: value.tagName,
331
- id: value.id,
332
- classes: value.classes,
447
+ ref: compactBatchString(value.ref),
448
+ tagName: compactBatchString(value.tagName),
449
+ id: compactBatchString(value.id),
450
+ classes: compactBatchStringList(value.classes, 24),
333
451
  textContent: truncate(value.textContent, 900),
334
452
  outerHTML: truncate(stripLiveRuntimeHtml(value.outerHTML), 1800),
335
453
  };
@@ -470,12 +588,11 @@ function runClaude(prompt, { cwd, env, resultPath, logPath, timeoutMs = DEFAULT_
470
588
  if (env.IMPECCABLE_LIVE_COPY_AGENT_MODEL) {
471
589
  args.push('--model', env.IMPECCABLE_LIVE_COPY_AGENT_MODEL);
472
590
  }
473
- args.push(prompt);
474
591
  // Forward env as-is so CLAUDE_CODE_OAUTH_TOKEN and ANTHROPIC_API_KEY flow
475
592
  // through. On macOS, `claude /login` stores creds in the Keychain, which a
476
593
  // non-TTY subprocess cannot read; setting CLAUDE_CODE_OAUTH_TOKEN (via
477
594
  // `claude setup-token`) is the supported headless auth path.
478
- return runAgentProcess('claude', args, '', { cwd, env, logPath, timeoutMs, mirrorOutputPath: resultPath });
595
+ return runAgentProcess('claude', args, prompt, { cwd, env, logPath, timeoutMs, mirrorOutputPath: resultPath });
479
596
  }
480
597
 
481
598
  function runAgentProcess(command, args, stdin, { cwd, env, logPath, timeoutMs, mirrorOutputPath }) {
@@ -27,6 +27,7 @@ import fs from 'node:fs';
27
27
  import path from 'node:path';
28
28
  import { fileURLToPath } from 'node:url';
29
29
  import { resolveLiveConfigPath } from './lib/impeccable-paths.mjs';
30
+ import { livePathGlobToRegex } from './lib/live-path-globs.mjs';
30
31
  import {
31
32
  describeInjectArtifacts,
32
33
  frameworkIgnorePatterns,
@@ -364,7 +365,7 @@ export function resolveFiles(rootDir, config) {
364
365
  const patterns = config.files;
365
366
  const userExcludes = Array.isArray(config.exclude) ? config.exclude : [];
366
367
  const allExcludes = [...HARD_EXCLUDES, ...userExcludes];
367
- const excludeRegexes = allExcludes.map(globToRegex);
368
+ const excludeRegexes = allExcludes.map(livePathGlobToRegex);
368
369
 
369
370
  const isExcluded = (relPath) => excludeRegexes.some((re) => re.test(relPath));
370
371
  const isGlob = (s) => /[*?[]/.test(s);
@@ -401,47 +402,6 @@ export function resolveFiles(rootDir, config) {
401
402
  return out;
402
403
  }
403
404
 
404
- /**
405
- * Convert a glob pattern to a RegExp. Supports:
406
- * ** → any number of path segments (including zero)
407
- * * → any chars except `/`
408
- * ? → any single char except `/`
409
- * Paths are normalized to forward slashes before matching.
410
- */
411
- function globToRegex(pattern) {
412
- let re = '';
413
- let i = 0;
414
- while (i < pattern.length) {
415
- const c = pattern[i];
416
- if (c === '*') {
417
- if (pattern[i + 1] === '*') {
418
- // ** — any number of segments, including zero. Handle the common
419
- // **/ and /** forms so `a/**/b` matches `a/b` as well as `a/x/y/b`.
420
- if (pattern[i + 2] === '/') {
421
- re += '(?:.*/)?';
422
- i += 3;
423
- } else {
424
- re += '.*';
425
- i += 2;
426
- }
427
- } else {
428
- re += '[^/]*';
429
- i += 1;
430
- }
431
- } else if (c === '?') {
432
- re += '[^/]';
433
- i += 1;
434
- } else if (/[.+^${}()|[\]\\]/.test(c)) {
435
- re += '\\' + c;
436
- i += 1;
437
- } else {
438
- re += c;
439
- i += 1;
440
- }
441
- }
442
- return new RegExp('^' + re + '$');
443
- }
444
-
445
405
  // ---------------------------------------------------------------------------
446
406
  // Core operations
447
407
  // ---------------------------------------------------------------------------
@@ -238,10 +238,9 @@ export async function completeAcceptHandling(event, base, token) {
238
238
  });
239
239
  } catch (err) {
240
240
  event._completionAck = { ok: false, error: err.message };
241
+ return event;
241
242
  }
242
- if (!event._completionAck) {
243
- event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult);
244
- }
243
+ event._completionAck = completionAckForAcceptResult(event.id, completionType, event._acceptResult);
245
244
  return event;
246
245
  }
247
246
 
@@ -269,9 +268,11 @@ export function printPollEvent(event) {
269
268
  // Situational plumbing rides with the event itself: `_instructions` is the
270
269
  // authoritative next step, with real ids and paths substituted, so the
271
270
  // reference doc can stay lean and can never drift from script behavior.
272
- if (event && typeof event === 'object' && !event._instructions) {
271
+ // A wire-supplied value must never win over the locally generated one.
272
+ if (event && typeof event === 'object') {
273
273
  const instructions = instructionsForEvent(event, { scriptsPath: SELF_DIR });
274
274
  if (instructions) event._instructions = instructions;
275
+ else delete event._instructions;
275
276
  }
276
277
  console.log(JSON.stringify(event));
277
278
  }