@hawkeyexl/manni 4.3.1 → 4.4.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 (225) hide show
  1. package/README.md +9 -1
  2. package/dist/batch-GFNLTGOH.js +36 -0
  3. package/dist/build-COYIQ25L.js +31 -0
  4. package/dist/calibrate-MDBLJKEE.js +31 -0
  5. package/dist/capture-LRML2PKV.js +32 -0
  6. package/dist/check-ASPIHA6B.js +40 -0
  7. package/dist/check-ASPIHA6B.js.map +1 -0
  8. package/dist/check-EDLBYL5N.js +27 -0
  9. package/dist/check-EDLBYL5N.js.map +1 -0
  10. package/dist/check-HYNGXYJV.js +18 -0
  11. package/dist/check-HYNGXYJV.js.map +1 -0
  12. package/dist/chunk-2B7ZNTPV.js +230 -0
  13. package/dist/chunk-2B7ZNTPV.js.map +1 -0
  14. package/dist/chunk-2H7UOFLK.js +11 -0
  15. package/dist/chunk-2H7UOFLK.js.map +1 -0
  16. package/dist/chunk-37LP5HMZ.js +1042 -0
  17. package/dist/chunk-37LP5HMZ.js.map +1 -0
  18. package/dist/chunk-3VK37OR2.js +2957 -0
  19. package/dist/chunk-3VK37OR2.js.map +1 -0
  20. package/dist/chunk-4IUC2MNL.js +2165 -0
  21. package/dist/chunk-4IUC2MNL.js.map +1 -0
  22. package/dist/{chunk-3R23C5DT.js → chunk-4TSWU2SU.js} +2 -14
  23. package/dist/chunk-4TSWU2SU.js.map +1 -0
  24. package/dist/chunk-57NEQRVO.js +10921 -0
  25. package/dist/chunk-57NEQRVO.js.map +1 -0
  26. package/dist/chunk-6JSSEWBG.js +6737 -0
  27. package/dist/chunk-6JSSEWBG.js.map +1 -0
  28. package/dist/chunk-6OYEI2VV.js +88 -0
  29. package/dist/chunk-6OYEI2VV.js.map +1 -0
  30. package/dist/chunk-77AINBYM.js +19 -0
  31. package/dist/chunk-77AINBYM.js.map +1 -0
  32. package/dist/chunk-7UKO3ASZ.js +54 -0
  33. package/dist/chunk-7UKO3ASZ.js.map +1 -0
  34. package/dist/chunk-A5ULMVHQ.js +164 -0
  35. package/dist/chunk-A5ULMVHQ.js.map +1 -0
  36. package/dist/chunk-C4SQM22E.js +299 -0
  37. package/dist/chunk-C4SQM22E.js.map +1 -0
  38. package/dist/chunk-CK7SKVHR.js +1042 -0
  39. package/dist/chunk-CK7SKVHR.js.map +1 -0
  40. package/dist/{chunk-G5CM2VGQ.js → chunk-DXNZS72W.js} +8 -94
  41. package/dist/chunk-DXNZS72W.js.map +1 -0
  42. package/dist/chunk-EPH54CAY.js +374 -0
  43. package/dist/chunk-EPH54CAY.js.map +1 -0
  44. package/dist/{chunk-KJR7OVDX.js → chunk-FBEQCK47.js} +56 -137
  45. package/dist/chunk-FBEQCK47.js.map +1 -0
  46. package/dist/{chunk-IOYXPBCL.js → chunk-G3T47CFV.js} +1 -7
  47. package/dist/{chunk-IOYXPBCL.js.map → chunk-G3T47CFV.js.map} +1 -1
  48. package/dist/chunk-GCQRPUSB.js +1287 -0
  49. package/dist/chunk-GCQRPUSB.js.map +1 -0
  50. package/dist/chunk-GLP5PSXB.js +72 -0
  51. package/dist/chunk-GLP5PSXB.js.map +1 -0
  52. package/dist/chunk-I2AH3CJJ.js +1192 -0
  53. package/dist/chunk-I2AH3CJJ.js.map +1 -0
  54. package/dist/chunk-IDV6HJCB.js +388 -0
  55. package/dist/chunk-IDV6HJCB.js.map +1 -0
  56. package/dist/chunk-IKEWBMCN.js +98 -0
  57. package/dist/chunk-IKEWBMCN.js.map +1 -0
  58. package/dist/chunk-JCDXE76J.js +52 -0
  59. package/dist/chunk-JCDXE76J.js.map +1 -0
  60. package/dist/chunk-JXLTXMJ7.js +455 -0
  61. package/dist/chunk-JXLTXMJ7.js.map +1 -0
  62. package/dist/chunk-LJ2RDH3F.js +49 -0
  63. package/dist/chunk-LJ2RDH3F.js.map +1 -0
  64. package/dist/chunk-M2JCK5F6.js +805 -0
  65. package/dist/chunk-M2JCK5F6.js.map +1 -0
  66. package/dist/chunk-M3W7J6MU.js +4398 -0
  67. package/dist/chunk-M3W7J6MU.js.map +1 -0
  68. package/dist/chunk-MMTSSTG7.js +23 -0
  69. package/dist/chunk-MMTSSTG7.js.map +1 -0
  70. package/dist/chunk-MNH7QO4Z.js +15 -0
  71. package/dist/chunk-MNH7QO4Z.js.map +1 -0
  72. package/dist/chunk-MWJLZMBL.js +934 -0
  73. package/dist/chunk-MWJLZMBL.js.map +1 -0
  74. package/dist/chunk-MYMBCTDE.js +387 -0
  75. package/dist/chunk-MYMBCTDE.js.map +1 -0
  76. package/dist/chunk-NCPWXHDO.js +683 -0
  77. package/dist/chunk-NCPWXHDO.js.map +1 -0
  78. package/dist/chunk-NLXTXE5Y.js +1661 -0
  79. package/dist/chunk-NLXTXE5Y.js.map +1 -0
  80. package/dist/{chunk-CFDUZRM2.js → chunk-NXV7WE2D.js} +32 -240
  81. package/dist/chunk-NXV7WE2D.js.map +1 -0
  82. package/dist/chunk-OWBRDNRZ.js +16 -0
  83. package/dist/chunk-OWBRDNRZ.js.map +1 -0
  84. package/dist/{chunk-KTSJ2ZHA.js → chunk-PAU352SV.js} +5 -3
  85. package/dist/{chunk-KTSJ2ZHA.js.map → chunk-PAU352SV.js.map} +1 -1
  86. package/dist/chunk-PCE7QHXJ.js +68 -0
  87. package/dist/chunk-PCE7QHXJ.js.map +1 -0
  88. package/dist/chunk-PIKOVN6W.js +1651 -0
  89. package/dist/chunk-PIKOVN6W.js.map +1 -0
  90. package/dist/chunk-PNPQTHBT.js +17 -0
  91. package/dist/chunk-PNPQTHBT.js.map +1 -0
  92. package/dist/chunk-PU6DFJMY.js +1975 -0
  93. package/dist/chunk-PU6DFJMY.js.map +1 -0
  94. package/dist/chunk-Q35YIDBV.js +1202 -0
  95. package/dist/chunk-Q35YIDBV.js.map +1 -0
  96. package/dist/{chunk-5HNHEUD5.js → chunk-QQUKGCAJ.js} +3 -3
  97. package/dist/chunk-QRWGPQKO.js +198 -0
  98. package/dist/chunk-QRWGPQKO.js.map +1 -0
  99. package/dist/{chunk-UBMMXS2Y.js → chunk-QZ6LRSNV.js} +69 -48
  100. package/dist/{chunk-UBMMXS2Y.js.map → chunk-QZ6LRSNV.js.map} +1 -1
  101. package/dist/chunk-RL5XWQOL.js +3872 -0
  102. package/dist/chunk-RL5XWQOL.js.map +1 -0
  103. package/dist/chunk-RNVYGWIC.js +30 -0
  104. package/dist/chunk-RNVYGWIC.js.map +1 -0
  105. package/dist/chunk-SE4JN5XN.js +19 -0
  106. package/dist/chunk-SE4JN5XN.js.map +1 -0
  107. package/dist/chunk-SKSDRUUK.js +622 -0
  108. package/dist/chunk-SKSDRUUK.js.map +1 -0
  109. package/dist/chunk-TIUTIOFB.js +25 -0
  110. package/dist/chunk-TIUTIOFB.js.map +1 -0
  111. package/dist/chunk-TK3FC4RF.js +3090 -0
  112. package/dist/chunk-TK3FC4RF.js.map +1 -0
  113. package/dist/chunk-TU2WUBVZ.js +2 -0
  114. package/dist/chunk-TU2WUBVZ.js.map +1 -0
  115. package/dist/chunk-TZOYZK2E.js +98 -0
  116. package/dist/chunk-TZOYZK2E.js.map +1 -0
  117. package/dist/chunk-UJCVAWP7.js +178 -0
  118. package/dist/chunk-UJCVAWP7.js.map +1 -0
  119. package/dist/chunk-UKZNLVJW.js +17 -0
  120. package/dist/chunk-UKZNLVJW.js.map +1 -0
  121. package/dist/{chunk-HAPTXWU2.js → chunk-UQISLDAV.js} +4 -4
  122. package/dist/chunk-WB3QM6PP.js +5440 -0
  123. package/dist/chunk-WB3QM6PP.js.map +1 -0
  124. package/dist/chunk-YB4IBW3M.js +96 -0
  125. package/dist/chunk-YB4IBW3M.js.map +1 -0
  126. package/dist/chunk-YEUYKR62.js +72 -0
  127. package/dist/chunk-YEUYKR62.js.map +1 -0
  128. package/dist/chunk-YJOGUXGP.js +634 -0
  129. package/dist/chunk-YJOGUXGP.js.map +1 -0
  130. package/dist/chunk-YNHZHSSM.js +41 -0
  131. package/dist/chunk-YNHZHSSM.js.map +1 -0
  132. package/dist/chunk-YTURO7SI.js +979 -0
  133. package/dist/chunk-YTURO7SI.js.map +1 -0
  134. package/dist/chunk-ZXRAE55H.js +31 -0
  135. package/dist/chunk-ZXRAE55H.js.map +1 -0
  136. package/dist/cli-3BIBYHFK.js +473 -0
  137. package/dist/cli-3BIBYHFK.js.map +1 -0
  138. package/dist/cli-673OTRGE.js +28 -0
  139. package/dist/cli-673OTRGE.js.map +1 -0
  140. package/dist/cli-6HIDIFVT.js +1203 -0
  141. package/dist/cli-6HIDIFVT.js.map +1 -0
  142. package/dist/cli-6UB5N6CU.js +232 -0
  143. package/dist/cli-6UB5N6CU.js.map +1 -0
  144. package/dist/cli-LFP5ZGYS.js +346 -0
  145. package/dist/cli-LFP5ZGYS.js.map +1 -0
  146. package/dist/cli-MN7PYMBI.js +552 -0
  147. package/dist/cli-MN7PYMBI.js.map +1 -0
  148. package/dist/cli-PYPX6O6E.js +739 -0
  149. package/dist/cli-PYPX6O6E.js.map +1 -0
  150. package/dist/cli-RPM5PELX.js +626 -0
  151. package/dist/cli-RPM5PELX.js.map +1 -0
  152. package/dist/cli-VI7VHWNZ.js +423 -0
  153. package/dist/cli-VI7VHWNZ.js.map +1 -0
  154. package/dist/cli.d.ts +12 -2
  155. package/dist/cli.js +109 -7661
  156. package/dist/cli.js.map +1 -1
  157. package/dist/docmeta.js +15 -3
  158. package/dist/docmeta.js.map +1 -1
  159. package/dist/github-6QMYOHC3.js +27 -0
  160. package/dist/github-6QMYOHC3.js.map +1 -0
  161. package/dist/github-SXPGP5DB.js +32 -0
  162. package/dist/github-SXPGP5DB.js.map +1 -0
  163. package/dist/index.d.ts +65 -3
  164. package/dist/index.js +407 -337
  165. package/dist/index.js.map +1 -1
  166. package/dist/json-7WV7KL6H.js +17 -0
  167. package/dist/json-7WV7KL6H.js.map +1 -0
  168. package/dist/json-SY7KLNC7.js +35 -0
  169. package/dist/json-SY7KLNC7.js.map +1 -0
  170. package/dist/lint-ASMJLLPA.js +28 -0
  171. package/dist/lint-ASMJLLPA.js.map +1 -0
  172. package/dist/list-KZNVTNVC.js +31 -0
  173. package/dist/list-KZNVTNVC.js.map +1 -0
  174. package/dist/list-Q57YTFUQ.js +29 -0
  175. package/dist/list-Q57YTFUQ.js.map +1 -0
  176. package/dist/page-KSGV5RD4.js +41 -0
  177. package/dist/page-KSGV5RD4.js.map +1 -0
  178. package/dist/picker-3XF36MJF.js +14 -0
  179. package/dist/picker-3XF36MJF.js.map +1 -0
  180. package/dist/pretty-A5EGTN35.js +37 -0
  181. package/dist/pretty-A5EGTN35.js.map +1 -0
  182. package/dist/pretty-CXSDXNAS.js +43 -0
  183. package/dist/pretty-CXSDXNAS.js.map +1 -0
  184. package/dist/reporters-HUY7UXFS.js +42 -0
  185. package/dist/reporters-HUY7UXFS.js.map +1 -0
  186. package/dist/reporters-K2VUKSAK.js +71 -0
  187. package/dist/reporters-K2VUKSAK.js.map +1 -0
  188. package/dist/reporters-M6LJ2IYU.js +21 -0
  189. package/dist/reporters-M6LJ2IYU.js.map +1 -0
  190. package/dist/run-3DYJNRIG.js +33 -0
  191. package/dist/run-3DYJNRIG.js.map +1 -0
  192. package/dist/term-BIQUPMOR.js +1592 -0
  193. package/dist/term-BIQUPMOR.js.map +1 -0
  194. package/dist/types-Y6TXHM7M.js +10 -0
  195. package/dist/types-Y6TXHM7M.js.map +1 -0
  196. package/dist/validate-53RKM2XG.js +16 -0
  197. package/dist/validate-53RKM2XG.js.map +1 -0
  198. package/package.json +1 -1
  199. package/plugin/manni/.claude-plugin/plugin.json +9 -0
  200. package/plugin/manni/hooks/hooks.json +69 -0
  201. package/plugin/manni/hooks/manni.mjs +157 -0
  202. package/plugin/manni/skills/check/SKILL.md +20 -0
  203. package/plugin/manni/skills/evals/SKILL.md +27 -0
  204. package/plugin/manni/skills/fix/SKILL.md +56 -0
  205. package/plugin/manni/skills/setup/SKILL.md +41 -0
  206. package/dist/batch-A5HZKYIW.js +0 -18
  207. package/dist/calibrate-S5K6QHUY.js +0 -13
  208. package/dist/capture-OZEZDABQ.js +0 -15
  209. package/dist/chunk-3R23C5DT.js.map +0 -1
  210. package/dist/chunk-CFDUZRM2.js.map +0 -1
  211. package/dist/chunk-G5CM2VGQ.js.map +0 -1
  212. package/dist/chunk-KJR7OVDX.js.map +0 -1
  213. package/dist/chunk-V4KUVE47.js +0 -27170
  214. package/dist/chunk-V4KUVE47.js.map +0 -1
  215. package/dist/chunk-ZQBTK3DO.js +0 -26518
  216. package/dist/chunk-ZQBTK3DO.js.map +0 -1
  217. package/dist/picker-6HRMETE7.js +0 -12
  218. package/plugin/tracevals/.claude-plugin/plugin.json +0 -9
  219. package/plugin/tracevals/hooks/hooks.json +0 -17
  220. /package/dist/{batch-A5HZKYIW.js.map → batch-GFNLTGOH.js.map} +0 -0
  221. /package/dist/{calibrate-S5K6QHUY.js.map → build-COYIQ25L.js.map} +0 -0
  222. /package/dist/{capture-OZEZDABQ.js.map → calibrate-MDBLJKEE.js.map} +0 -0
  223. /package/dist/{picker-6HRMETE7.js.map → capture-LRML2PKV.js.map} +0 -0
  224. /package/dist/{chunk-5HNHEUD5.js.map → chunk-QQUKGCAJ.js.map} +0 -0
  225. /package/dist/{chunk-HAPTXWU2.js.map → chunk-UQISLDAV.js.map} +0 -0
@@ -0,0 +1,1192 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ discoverArtifacts
4
+ } from "./chunk-QQUKGCAJ.js";
5
+ import {
6
+ METADATA_KEY,
7
+ assertProviderSelection,
8
+ constructProvider,
9
+ graderFor,
10
+ keepNewerThan,
11
+ loadExternalEvals,
12
+ loadGraderPlugins,
13
+ parseNewerThan,
14
+ resolveProviderIdentity,
15
+ selectProvider,
16
+ urlManifestRefusal
17
+ } from "./chunk-FBEQCK47.js";
18
+ import {
19
+ discoverConfig
20
+ } from "./chunk-NXV7WE2D.js";
21
+ import {
22
+ discoverTraces
23
+ } from "./chunk-4TSWU2SU.js";
24
+ import {
25
+ TracevalsError
26
+ } from "./chunk-UKZNLVJW.js";
27
+ import {
28
+ TURN_BUDGET_SKIP
29
+ } from "./chunk-LJ2RDH3F.js";
30
+ import {
31
+ SEVERITIES
32
+ } from "./chunk-77AINBYM.js";
33
+ import {
34
+ externalWriteWarnings,
35
+ mergeMetaProvenance,
36
+ offerExternalHomes
37
+ } from "./chunk-A5ULMVHQ.js";
38
+ import {
39
+ keyHome,
40
+ marksValidator,
41
+ metaSection,
42
+ readManifestOrEmpty,
43
+ readManifestValue,
44
+ spliceManifestValue
45
+ } from "./chunk-M3W7J6MU.js";
46
+ import {
47
+ DocmetaError,
48
+ extractFrontmatter,
49
+ writeFileAtomic
50
+ } from "./chunk-57NEQRVO.js";
51
+ import {
52
+ palette
53
+ } from "./chunk-RNVYGWIC.js";
54
+ import {
55
+ errorMessage
56
+ } from "./chunk-G3T47CFV.js";
57
+
58
+ // src/tracevals/commands/list.ts
59
+ async function runList(options = {}) {
60
+ if (options.newerThan !== void 0) parseNewerThan(options.newerThan);
61
+ const { newerThan, ...discover } = options;
62
+ return { traces: keepNewerThan(await discoverTraces(discover), newerThan) };
63
+ }
64
+ function renderList(run, opts = {}) {
65
+ const pc = palette(opts.color === true);
66
+ const dim = pc.dim;
67
+ const bold = pc.bold;
68
+ if (run.traces.length === 0) {
69
+ return "No traces found. Pass --all-projects to scan every project, or --project <dir> to scope to one.";
70
+ }
71
+ const lines = [];
72
+ run.traces.forEach((t, i) => {
73
+ const when = new Date(t.mtimeMs).toISOString().slice(0, 16).replace("T", " ");
74
+ const size = `${Math.max(1, Math.round(t.sizeBytes / 1024))}KB`;
75
+ lines.push(
76
+ `${bold(String(i + 1).padStart(3))}. ${dim(when)} ${t.firstPrompt ?? dim("(no prompt)")}`
77
+ );
78
+ lines.push(
79
+ ` ${dim(`${t.project ?? "unknown project"} \xB7 ${t.sessionId ?? "?"} \xB7 ${size}`)}`
80
+ );
81
+ lines.push(` ${dim(t.file)}`);
82
+ });
83
+ return lines.join("\n");
84
+ }
85
+
86
+ // src/tracevals/commands/fill.ts
87
+ import { writeFile } from "node:fs/promises";
88
+ import { relative as relative2, resolve as resolve2 } from "node:path";
89
+
90
+ // src/tracevals/evals/write.ts
91
+ import {
92
+ Document,
93
+ isMap,
94
+ isNode,
95
+ isScalar,
96
+ isSeq,
97
+ parseDocument
98
+ } from "yaml";
99
+ var BOM = "\uFEFF";
100
+ function leadingFormat(content) {
101
+ const body = content.startsWith(BOM) ? content.slice(1) : content;
102
+ if (/^---\r?\n/.test(body)) return "yaml";
103
+ if (/^\+\+\+\r?\n/.test(body)) return "toml";
104
+ if (/^;;;\r?\n/.test(body)) return "json";
105
+ return void 0;
106
+ }
107
+ function splitYamlFrontmatter(content, path) {
108
+ const bom = content.startsWith(BOM) ? BOM : "";
109
+ const body = bom ? content.slice(1) : content;
110
+ const openMatch = /^---(\r?\n)/.exec(body);
111
+ if (!openMatch) {
112
+ throw new TracevalsError(`${path}: no YAML frontmatter block to edit`);
113
+ }
114
+ const eol = openMatch[1] === "\r\n" ? "\r\n" : "\n";
115
+ const [fence = body, ...rest] = body.split(/(?<=\n)/);
116
+ let offset = fence.length;
117
+ for (const line of rest) {
118
+ const stripped = line.replace(/\r?\n$/, "");
119
+ if (stripped === "---" || stripped === "...") {
120
+ return {
121
+ open: bom + fence,
122
+ block: body.slice(fence.length, offset),
123
+ suffix: body.slice(offset),
124
+ eol
125
+ };
126
+ }
127
+ offset += line.length;
128
+ }
129
+ throw new TracevalsError(`${path}: unterminated frontmatter block`);
130
+ }
131
+ function entryObject(entry) {
132
+ const obj = { id: entry.id };
133
+ if (entry.assertion !== void 0) obj.assertion = entry.assertion;
134
+ if (entry.type !== void 0) obj.type = entry.type;
135
+ if (entry.grader !== void 0) obj.grader = entry.grader;
136
+ if (entry.options !== void 0) obj.options = entry.options;
137
+ if (entry.severity !== void 0) obj.severity = entry.severity;
138
+ if (entry.evidence !== void 0) obj.evidence = entry.evidence;
139
+ if (entry.examples !== void 0) obj.examples = entry.examples;
140
+ return obj;
141
+ }
142
+ function existingIds(seq) {
143
+ const ids = /* @__PURE__ */ new Set();
144
+ for (const item of seq.items) {
145
+ if (isMap(item)) {
146
+ const id = item.get("id");
147
+ if (typeof id === "string") ids.add(id);
148
+ }
149
+ }
150
+ return ids;
151
+ }
152
+ function isEmptyNode(node) {
153
+ return node === void 0 || node === null || isNullScalar(node);
154
+ }
155
+ function isNullScalar(node) {
156
+ return isScalar(node) && node.value === null;
157
+ }
158
+ function evalsSeq(doc, path) {
159
+ const existing = doc.getIn(["metadata", "evals"], true);
160
+ if (!isEmptyNode(existing)) {
161
+ if (isScalar(existing) && typeof existing.value === "string") {
162
+ throw new TracevalsError(
163
+ `${path}: metadata.evals is a single assertion string; expand it to a list before appending`
164
+ );
165
+ }
166
+ if (!isSeq(existing)) {
167
+ throw new TracevalsError(`${path}: metadata.evals is not a list`);
168
+ }
169
+ return existing;
170
+ }
171
+ const node = doc.getIn(["metadata"], true);
172
+ if (isEmptyNode(node)) {
173
+ doc.setIn(["metadata"], doc.createNode({}));
174
+ } else if (!isMap(node)) {
175
+ throw new TracevalsError(`${path}: metadata is not a mapping`);
176
+ }
177
+ const seq = doc.createNode([]);
178
+ doc.setIn(["metadata", "evals"], seq);
179
+ return seq;
180
+ }
181
+ var META_PROVENANCE_KEY = "meta-provenance";
182
+ function mergeProvenance(doc, provenance, ids) {
183
+ if (ids.length === 0) return;
184
+ const node = doc.getIn(["metadata", META_PROVENANCE_KEY], true);
185
+ const held = isEmptyNode(node) ? void 0 : isNode(node) ? node.toJSON() : node;
186
+ const merged = mergeMetaProvenance(
187
+ held,
188
+ provenance.generatedBy,
189
+ "evals",
190
+ ids.map((id) => ({ name: id, confidence: provenance.confidence[id] ?? 0 }))
191
+ );
192
+ if (!merged) {
193
+ throw new TracevalsError(`metadata.${META_PROVENANCE_KEY} is not a list`);
194
+ }
195
+ doc.setIn(
196
+ ["metadata", META_PROVENANCE_KEY],
197
+ doc.createNode(merged.list)
198
+ );
199
+ }
200
+ function appendMetadataEvals(held, label, entries, provenance) {
201
+ const block = held === void 0 || held === null ? {} : typeof held === "object" && !Array.isArray(held) ? { ...held } : (() => {
202
+ throw new TracevalsError(`${label}: metadata is not a mapping`);
203
+ })();
204
+ const existing = block.evals;
205
+ if (typeof existing === "string") {
206
+ throw new TracevalsError(
207
+ `${label}: metadata.evals is a single assertion string; expand it to a list before appending`
208
+ );
209
+ }
210
+ if (existing !== void 0 && existing !== null && !Array.isArray(existing)) {
211
+ throw new TracevalsError(`${label}: metadata.evals is not a list`);
212
+ }
213
+ const list = Array.isArray(existing) ? [...existing] : [];
214
+ const taken = /* @__PURE__ */ new Set();
215
+ for (const item of list) {
216
+ if (item !== null && typeof item === "object" && !Array.isArray(item)) {
217
+ const id = item.id;
218
+ if (typeof id === "string") taken.add(id);
219
+ }
220
+ }
221
+ for (const entry of entries) {
222
+ if (taken.has(entry.id)) {
223
+ throw new TracevalsError(
224
+ `${label}: eval "${entry.id}" already exists in ${METADATA_KEY}`
225
+ );
226
+ }
227
+ taken.add(entry.id);
228
+ list.push(entryObject(entry));
229
+ }
230
+ block.evals = list;
231
+ if (provenance !== void 0 && entries.length > 0) {
232
+ const merged = mergeMetaProvenance(
233
+ block[META_PROVENANCE_KEY],
234
+ provenance.generatedBy,
235
+ "evals",
236
+ entries.map((e) => ({
237
+ name: e.id,
238
+ confidence: provenance.confidence[e.id] ?? 0
239
+ }))
240
+ );
241
+ if (!merged) {
242
+ throw new TracevalsError(`${label}: metadata.${META_PROVENANCE_KEY} is not a list`);
243
+ }
244
+ block[META_PROVENANCE_KEY] = merged.list;
245
+ }
246
+ return block;
247
+ }
248
+ function appendArtifactEvals(content, path, entries, provenance) {
249
+ if (entries.length === 0) return content;
250
+ const format = leadingFormat(content);
251
+ if (format === "toml" || format === "json") {
252
+ throw new TracevalsError(
253
+ `${path}: only YAML frontmatter can be edited (found ${format} frontmatter)`
254
+ );
255
+ }
256
+ const bom = content.startsWith(BOM) ? BOM : "";
257
+ const stripped = bom ? content.slice(1) : content;
258
+ if (format === void 0) {
259
+ const eol2 = stripped.includes("\r\n") ? "\r\n" : "\n";
260
+ const doc2 = new Document({
261
+ metadata: { evals: entries.map(entryObject) }
262
+ });
263
+ if (provenance) {
264
+ mergeProvenance(
265
+ doc2,
266
+ provenance,
267
+ entries.map((e) => e.id)
268
+ );
269
+ }
270
+ let block2 = doc2.toString({ lineWidth: 0 });
271
+ if (eol2 === "\r\n") block2 = block2.replace(/(?<!\r)\n/g, "\r\n");
272
+ return `${bom}---${eol2}${block2}---${eol2}${stripped}`;
273
+ }
274
+ const { open, block, suffix, eol } = splitYamlFrontmatter(content, path);
275
+ const doc = parseDocument(block);
276
+ if (doc.errors.length > 0) {
277
+ throw new TracevalsError(
278
+ `${path}: cannot edit frontmatter \u2014 ${doc.errors[0]?.message ?? "parse error"}`
279
+ );
280
+ }
281
+ let seq;
282
+ try {
283
+ seq = evalsSeq(doc, path);
284
+ } catch (err) {
285
+ if (err instanceof TracevalsError) throw err;
286
+ throw new TracevalsError(
287
+ `${path}: cannot edit frontmatter \u2014 ${err instanceof Error ? err.message : String(err)}`
288
+ );
289
+ }
290
+ const taken = existingIds(seq);
291
+ for (const entry of entries) {
292
+ if (taken.has(entry.id)) {
293
+ throw new TracevalsError(
294
+ `${path}: eval "${entry.id}" already exists in frontmatter`
295
+ );
296
+ }
297
+ taken.add(entry.id);
298
+ seq.add(doc.createNode(entryObject(entry)));
299
+ }
300
+ if (provenance) {
301
+ try {
302
+ mergeProvenance(
303
+ doc,
304
+ provenance,
305
+ entries.map((e) => e.id)
306
+ );
307
+ } catch (err) {
308
+ if (err instanceof TracevalsError) {
309
+ throw new TracevalsError(`${path}: ${err.message}`);
310
+ }
311
+ throw err;
312
+ }
313
+ }
314
+ let updated = doc.toString({ lineWidth: 0 });
315
+ if (eol === "\r\n") updated = updated.replace(/(?<!\r)\n/g, "\r\n");
316
+ return open + updated + suffix;
317
+ }
318
+
319
+ // src/tracevals/evals/write-location.ts
320
+ import { relative, resolve } from "node:path";
321
+ function noEntryRefusal(label, join, file) {
322
+ return `${label}: ${METADATA_KEY} is owned by ${file}, which joins on "${join}", and this artifact carries no ${join}.`;
323
+ }
324
+ var MetadataWriter = class _MetadataWriter {
325
+ constructor(ctx) {
326
+ this.ctx = ctx;
327
+ }
328
+ ctx;
329
+ static async for(opts) {
330
+ const { configDir, configPath } = opts;
331
+ const config = configPath === void 0 ? null : await metaConfig(configPath, configDir);
332
+ return new _MetadataWriter({
333
+ // Artifact labels are absolute, and a manifest is reported relative to
334
+ // the config directory, the way `external.ts` reports one it read.
335
+ cwd: configDir,
336
+ base: configDir,
337
+ noConfig: configPath === void 0,
338
+ config,
339
+ ...configPath === void 0 ? {} : { configDir, configPath },
340
+ collections: opts.collections,
341
+ declaredCollections: opts.collections,
342
+ targets: opts.targets.map((t) => relative(configDir, resolve(opts.cwd, t)) || "."),
343
+ validator: marksValidator({ config, cwd: configDir, configDir })()
344
+ });
345
+ }
346
+ /** Where the `metadata` block of the artifact `label` goes. `data` is its own front matter. */
347
+ async homeFor(label, data) {
348
+ const home = await keyHome(this.ctx, label, data, METADATA_KEY);
349
+ if (home.kind === "url") return { kind: "url", file: home.file };
350
+ if (home.kind === "manifest") {
351
+ if (home.entry === void 0) {
352
+ return { kind: "no-entry", file: home.file, join: home.join };
353
+ }
354
+ return {
355
+ kind: "manifest",
356
+ file: home.file,
357
+ absPath: home.absPath,
358
+ join: home.join,
359
+ entry: home.entry,
360
+ perPage: home.perPage
361
+ };
362
+ }
363
+ return { kind: "page", proposed: home.home };
364
+ }
365
+ /**
366
+ * Ask P1 once per collection the flagged writes would be homed in. Returns
367
+ * true when a relocation was applied, which leaves the caller's config,
368
+ * artifacts and this writer stale: rebuild all three before writing.
369
+ */
370
+ async offer(writes, hooks) {
371
+ if (writes.length === 0) return false;
372
+ const applied = await offerExternalHomes(this.ctx, writes, hooks);
373
+ return applied.size > 0;
374
+ }
375
+ /**
376
+ * Replace this artifact's `metadata` block in its manifest entry with what
377
+ * `update` makes of the block the file holds now. The file is read just
378
+ * before the write, so a block another run wrote meanwhile is extended
379
+ * rather than lost, and no other byte of the file changes.
380
+ */
381
+ async write(home, update) {
382
+ let text;
383
+ try {
384
+ text = await readManifestOrEmpty(home.absPath, home.perPage);
385
+ } catch (e) {
386
+ throw new TracevalsError(`${home.file} could not be read: ${errorMessage(e)}`);
387
+ }
388
+ const at = { entry: home.entry, key: METADATA_KEY, join: home.join };
389
+ let spliced;
390
+ try {
391
+ spliced = spliceManifestValue(text, {
392
+ ...at,
393
+ value: update(readManifestValue(text, at)),
394
+ file: home.file
395
+ }).text;
396
+ } catch (e) {
397
+ throw new TracevalsError(errorMessage(e));
398
+ }
399
+ if (spliced !== text) {
400
+ await writeFileAtomic(home.absPath, spliced, { createParents: true });
401
+ }
402
+ }
403
+ };
404
+ async function metaConfig(configPath, configDir) {
405
+ try {
406
+ return await metaSection(configPath, configDir);
407
+ } catch (err) {
408
+ if (err instanceof DocmetaError) return null;
409
+ throw err;
410
+ }
411
+ }
412
+
413
+ // src/tracevals/fill/cache.ts
414
+ import { JsonCache, buildCacheKey, sha256 } from "@hawkeyexl/inference";
415
+
416
+ // src/tracevals/fill/prompt.ts
417
+ import { Ajv2020 } from "ajv/dist/2020.js";
418
+
419
+ // src/tracevals/fill/gate.ts
420
+ var ALLOWED_GRADERS = {
421
+ skill: ["ai", "tool-usage", "file-access", "regex"],
422
+ "slash-command": ["ai", "tool-usage", "file-access", "regex"],
423
+ agent: ["ai", "tool-usage", "file-access"],
424
+ "project-rules": ["ai", "skill-invoked", "tool-usage"]
425
+ };
426
+ function isAbsoluteish(path) {
427
+ return path.startsWith("/") || /^[A-Za-z]:[\\/]/.test(path);
428
+ }
429
+ function groundingError(proposal, vocabulary) {
430
+ const options = proposal.options ?? {};
431
+ if (proposal.grader === "tool-usage") {
432
+ const tool = options.tool;
433
+ if (typeof tool === "string" && !tool.startsWith("mcp__") && !vocabulary.tools.has(tool)) {
434
+ return `no tool named "${tool}" is available in this project`;
435
+ }
436
+ }
437
+ if (proposal.grader === "skill-invoked") {
438
+ const skill = options.skill;
439
+ if (typeof skill === "string" && !vocabulary.skills.has(skill)) {
440
+ return `no skill named "${skill}" was found in this project`;
441
+ }
442
+ }
443
+ if (proposal.grader === "file-access") {
444
+ const path = options.path;
445
+ if (typeof path === "string" && isAbsoluteish(path)) {
446
+ return `options.path must be a repository-relative suffix, not "${path}"`;
447
+ }
448
+ }
449
+ return void 0;
450
+ }
451
+ function normalize(proposal, artifactType) {
452
+ if (artifactType !== "agent" || proposal.grader !== "tool-usage") {
453
+ return proposal;
454
+ }
455
+ return {
456
+ ...proposal,
457
+ options: { ...proposal.options ?? {}, includeSidechains: true }
458
+ };
459
+ }
460
+ function gateProposals(proposals, options) {
461
+ const { artifactType, threshold, maxEvals, vocabulary } = options;
462
+ const allowed = new Set(ALLOWED_GRADERS[artifactType]);
463
+ const taken = new Set(options.existingNames);
464
+ const rejected = [];
465
+ const survivors = [];
466
+ for (const raw of proposals) {
467
+ if (taken.has(raw.name)) {
468
+ rejected.push({ proposal: raw, reason: "duplicate-name" });
469
+ continue;
470
+ }
471
+ if (!allowed.has(raw.grader)) {
472
+ rejected.push({
473
+ proposal: raw,
474
+ reason: "grader-not-allowed",
475
+ detail: `${raw.grader} may not be declared on a ${artifactType} artifact`
476
+ });
477
+ continue;
478
+ }
479
+ const proposal = normalize(raw, artifactType);
480
+ if (proposal.grader !== "ai") {
481
+ const grader = graderFor(proposal.grader);
482
+ const validate = grader?.validateOptions;
483
+ if (validate === void 0) {
484
+ rejected.push({
485
+ proposal,
486
+ reason: "invalid-options",
487
+ detail: `grader ${proposal.grader} cannot validate its options`
488
+ });
489
+ continue;
490
+ }
491
+ const invalid = validate(proposal.options ?? {});
492
+ if (invalid !== void 0) {
493
+ rejected.push({ proposal, reason: "invalid-options", detail: invalid });
494
+ continue;
495
+ }
496
+ const ungrounded = groundingError(proposal, vocabulary);
497
+ if (ungrounded !== void 0) {
498
+ rejected.push({
499
+ proposal,
500
+ reason: "ungrounded-target",
501
+ detail: ungrounded
502
+ });
503
+ continue;
504
+ }
505
+ }
506
+ taken.add(proposal.name);
507
+ survivors.push(proposal);
508
+ }
509
+ const confident = [];
510
+ for (const proposal of survivors) {
511
+ if (proposal.confidence >= threshold) confident.push(proposal);
512
+ else rejected.push({ proposal, reason: "low-confidence" });
513
+ }
514
+ confident.sort((a, b) => b.confidence - a.confidence);
515
+ return {
516
+ accepted: confident.slice(0, maxEvals),
517
+ capped: confident.slice(maxEvals),
518
+ rejected
519
+ };
520
+ }
521
+
522
+ // src/tracevals/fill/prompt.ts
523
+ var FILL_PROMPT_VERSION = 4;
524
+ var MAX_BODY_CHARS = 6e3;
525
+ var TYPE_GUIDANCE = {
526
+ skill: [
527
+ "This artifact is a skill: a procedure an agent follows on demand.",
528
+ "Its testable claims are entry criteria (what must be true before starting),",
529
+ "process steps (what must actually happen), and output specifications (what",
530
+ "the result must contain or must never contain)."
531
+ ].join("\n"),
532
+ "slash-command": [
533
+ "This artifact is a slash command: a procedure whose body is injected as a",
534
+ "prompt when a person types its name. Its testable claims are the steps it",
535
+ "mandates, the tools it permits or forbids, and the artifact it must leave",
536
+ "behind. Its `argument-hint` and `description` are harness metadata, not",
537
+ "assertions \u2014 do not test them."
538
+ ].join("\n"),
539
+ agent: [
540
+ "This artifact is an agent definition: a role with boundaries.",
541
+ "Its testable claims are constraints (what the agent must never do),",
542
+ "quality criteria (measurable standards its output must meet), and",
543
+ "escalation rules (conditions under which it must stop and hand off).",
544
+ "Its identity and persona are framing, not assertions \u2014 do not test them."
545
+ ].join("\n"),
546
+ "project-rules": [
547
+ "This artifact is a project rules file: standing rules for all work in the",
548
+ "repository. Its testable claims are process gates, quality gates, approval",
549
+ "requirements, and hard limits. These files are assertion-dense; prefer the",
550
+ "rules stated plainly over anything you would have to infer."
551
+ ].join("\n")
552
+ };
553
+ var GRADER_GUIDANCE = {
554
+ ai: "judgment a human would have to read the session to make",
555
+ "tool-usage": "a named tool was or was not used (options: tool, expect, min, max)",
556
+ "skill-invoked": "a named skill was or was not invoked (options: skill, expect)",
557
+ "file-access": "a file was or was not read/written/edited (options: path, op, expect)",
558
+ regex: "a pattern does or does not appear in session text (options: pattern, flags, on, expect)"
559
+ };
560
+ function systemPromptFor(artifactType) {
561
+ const allowed = ALLOWED_GRADERS[artifactType];
562
+ return [
563
+ "You extract evals from an AI agent instruction artifact.",
564
+ "",
565
+ TYPE_GUIDANCE[artifactType],
566
+ "",
567
+ "An eval is a durable claim about how a session behaved, graded against",
568
+ "a recorded session trace on every future run. You are extracting claims the",
569
+ "artifact already makes \u2014 not inventing new policy for it.",
570
+ "",
571
+ "Rules for every eval you propose:",
572
+ "- It must be decidable as a binary pass or fail against session evidence.",
573
+ " If you cannot say precisely what would make it fail, it does not qualify.",
574
+ "- Quote the artifact's own intent. Never assert incidental phrasing,",
575
+ " formatting, or wording that is expected to change.",
576
+ "- Prefer a deterministic grader whenever the claim can be checked",
577
+ " mechanically; fall back to `ai` only for genuine judgment.",
578
+ "- Ids are short kebab-case identifiers, unique within the artifact.",
579
+ "- Provide examples.pass and examples.fail: one sentence each describing a",
580
+ " session that satisfies or violates the eval.",
581
+ "- Report an honest confidence between 0 and 1 that the criterion is",
582
+ " correct, checkable, and worth guarding. Do not inflate it.",
583
+ "- Do not restate evals the artifact already declares.",
584
+ "- Propose at most the requested number, fewer when the artifact offers",
585
+ " little worth guarding, and none at all when nothing qualifies.",
586
+ "",
587
+ "Graders available for this artifact type:",
588
+ ...allowed.map((kind) => `- ${kind}: ${GRADER_GUIDANCE[kind] ?? ""}`),
589
+ "",
590
+ "Deterministic graders only work against targets that really exist. Use tool",
591
+ "and skill names exactly as given below; if the name you want is not listed,",
592
+ "use the `ai` grader instead of guessing.",
593
+ "",
594
+ "Finally, list under `needsSharpening` any instruction in the artifact that",
595
+ "sounds like a requirement but cannot be tested as written \u2014 an undefined",
596
+ "quality bar, an unmeasurable adjective, a rule with no observable outcome.",
597
+ "Say what makes it untestable. Do not invent a criterion to cover it."
598
+ ].join("\n");
599
+ }
600
+ var PROPOSAL_SCHEMA = {
601
+ type: "object",
602
+ required: ["evals"],
603
+ additionalProperties: false,
604
+ properties: {
605
+ evals: {
606
+ type: "array",
607
+ items: {
608
+ type: "object",
609
+ required: ["name", "assertion", "grader", "examples", "confidence"],
610
+ additionalProperties: false,
611
+ properties: {
612
+ name: { type: "string", pattern: "^[a-z0-9][a-z0-9-]*$" },
613
+ assertion: { type: "string", minLength: 1 },
614
+ grader: { type: "string" },
615
+ options: { type: "object" },
616
+ evidence: { type: "string" },
617
+ examples: {
618
+ type: "object",
619
+ required: ["pass", "fail"],
620
+ additionalProperties: false,
621
+ properties: {
622
+ pass: { type: "string" },
623
+ fail: { type: "string" }
624
+ }
625
+ },
626
+ severity: { enum: [...SEVERITIES] },
627
+ confidence: { type: "number", minimum: 0, maximum: 1 },
628
+ rationale: { type: "string" }
629
+ }
630
+ }
631
+ },
632
+ needsSharpening: {
633
+ type: "array",
634
+ items: {
635
+ type: "object",
636
+ required: ["instruction", "reason"],
637
+ additionalProperties: false,
638
+ properties: {
639
+ instruction: { type: "string" },
640
+ reason: { type: "string" },
641
+ suggestion: { type: "string" }
642
+ }
643
+ }
644
+ }
645
+ }
646
+ };
647
+ var ajv = new Ajv2020({ allErrors: true });
648
+ var validateProposal = ajv.compile(
649
+ PROPOSAL_SCHEMA
650
+ );
651
+ function isValidProposal(value) {
652
+ return validateProposal(value);
653
+ }
654
+ function buildFillUser(options) {
655
+ const { artifact, existingNames, maxEvals, facts, knownSkills } = options;
656
+ const body = artifact.content.length > MAX_BODY_CHARS ? `${artifact.content.slice(0, MAX_BODY_CHARS)}
657
+ \u2026(truncated)` : artifact.content;
658
+ const lines = [
659
+ "# Artifact",
660
+ `path: ${artifact.path}`,
661
+ `type: ${artifact.type}`,
662
+ `name: ${facts.name ?? artifact.name}`
663
+ ];
664
+ if (facts.description !== void 0) {
665
+ lines.push(`description: ${facts.description}`);
666
+ }
667
+ lines.push(
668
+ "",
669
+ "# Existing evals (do not restate these)",
670
+ existingNames.length > 0 ? existingNames.join(", ") : "(none)",
671
+ "",
672
+ "# Tools this artifact grants itself",
673
+ facts.declaredTools.length > 0 ? facts.declaredTools.join(", ") : "(none declared)",
674
+ "",
675
+ "# Skills that exist in this project",
676
+ knownSkills.length > 0 ? knownSkills.join(", ") : "(none)",
677
+ "",
678
+ "# Maximum evals to propose",
679
+ String(maxEvals),
680
+ "",
681
+ "# Artifact content",
682
+ "",
683
+ body
684
+ );
685
+ return lines.join("\n");
686
+ }
687
+
688
+ // src/tracevals/fill/cache.ts
689
+ function listPart(values) {
690
+ return JSON.stringify([...values].sort());
691
+ }
692
+ function fillCacheKey(parts) {
693
+ return buildCacheKey([
694
+ parts.provider,
695
+ parts.model,
696
+ `fill-v${FILL_PROMPT_VERSION}`,
697
+ `t${parts.temperature}`,
698
+ `n${parts.maxEvals}`,
699
+ parts.artifactType,
700
+ parts.path,
701
+ sha256(parts.body),
702
+ listPart(parts.existingNames),
703
+ listPart(parts.knownSkills)
704
+ ]);
705
+ }
706
+ var FillCache = class {
707
+ store;
708
+ constructor(dir, enabled = true) {
709
+ this.store = new JsonCache(dir, enabled, "manni-tracevals");
710
+ }
711
+ get(key) {
712
+ const proposal = this.store.get(key);
713
+ if (!isValidProposal(proposal)) return void 0;
714
+ return proposal;
715
+ }
716
+ set(key, proposal) {
717
+ this.store.set(key, proposal);
718
+ }
719
+ };
720
+
721
+ // src/tracevals/fill/facts.ts
722
+ function toolList(value) {
723
+ if (Array.isArray(value)) {
724
+ return value.filter((v) => typeof v === "string");
725
+ }
726
+ if (typeof value === "string") {
727
+ return value.split(",").map((tool) => tool.trim()).filter((tool) => tool.length > 0);
728
+ }
729
+ return [];
730
+ }
731
+ function artifactFacts(artifact) {
732
+ try {
733
+ const { data } = extractFrontmatter(artifact.content, "markdown");
734
+ const facts = { declaredTools: toolList(data.tools) };
735
+ if (typeof data.name === "string") facts.name = data.name;
736
+ if (typeof data.description === "string") {
737
+ facts.description = data.description;
738
+ }
739
+ return facts;
740
+ } catch {
741
+ return { declaredTools: [] };
742
+ }
743
+ }
744
+
745
+ // src/tracevals/fill/mock.ts
746
+ function mockFillProposal() {
747
+ return {
748
+ json: {
749
+ evals: [
750
+ {
751
+ name: "reads-before-editing",
752
+ assertion: "The session read a source file before editing it.",
753
+ grader: "tool-usage",
754
+ options: { tool: "Read", expect: "used" },
755
+ examples: {
756
+ pass: "Read is called before the first Edit",
757
+ fail: "Edit is called with no prior Read"
758
+ },
759
+ confidence: 0.9
760
+ },
761
+ {
762
+ name: "explains-its-reasoning",
763
+ assertion: "The session explained why it made each change.",
764
+ grader: "ai",
765
+ examples: {
766
+ pass: "Each edit is accompanied by a rationale",
767
+ fail: "Edits land with no explanation"
768
+ },
769
+ confidence: 0.3
770
+ }
771
+ ],
772
+ needsSharpening: [
773
+ {
774
+ instruction: "Produce high-quality work.",
775
+ reason: '"high-quality" has no measurable bar, so no session could fail it',
776
+ suggestion: "Name the specific property that must hold, and how to observe it."
777
+ }
778
+ ]
779
+ }
780
+ };
781
+ }
782
+
783
+ // src/tracevals/fill/vocabulary.ts
784
+ var BUILTIN_TOOLS = [
785
+ "Agent",
786
+ "AskUserQuestion",
787
+ "Bash",
788
+ "BashOutput",
789
+ "Edit",
790
+ "ExitPlanMode",
791
+ "Glob",
792
+ "Grep",
793
+ "KillShell",
794
+ "NotebookEdit",
795
+ "Read",
796
+ "Skill",
797
+ "SlashCommand",
798
+ "Task",
799
+ "TodoWrite",
800
+ "WebFetch",
801
+ "WebSearch",
802
+ "Write"
803
+ ];
804
+ function buildVocabulary(artifacts) {
805
+ const tools = new Set(BUILTIN_TOOLS);
806
+ const skills = /* @__PURE__ */ new Set();
807
+ for (const discovered of artifacts) {
808
+ if (discovered.artifact.type === "skill") {
809
+ skills.add(discovered.artifact.name);
810
+ }
811
+ for (const tool of artifactFacts(discovered.artifact).declaredTools) {
812
+ tools.add(tool);
813
+ }
814
+ }
815
+ return { tools, skills };
816
+ }
817
+
818
+ // src/tracevals/commands/fill.ts
819
+ function ownData(content) {
820
+ return extractFrontmatter(content, "markdown").data;
821
+ }
822
+ function toEvalEntry(proposed) {
823
+ const entry = {
824
+ id: proposed.name,
825
+ assertion: proposed.assertion,
826
+ // New evals start as regression: they describe behavior the artifact
827
+ // already asks for, not a boundary being probed.
828
+ type: "regression",
829
+ grader: proposed.grader,
830
+ // The vocabulary takes one anchor or a list; the model proposes one of each.
831
+ examples: { pass: [proposed.examples.pass], fail: [proposed.examples.fail] }
832
+ };
833
+ if (proposed.options !== void 0) entry.options = proposed.options;
834
+ if (proposed.evidence !== void 0) entry.evidence = proposed.evidence;
835
+ if (proposed.severity !== void 0) entry.severity = proposed.severity;
836
+ return entry;
837
+ }
838
+ async function runFill(options = {}) {
839
+ const cwd = options.cwd ?? process.cwd();
840
+ const root = resolve2(options.project ?? cwd);
841
+ const lookup = {
842
+ ...options.config === void 0 ? {} : { configPath: options.config },
843
+ ...options.noConfig === void 0 ? {} : { noConfig: options.noConfig }
844
+ };
845
+ const {
846
+ config: loaded,
847
+ dir: configDir,
848
+ path: configPath
849
+ } = await discoverConfig(options.configDir ?? cwd, lookup);
850
+ const config = {
851
+ ...loaded,
852
+ plugins: [...loaded.plugins, ...options.require ?? []]
853
+ };
854
+ const plugins = await loadGraderPlugins({
855
+ plugins: config.plugins,
856
+ configDir
857
+ });
858
+ const flags = {
859
+ ...options.provider !== void 0 ? { provider: options.provider } : {},
860
+ ...options.model !== void 0 ? { model: options.model } : {},
861
+ ...options.local !== void 0 ? { local: options.local } : {}
862
+ };
863
+ assertProviderSelection(selectProvider(config, flags));
864
+ const threshold = options.confidence ?? config.fill.confidenceThreshold;
865
+ const maxEvals = options.maxEvals ?? config.fill.maxEvalsPerArtifact;
866
+ const temperature = config.fill.temperature;
867
+ const maxTurns = options.maxTurns ?? config.fill.maxTurns;
868
+ const cache = new FillCache(
869
+ resolve2(configDir, config.fill.cacheDir),
870
+ options.noCache !== true
871
+ );
872
+ const load = async () => {
873
+ const { collections: collections2 } = await discoverConfig(options.configDir ?? cwd, lookup);
874
+ const external = await loadExternalEvals({
875
+ collections: collections2,
876
+ configDir,
877
+ ...configPath === void 0 ? {} : { configPath },
878
+ ...options.offline === void 0 ? {} : { offline: options.offline }
879
+ });
880
+ const discovery2 = await discoverArtifacts({
881
+ root,
882
+ cwd,
883
+ ...options.paths !== void 0 ? { paths: options.paths } : {},
884
+ ...options.exclude !== void 0 ? { exclude: options.exclude } : {},
885
+ ...external === null ? {} : {
886
+ metadataFor: async (artifact) => (await external.forArtifact(artifact)).extracted
887
+ }
888
+ });
889
+ const writer2 = await MetadataWriter.for({
890
+ collections: collections2,
891
+ configDir,
892
+ configPath,
893
+ cwd,
894
+ targets: options.paths ?? []
895
+ });
896
+ return { collections: collections2, discovery: discovery2, writer: writer2 };
897
+ };
898
+ let { collections, discovery, writer } = await load();
899
+ if (options.confirm !== void 0 && options.dryRun !== true && collections.length > 0) {
900
+ const flagged = [];
901
+ for (const { artifact, status, skip } of discovery.artifacts) {
902
+ if (status !== "ok" || skip || artifact.type === "project-rules") continue;
903
+ const home = await writer.homeFor(artifact.path, ownData(artifact.content));
904
+ if (home.kind === "page") {
905
+ flagged.push({ label: artifact.path, key: METADATA_KEY, home: home.proposed });
906
+ }
907
+ }
908
+ const applied = await writer.offer(flagged, {
909
+ confirm: options.confirm,
910
+ ...options.onNotice === void 0 ? {} : { onNotice: options.onNotice },
911
+ ...options.onRelocated === void 0 ? {} : { onRelocated: options.onRelocated }
912
+ });
913
+ if (applied) ({ collections, discovery, writer } = await load());
914
+ }
915
+ const vocabulary = buildVocabulary(discovery.artifacts);
916
+ const knownSkills = [...vocabulary.skills].sort();
917
+ const injected = options.providerInstance;
918
+ let resolving;
919
+ const resolveOnce = () => resolving ??= resolveProviderIdentity(config, flags);
920
+ const getIdentity = async () => injected !== void 0 ? { provider: injected.provider(), model: injected.modelName() } : resolveOnce();
921
+ let provider = injected;
922
+ const getProvider = async () => provider ??= constructProvider(config, await resolveOnce(), {
923
+ // The default mock response is judge-shaped and would fail this
924
+ // command's schema, so seed the mock seam with a proposal instead.
925
+ mockResponses: [mockFillProposal()]
926
+ });
927
+ let turns = 0;
928
+ const results = [];
929
+ const homeless = [];
930
+ for (const discovered of discovery.artifacts) {
931
+ results.push(await fillOne(discovered));
932
+ }
933
+ const report = {
934
+ results,
935
+ threshold,
936
+ dryRun: options.dryRun === true,
937
+ warnings: [
938
+ ...plugins.warnings,
939
+ ...discovery.warnings,
940
+ ...externalWriteWarnings(homeless, options.dryRun === true)
941
+ ],
942
+ exitCode: results.some((r) => r.status === "error") ? 1 : 0
943
+ };
944
+ return {
945
+ report,
946
+ rendered: renderFill(report, {
947
+ cwd,
948
+ ...options.color === void 0 ? {} : { color: options.color }
949
+ })
950
+ };
951
+ async function fillOne(discovered) {
952
+ const { artifact } = discovered;
953
+ const base = {
954
+ artifact: artifact.path,
955
+ type: artifact.type,
956
+ status: "nothing-proposed",
957
+ written: [],
958
+ rejected: [],
959
+ capped: [],
960
+ needsSharpening: [],
961
+ cached: false
962
+ };
963
+ if (discovered.status !== "ok") {
964
+ return {
965
+ ...base,
966
+ status: discovered.status === "unreadable" ? "unreadable" : "error",
967
+ ...discovered.error !== void 0 ? { error: discovered.error } : {}
968
+ };
969
+ }
970
+ if (discovered.skip) return { ...base, status: "skipped" };
971
+ const facts = artifactFacts(artifact);
972
+ const identity = await getIdentity();
973
+ const key = fillCacheKey({
974
+ provider: identity.provider,
975
+ model: identity.model,
976
+ temperature,
977
+ maxEvals,
978
+ artifactType: artifact.type,
979
+ path: artifact.path,
980
+ body: artifact.content,
981
+ existingNames: discovered.existingNames,
982
+ knownSkills
983
+ });
984
+ let raw = cache.get(key);
985
+ const cached = raw !== void 0;
986
+ if (raw === void 0) {
987
+ if (maxTurns !== null && turns >= maxTurns) {
988
+ return { ...base, status: "skipped", error: `${TURN_BUDGET_SKIP} exhausted` };
989
+ }
990
+ turns += 1;
991
+ try {
992
+ const response = await (await getProvider()).completeJSON({
993
+ system: systemPromptFor(artifact.type),
994
+ user: buildFillUser({
995
+ artifact,
996
+ existingNames: discovered.existingNames,
997
+ maxEvals,
998
+ facts,
999
+ knownSkills
1000
+ }),
1001
+ schema: PROPOSAL_SCHEMA,
1002
+ temperature
1003
+ });
1004
+ if (!isValidProposal(response.json)) {
1005
+ return {
1006
+ ...base,
1007
+ status: "error",
1008
+ error: "provider returned a proposal that does not match the schema"
1009
+ };
1010
+ }
1011
+ raw = response.json;
1012
+ cache.set(key, raw);
1013
+ } catch (err) {
1014
+ if (err instanceof TracevalsError) throw err;
1015
+ return {
1016
+ ...base,
1017
+ status: "error",
1018
+ error: err instanceof Error ? err.message : String(err)
1019
+ };
1020
+ }
1021
+ }
1022
+ const gated = gateProposals(raw.evals, {
1023
+ artifactType: artifact.type,
1024
+ threshold,
1025
+ existingNames: discovered.existingNames,
1026
+ // The cap is a ceiling on the artifact's total, not on one run's
1027
+ // additions, so repeated fills cannot grow it without bound.
1028
+ maxEvals: Math.max(0, maxEvals - discovered.existingNames.length),
1029
+ vocabulary
1030
+ });
1031
+ const result = {
1032
+ ...base,
1033
+ written: gated.accepted,
1034
+ rejected: gated.rejected,
1035
+ capped: gated.capped,
1036
+ needsSharpening: raw.needsSharpening ?? [],
1037
+ cached
1038
+ };
1039
+ if (gated.accepted.length === 0) return result;
1040
+ if (artifact.type === "project-rules") {
1041
+ return { ...result, status: "propose-only" };
1042
+ }
1043
+ const home = await writer.homeFor(artifact.path, ownData(artifact.content));
1044
+ if (options.dryRun !== true) {
1045
+ if (home.kind === "url") {
1046
+ throw new TracevalsError(urlManifestRefusal(artifact.path, home.file));
1047
+ }
1048
+ if (home.kind === "no-entry") {
1049
+ throw new TracevalsError(noEntryRefusal(artifact.path, home.join, home.file));
1050
+ }
1051
+ }
1052
+ const landed = home.kind === "page" ? result : { ...result, manifest: home.file };
1053
+ if (options.dryRun === true) return { ...landed, status: "proposed" };
1054
+ const provenance = {
1055
+ generatedBy: identity.model || identity.provider,
1056
+ confidence: Object.fromEntries(
1057
+ gated.accepted.map((c) => [c.name, c.confidence])
1058
+ )
1059
+ };
1060
+ try {
1061
+ if (home.kind === "manifest") {
1062
+ await writer.write(
1063
+ home,
1064
+ (current) => appendMetadataEvals(
1065
+ current,
1066
+ artifact.path,
1067
+ gated.accepted.map(toEvalEntry),
1068
+ provenance
1069
+ )
1070
+ );
1071
+ } else {
1072
+ const updated = appendArtifactEvals(
1073
+ artifact.content,
1074
+ artifact.path,
1075
+ gated.accepted.map(toEvalEntry),
1076
+ provenance
1077
+ );
1078
+ await writeFile(artifact.path, updated);
1079
+ if (home.kind === "page") noteHomeless(artifact.path, home);
1080
+ }
1081
+ } catch (err) {
1082
+ return {
1083
+ ...landed,
1084
+ status: "error",
1085
+ written: [],
1086
+ error: err instanceof Error ? err.message : String(err)
1087
+ };
1088
+ }
1089
+ return { ...landed, status: "filled" };
1090
+ }
1091
+ function noteHomeless(label, home) {
1092
+ if (collections.length === 0) return;
1093
+ homeless.push({ label, key: METADATA_KEY, home: home.proposed });
1094
+ }
1095
+ }
1096
+ var STATUS_LABEL = {
1097
+ filled: "filled",
1098
+ proposed: "proposed",
1099
+ "propose-only": "proposed",
1100
+ "nothing-proposed": "no-op",
1101
+ skipped: "skipped",
1102
+ unreadable: "skipped",
1103
+ error: "error"
1104
+ };
1105
+ function names(entries) {
1106
+ return entries.map((c) => `${c.name} ${c.confidence.toFixed(2)}`).join(", ");
1107
+ }
1108
+ function renderFill(report, opts = {}) {
1109
+ const pc = palette(opts.color === true);
1110
+ const from = opts.cwd ?? process.cwd();
1111
+ const show = (path) => {
1112
+ const rel = relative2(from, path);
1113
+ return rel === "" || rel.startsWith("..") ? path : rel;
1114
+ };
1115
+ const { green, cyan, dim, red } = pc;
1116
+ const lines = [];
1117
+ for (const result of report.results) {
1118
+ const label = STATUS_LABEL[result.status].padEnd(9);
1119
+ const tag = result.cached ? dim(" [cached]") : "";
1120
+ const where = result.manifest === void 0 ? "" : dim(` in ${result.manifest}`);
1121
+ switch (result.status) {
1122
+ case "filled":
1123
+ lines.push(`${green(label)} ${show(result.artifact)} +${result.written.length} (${names(result.written)})${where}${tag}`);
1124
+ break;
1125
+ case "proposed":
1126
+ lines.push(`${cyan(label)} ${show(result.artifact)} +${result.written.length} (${names(result.written)})${where}${tag} \u2014 dry run, not written`);
1127
+ break;
1128
+ case "propose-only":
1129
+ lines.push(`${cyan(label)} ${show(result.artifact)} +${result.written.length} (${names(result.written)})${tag} \u2014 project rules are never written; copy what you want`);
1130
+ break;
1131
+ case "nothing-proposed":
1132
+ lines.push(`${dim(label)} ${show(result.artifact)} (nothing new proposed)${tag}`);
1133
+ break;
1134
+ case "skipped":
1135
+ lines.push(`${dim(label)} ${show(result.artifact)} (${result.error ?? "metadata.evals.skip"})`);
1136
+ break;
1137
+ case "unreadable":
1138
+ lines.push(`${dim(label)} ${show(result.artifact)} (${result.error ?? "unreadable frontmatter"})`);
1139
+ break;
1140
+ case "error":
1141
+ lines.push(`${red(label)} ${show(result.artifact)}: ${result.error ?? "unknown error"}`);
1142
+ break;
1143
+ }
1144
+ const belowThreshold = result.rejected.filter((r) => r.reason === "low-confidence");
1145
+ if (belowThreshold.length > 0) {
1146
+ lines.push(dim(` below ${report.threshold}: ${names(belowThreshold.map((r) => r.proposal))}`));
1147
+ }
1148
+ for (const rejection of result.rejected) {
1149
+ if (rejection.reason === "low-confidence") continue;
1150
+ lines.push(dim(` ${rejection.reason}: ${rejection.proposal.name}${rejection.detail ? ` \u2014 ${rejection.detail}` : ""}`));
1151
+ }
1152
+ if (result.capped.length > 0) {
1153
+ lines.push(dim(` over per-artifact cap: ${names(result.capped)}`));
1154
+ }
1155
+ for (const note of result.needsSharpening) {
1156
+ lines.push(dim(` needs sharpening: "${note.instruction}" \u2014 ${note.reason}`));
1157
+ }
1158
+ }
1159
+ if (report.results.length === 0) {
1160
+ lines.push("No skills, agent definitions, or project rules found.");
1161
+ }
1162
+ for (const warning of report.warnings) lines.push(dim(`warning: ${warning}`));
1163
+ lines.push("");
1164
+ const total = report.results.reduce((n, r) => n + r.written.length, 0);
1165
+ lines.push(
1166
+ report.dryRun ? `Threshold ${report.threshold} \xB7 ${total} evals proposed, none written (dry run)` : `Threshold ${report.threshold} \xB7 ${total} evals written`
1167
+ );
1168
+ return lines.join("\n");
1169
+ }
1170
+
1171
+ export {
1172
+ runList,
1173
+ renderList,
1174
+ appendArtifactEvals,
1175
+ ALLOWED_GRADERS,
1176
+ gateProposals,
1177
+ FILL_PROMPT_VERSION,
1178
+ MAX_BODY_CHARS,
1179
+ systemPromptFor,
1180
+ PROPOSAL_SCHEMA,
1181
+ isValidProposal,
1182
+ buildFillUser,
1183
+ fillCacheKey,
1184
+ FillCache,
1185
+ artifactFacts,
1186
+ mockFillProposal,
1187
+ BUILTIN_TOOLS,
1188
+ buildVocabulary,
1189
+ runFill,
1190
+ renderFill
1191
+ };
1192
+ //# sourceMappingURL=chunk-I2AH3CJJ.js.map