@hawkeyexl/manni 4.3.0 → 4.4.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 (224) hide show
  1. package/README.md +9 -1
  2. package/dist/batch-DFRCLY77.js +36 -0
  3. package/dist/build-Q5C6SX5J.js +31 -0
  4. package/dist/calibrate-XQILKXS3.js +31 -0
  5. package/dist/capture-JQMOC3BW.js +32 -0
  6. package/dist/check-HYNGXYJV.js +18 -0
  7. package/dist/check-HYNGXYJV.js.map +1 -0
  8. package/dist/check-NFZQLJKN.js +27 -0
  9. package/dist/check-NFZQLJKN.js.map +1 -0
  10. package/dist/check-U54XV6BP.js +40 -0
  11. package/dist/check-U54XV6BP.js.map +1 -0
  12. package/dist/chunk-252LNOEG.js +622 -0
  13. package/dist/chunk-252LNOEG.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-2U3ORT6D.js +230 -0
  17. package/dist/chunk-2U3ORT6D.js.map +1 -0
  18. package/dist/chunk-3AFUD75K.js +23 -0
  19. package/dist/chunk-3AFUD75K.js.map +1 -0
  20. package/dist/{chunk-3R23C5DT.js → chunk-4TSWU2SU.js} +2 -14
  21. package/dist/chunk-4TSWU2SU.js.map +1 -0
  22. package/dist/chunk-5KHIFVD2.js +1192 -0
  23. package/dist/chunk-5KHIFVD2.js.map +1 -0
  24. package/dist/chunk-5YXBLLAC.js +2165 -0
  25. package/dist/chunk-5YXBLLAC.js.map +1 -0
  26. package/dist/chunk-644JVKJ4.js +3872 -0
  27. package/dist/chunk-644JVKJ4.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-77HX34KD.js +1287 -0
  33. package/dist/chunk-77HX34KD.js.map +1 -0
  34. package/dist/{chunk-MOQXWXSG.js → chunk-7QMCHA4N.js} +32 -240
  35. package/dist/chunk-7QMCHA4N.js.map +1 -0
  36. package/dist/chunk-BUXDLRDT.js +455 -0
  37. package/dist/chunk-BUXDLRDT.js.map +1 -0
  38. package/dist/chunk-C6OH6CNF.js +388 -0
  39. package/dist/chunk-C6OH6CNF.js.map +1 -0
  40. package/dist/chunk-CK7SKVHR.js +1042 -0
  41. package/dist/chunk-CK7SKVHR.js.map +1 -0
  42. package/dist/chunk-CYWHFSRT.js +6737 -0
  43. package/dist/chunk-CYWHFSRT.js.map +1 -0
  44. package/dist/chunk-ERMSO34F.js +17 -0
  45. package/dist/chunk-ERMSO34F.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-G7NJSREW.js +979 -0
  49. package/dist/chunk-G7NJSREW.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-H2DLH2JI.js +198 -0
  53. package/dist/chunk-H2DLH2JI.js.map +1 -0
  54. package/dist/chunk-H6FZ66XR.js +634 -0
  55. package/dist/chunk-H6FZ66XR.js.map +1 -0
  56. package/dist/{chunk-CI2GOU3A.js → chunk-IEQHSCZG.js} +56 -137
  57. package/dist/chunk-IEQHSCZG.js.map +1 -0
  58. package/dist/chunk-IKEWBMCN.js +98 -0
  59. package/dist/chunk-IKEWBMCN.js.map +1 -0
  60. package/dist/chunk-JOHKIAVH.js +5440 -0
  61. package/dist/chunk-JOHKIAVH.js.map +1 -0
  62. package/dist/chunk-JVTNMVYU.js +10921 -0
  63. package/dist/chunk-JVTNMVYU.js.map +1 -0
  64. package/dist/chunk-K3D6FPV6.js +164 -0
  65. package/dist/chunk-K3D6FPV6.js.map +1 -0
  66. package/dist/{chunk-MFDD3YGU.js → chunk-KCGV52AM.js} +69 -48
  67. package/dist/{chunk-MFDD3YGU.js.map → chunk-KCGV52AM.js.map} +1 -1
  68. package/dist/chunk-LAKAWRV5.js +1651 -0
  69. package/dist/chunk-LAKAWRV5.js.map +1 -0
  70. package/dist/chunk-LJ2RDH3F.js +49 -0
  71. package/dist/chunk-LJ2RDH3F.js.map +1 -0
  72. package/dist/chunk-M2JCK5F6.js +805 -0
  73. package/dist/chunk-M2JCK5F6.js.map +1 -0
  74. package/dist/chunk-MNH7QO4Z.js +15 -0
  75. package/dist/chunk-MNH7QO4Z.js.map +1 -0
  76. package/dist/{chunk-RVVCA4WL.js → chunk-MYC3O5EJ.js} +4 -4
  77. package/dist/chunk-MYMBCTDE.js +387 -0
  78. package/dist/chunk-MYMBCTDE.js.map +1 -0
  79. package/dist/chunk-NCPWXHDO.js +683 -0
  80. package/dist/chunk-NCPWXHDO.js.map +1 -0
  81. package/dist/chunk-NWZ2SLYL.js +2957 -0
  82. package/dist/chunk-NWZ2SLYL.js.map +1 -0
  83. package/dist/chunk-OBFENF3U.js +934 -0
  84. package/dist/chunk-OBFENF3U.js.map +1 -0
  85. package/dist/chunk-OWBRDNRZ.js +16 -0
  86. package/dist/chunk-OWBRDNRZ.js.map +1 -0
  87. package/dist/{chunk-KTSJ2ZHA.js → chunk-PAU352SV.js} +5 -3
  88. package/dist/{chunk-KTSJ2ZHA.js.map → chunk-PAU352SV.js.map} +1 -1
  89. package/dist/chunk-PCE7QHXJ.js +68 -0
  90. package/dist/chunk-PCE7QHXJ.js.map +1 -0
  91. package/dist/chunk-PCHZY2I7.js +299 -0
  92. package/dist/chunk-PCHZY2I7.js.map +1 -0
  93. package/dist/chunk-PHF3D7ER.js +1042 -0
  94. package/dist/chunk-PHF3D7ER.js.map +1 -0
  95. package/dist/chunk-Q35YIDBV.js +1202 -0
  96. package/dist/chunk-Q35YIDBV.js.map +1 -0
  97. package/dist/chunk-RLR4TKUZ.js +4398 -0
  98. package/dist/chunk-RLR4TKUZ.js.map +1 -0
  99. package/dist/chunk-RNVYGWIC.js +30 -0
  100. package/dist/chunk-RNVYGWIC.js.map +1 -0
  101. package/dist/chunk-SE4JN5XN.js +19 -0
  102. package/dist/chunk-SE4JN5XN.js.map +1 -0
  103. package/dist/chunk-TIUTIOFB.js +25 -0
  104. package/dist/chunk-TIUTIOFB.js.map +1 -0
  105. package/dist/chunk-TU2WUBVZ.js +2 -0
  106. package/dist/chunk-TU2WUBVZ.js.map +1 -0
  107. package/dist/chunk-TZOYZK2E.js +98 -0
  108. package/dist/chunk-TZOYZK2E.js.map +1 -0
  109. package/dist/chunk-UKZNLVJW.js +17 -0
  110. package/dist/chunk-UKZNLVJW.js.map +1 -0
  111. package/dist/chunk-UZC7LDAQ.js +178 -0
  112. package/dist/chunk-UZC7LDAQ.js.map +1 -0
  113. package/dist/chunk-V46OILVC.js +3090 -0
  114. package/dist/chunk-V46OILVC.js.map +1 -0
  115. package/dist/chunk-WQE54PNH.js +1975 -0
  116. package/dist/chunk-WQE54PNH.js.map +1 -0
  117. package/dist/chunk-X3BPBDGQ.js +52 -0
  118. package/dist/chunk-X3BPBDGQ.js.map +1 -0
  119. package/dist/chunk-XSLV76YK.js +54 -0
  120. package/dist/chunk-XSLV76YK.js.map +1 -0
  121. package/dist/{chunk-QROCFW5S.js → chunk-XYLUCIKQ.js} +3 -3
  122. package/dist/chunk-YEUYKR62.js +72 -0
  123. package/dist/chunk-YEUYKR62.js.map +1 -0
  124. package/dist/{chunk-ESJ5W7EV.js → chunk-YFQBFNQS.js} +8 -94
  125. package/dist/chunk-YFQBFNQS.js.map +1 -0
  126. package/dist/chunk-YIL4XLLJ.js +1661 -0
  127. package/dist/chunk-YIL4XLLJ.js.map +1 -0
  128. package/dist/chunk-YNHZHSSM.js +41 -0
  129. package/dist/chunk-YNHZHSSM.js.map +1 -0
  130. package/dist/chunk-YTCZRLTU.js +96 -0
  131. package/dist/chunk-YTCZRLTU.js.map +1 -0
  132. package/dist/chunk-YYBFZLAJ.js +374 -0
  133. package/dist/chunk-YYBFZLAJ.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-2QPR2OZG.js +232 -0
  137. package/dist/cli-2QPR2OZG.js.map +1 -0
  138. package/dist/cli-4IOPGZHR.js +1203 -0
  139. package/dist/cli-4IOPGZHR.js.map +1 -0
  140. package/dist/cli-5FL73WHA.js +346 -0
  141. package/dist/cli-5FL73WHA.js.map +1 -0
  142. package/dist/cli-C262QOWM.js +739 -0
  143. package/dist/cli-C262QOWM.js.map +1 -0
  144. package/dist/cli-LJLLIQB2.js +423 -0
  145. package/dist/cli-LJLLIQB2.js.map +1 -0
  146. package/dist/cli-OFPZ5MAS.js +626 -0
  147. package/dist/cli-OFPZ5MAS.js.map +1 -0
  148. package/dist/cli-SHSMSRQY.js +28 -0
  149. package/dist/cli-SHSMSRQY.js.map +1 -0
  150. package/dist/cli-SRHFUZHA.js +473 -0
  151. package/dist/cli-SRHFUZHA.js.map +1 -0
  152. package/dist/cli-URZQXL4K.js +552 -0
  153. package/dist/cli-URZQXL4K.js.map +1 -0
  154. package/dist/cli.d.ts +12 -2
  155. package/dist/cli.js +109 -7651
  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-FE6KCNYJ.js +27 -0
  160. package/dist/github-FE6KCNYJ.js.map +1 -0
  161. package/dist/github-QXL3VMK2.js +32 -0
  162. package/dist/github-QXL3VMK2.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-SGZ6ZRFD.js +35 -0
  169. package/dist/json-SGZ6ZRFD.js.map +1 -0
  170. package/dist/lint-DL4LJRAY.js +28 -0
  171. package/dist/lint-DL4LJRAY.js.map +1 -0
  172. package/dist/list-6BAXTZUM.js +29 -0
  173. package/dist/list-6BAXTZUM.js.map +1 -0
  174. package/dist/list-7BKNYPSB.js +31 -0
  175. package/dist/list-7BKNYPSB.js.map +1 -0
  176. package/dist/page-3U2GCFNE.js +41 -0
  177. package/dist/page-3U2GCFNE.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-2H57QVTY.js +43 -0
  181. package/dist/pretty-2H57QVTY.js.map +1 -0
  182. package/dist/pretty-N6DO4OOL.js +37 -0
  183. package/dist/pretty-N6DO4OOL.js.map +1 -0
  184. package/dist/reporters-M6LJ2IYU.js +21 -0
  185. package/dist/reporters-M6LJ2IYU.js.map +1 -0
  186. package/dist/reporters-N6GHXSS7.js +42 -0
  187. package/dist/reporters-N6GHXSS7.js.map +1 -0
  188. package/dist/reporters-UCNEV7PH.js +71 -0
  189. package/dist/reporters-UCNEV7PH.js.map +1 -0
  190. package/dist/run-XZDAREJE.js +33 -0
  191. package/dist/run-XZDAREJE.js.map +1 -0
  192. package/dist/term-5ICGKPC3.js +1592 -0
  193. package/dist/term-5ICGKPC3.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-GQRM77YN.js +16 -0
  197. package/dist/validate-GQRM77YN.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 +52 -0
  201. package/plugin/manni/skills/check/SKILL.md +20 -0
  202. package/plugin/manni/skills/evals/SKILL.md +27 -0
  203. package/plugin/manni/skills/fix/SKILL.md +56 -0
  204. package/plugin/manni/skills/setup/SKILL.md +41 -0
  205. package/dist/batch-7JJWXWSF.js +0 -18
  206. package/dist/calibrate-IA2ARFMH.js +0 -13
  207. package/dist/capture-C32JXIFN.js +0 -15
  208. package/dist/chunk-3R23C5DT.js.map +0 -1
  209. package/dist/chunk-CI2GOU3A.js.map +0 -1
  210. package/dist/chunk-DP2ZA3WH.js +0 -26518
  211. package/dist/chunk-DP2ZA3WH.js.map +0 -1
  212. package/dist/chunk-ESJ5W7EV.js.map +0 -1
  213. package/dist/chunk-MOQXWXSG.js.map +0 -1
  214. package/dist/chunk-O6RVQKK6.js +0 -27170
  215. package/dist/chunk-O6RVQKK6.js.map +0 -1
  216. package/dist/picker-6HRMETE7.js +0 -12
  217. package/plugin/tracevals/.claude-plugin/plugin.json +0 -9
  218. package/plugin/tracevals/hooks/hooks.json +0 -17
  219. /package/dist/{batch-7JJWXWSF.js.map → batch-DFRCLY77.js.map} +0 -0
  220. /package/dist/{calibrate-IA2ARFMH.js.map → build-Q5C6SX5J.js.map} +0 -0
  221. /package/dist/{capture-C32JXIFN.js.map → calibrate-XQILKXS3.js.map} +0 -0
  222. /package/dist/{picker-6HRMETE7.js.map → capture-JQMOC3BW.js.map} +0 -0
  223. /package/dist/{chunk-RVVCA4WL.js.map → chunk-MYC3O5EJ.js.map} +0 -0
  224. /package/dist/{chunk-QROCFW5S.js.map → chunk-XYLUCIKQ.js.map} +0 -0
@@ -0,0 +1,1202 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ programName
4
+ } from "./chunk-OWBRDNRZ.js";
5
+ import {
6
+ errorMessage
7
+ } from "./chunk-G3T47CFV.js";
8
+
9
+ // src/shared/warn.ts
10
+ var said = /* @__PURE__ */ new Set();
11
+ function warn(message) {
12
+ if (said.has(message)) return;
13
+ said.add(message);
14
+ process.stderr.write(`${programName()}: ${message}
15
+ `);
16
+ }
17
+ function notice(message) {
18
+ process.stderr.write(`${programName()}: ${message}
19
+ `);
20
+ }
21
+ var noticed = /* @__PURE__ */ new Set();
22
+ function noticeOnce(message) {
23
+ if (noticed.has(message)) return;
24
+ noticed.add(message);
25
+ notice(message);
26
+ }
27
+
28
+ // src/shared/providers.ts
29
+ import { resolve } from "node:path";
30
+ import {
31
+ DEFAULT_MODELS,
32
+ DEFAULT_OPENAI_BASE_URL,
33
+ InferenceError,
34
+ resolveProviderIdentityAsync
35
+ } from "@hawkeyexl/inference";
36
+ var DEFAULT_PROVIDER = "auto";
37
+ var PROVIDERS = /* @__PURE__ */ new Set([
38
+ ...Object.keys(DEFAULT_MODELS),
39
+ DEFAULT_PROVIDER
40
+ ]);
41
+ var NAMEABLE_PROVIDERS = Object.keys(DEFAULT_MODELS).filter(
42
+ (name) => name !== "mock"
43
+ );
44
+ function assertKnownProvider(name, toError) {
45
+ if (PROVIDERS.has(name)) return;
46
+ throw toError(
47
+ `Unknown provider "${name}". Available: ${[...NAMEABLE_PROVIDERS, DEFAULT_PROVIDER].join(", ")}.`
48
+ );
49
+ }
50
+ function assertModelHasProvider(name, model, configKey, toError) {
51
+ if (name !== DEFAULT_PROVIDER || model == null) return;
52
+ throw toError(
53
+ `Model "${model}" was given without a provider: a model name does not say which provider owns it. Set --provider or ${configKey} to one of ${NAMEABLE_PROVIDERS.join(", ")}, or drop the model to take the detected provider's default.`
54
+ );
55
+ }
56
+ var PROVIDERS_KEY = "providers";
57
+ var CONNECTION_KEYS = {
58
+ anthropic: ["apiKeyEnv"],
59
+ openai: ["baseUrl", "apiKeyEnv"],
60
+ "claude-cli": ["command"],
61
+ "llama-cpp": ["modelsDir", "thoughtTokens"]
62
+ };
63
+ var CONNECTION_NAMES = Object.keys(CONNECTION_KEYS);
64
+ var TOP_LEVEL_KEYS = ["provider", "model", ...CONNECTION_NAMES];
65
+ function isMapping(value) {
66
+ return typeof value === "object" && value !== null && !Array.isArray(value);
67
+ }
68
+ function rejectUnknown(raw, allowed, label, source, toError) {
69
+ for (const key of Object.keys(raw)) {
70
+ if (!allowed.includes(key)) {
71
+ throw toError(
72
+ `${source}: "${label}" has unknown key "${key}". Supported keys: ${allowed.join(", ")}.`
73
+ );
74
+ }
75
+ }
76
+ }
77
+ function parseProviders(value, source, dir, toError) {
78
+ if (value == null) return {};
79
+ if (!isMapping(value)) {
80
+ throw toError(`${source}: "${PROVIDERS_KEY}" must be a mapping.`);
81
+ }
82
+ rejectUnknown(value, TOP_LEVEL_KEYS, PROVIDERS_KEY, source, toError);
83
+ const string = (raw, key, label) => {
84
+ const v = raw[key];
85
+ if (v === void 0) return void 0;
86
+ if (typeof v !== "string") {
87
+ throw toError(`${source}: "${label}" must be a string.`);
88
+ }
89
+ return v;
90
+ };
91
+ const parsed = {};
92
+ const provider = string(value, "provider", `${PROVIDERS_KEY}.provider`);
93
+ if (provider !== void 0) {
94
+ assertKnownProvider(provider, (message) => toError(`${source}: ${message}`));
95
+ parsed.provider = provider;
96
+ }
97
+ const model = string(value, "model", `${PROVIDERS_KEY}.model`);
98
+ if (model !== void 0) {
99
+ if (provider === void 0 || provider === DEFAULT_PROVIDER) {
100
+ throw toError(
101
+ `${source}: "${PROVIDERS_KEY}.model" was given without a provider: a model name does not say which provider owns it. Set ${PROVIDERS_KEY}.provider to one of ${NAMEABLE_PROVIDERS.join(", ")}, or drop the model to take the detected provider's default.`
102
+ );
103
+ }
104
+ parsed.model = model;
105
+ }
106
+ for (const name of CONNECTION_NAMES) {
107
+ const section = value[name];
108
+ if (section == null) continue;
109
+ const label = `${PROVIDERS_KEY}.${name}`;
110
+ if (!isMapping(section)) {
111
+ throw toError(`${source}: "${label}" must be a mapping.`);
112
+ }
113
+ rejectUnknown(section, CONNECTION_KEYS[name], label, source, toError);
114
+ const field = (key) => string(section, key, `${label}.${key}`);
115
+ switch (name) {
116
+ case "anthropic": {
117
+ const apiKeyEnv = field("apiKeyEnv");
118
+ parsed.anthropic = apiKeyEnv === void 0 ? {} : { apiKeyEnv };
119
+ break;
120
+ }
121
+ case "openai": {
122
+ const baseUrl = field("baseUrl");
123
+ const apiKeyEnv = field("apiKeyEnv");
124
+ parsed.openai = {
125
+ ...baseUrl !== void 0 ? { baseUrl } : {},
126
+ ...apiKeyEnv !== void 0 ? { apiKeyEnv } : {}
127
+ };
128
+ break;
129
+ }
130
+ case "claude-cli": {
131
+ const command = field("command");
132
+ parsed["claude-cli"] = command === void 0 ? {} : { command };
133
+ break;
134
+ }
135
+ case "llama-cpp": {
136
+ const modelsDir = field("modelsDir");
137
+ const thoughtTokens = section["thoughtTokens"];
138
+ if (thoughtTokens !== void 0 && (typeof thoughtTokens !== "number" || !Number.isInteger(thoughtTokens) || thoughtTokens < 0)) {
139
+ throw toError(
140
+ `${source}: "${label}.thoughtTokens" must be a whole number of 0 or more, got ${JSON.stringify(thoughtTokens)}.`
141
+ );
142
+ }
143
+ parsed["llama-cpp"] = {
144
+ ...modelsDir !== void 0 ? { modelsDir: resolve(dir, modelsDir) } : {},
145
+ ...thoughtTokens !== void 0 ? { thoughtTokens } : {}
146
+ };
147
+ break;
148
+ }
149
+ }
150
+ }
151
+ return parsed;
152
+ }
153
+ var LOCAL_PROVIDER = "llama-cpp";
154
+ var LOCAL_FLAG_HELP = "run inference on this machine (llama-cpp); overrides any configured or eval-level provider";
155
+ var LOCAL_COMPATIBLE = /* @__PURE__ */ new Set([LOCAL_PROVIDER, DEFAULT_PROVIDER, "mock"]);
156
+ function assertLocalFlag(flag, toError) {
157
+ if (flag.local !== true || flag.provider === void 0) return;
158
+ assertKnownProvider(flag.provider, toError);
159
+ if (LOCAL_COMPATIBLE.has(flag.provider)) return;
160
+ throw toError(
161
+ `--local and --provider ${flag.provider} contradict each other: --local runs inference on this machine with ${LOCAL_PROVIDER}. Drop one of them.`
162
+ );
163
+ }
164
+ function selectProvider(flag, levels) {
165
+ if (flag.local === true) return selectLocal(flag, levels);
166
+ const provider = flag.provider ?? levels.find((level) => level.provider !== void 0)?.provider ?? DEFAULT_PROVIDER;
167
+ const model = flag.model ?? levels.find(
168
+ (level) => level.model !== void 0 && (level.provider === void 0 || level.provider === provider)
169
+ )?.model;
170
+ return { provider, model };
171
+ }
172
+ function selectLocal(flag, levels) {
173
+ const provider = flag.provider === "mock" ? "mock" : LOCAL_PROVIDER;
174
+ const model = flag.model ?? levels.find((level) => level.model !== void 0 && level.provider === provider)?.model;
175
+ const chosen = flag.provider === void 0 ? levels.find((level) => level.provider !== void 0) : void 0;
176
+ const replaced = chosen?.provider !== void 0 && chosen.origin !== void 0 && chosen.provider !== provider && chosen.provider !== DEFAULT_PROVIDER ? { provider: chosen.provider, source: chosen.origin } : void 0;
177
+ return replaced === void 0 ? { provider, model } : { provider, model, replaced };
178
+ }
179
+ function localNotice(selection) {
180
+ const { replaced } = selection;
181
+ if (replaced === void 0) return void 0;
182
+ return `--local: using ${LOCAL_PROVIDER} instead of "${replaced.provider}" from ${replaced.source}.`;
183
+ }
184
+ function providerSpecFor(connections, selection) {
185
+ const { provider, model } = selection;
186
+ const local = connections["llama-cpp"] ?? {};
187
+ const llamaCpp = {
188
+ thoughtTokens: local.thoughtTokens ?? 0,
189
+ ...local.modelsDir !== void 0 ? { modelsDirectory: local.modelsDir } : {}
190
+ };
191
+ switch (provider) {
192
+ case "anthropic":
193
+ return {
194
+ provider,
195
+ model,
196
+ apiKeyEnv: connections.anthropic?.apiKeyEnv ?? "ANTHROPIC_API_KEY"
197
+ };
198
+ case "openai":
199
+ return {
200
+ provider,
201
+ model,
202
+ apiKeyEnv: connections.openai?.apiKeyEnv ?? "OPENAI_API_KEY",
203
+ baseUrl: connections.openai?.baseUrl ?? DEFAULT_OPENAI_BASE_URL
204
+ };
205
+ case "claude-cli":
206
+ return { provider, model, command: connections["claude-cli"]?.command ?? "claude" };
207
+ case "llama-cpp":
208
+ return { provider, model, llamaCpp };
209
+ case "mock":
210
+ return { provider, model };
211
+ case "auto": {
212
+ const baseUrl = connections.openai?.baseUrl;
213
+ const command = connections["claude-cli"]?.command;
214
+ const configuredLocal = local.thoughtTokens !== void 0 || local.modelsDir !== void 0;
215
+ return {
216
+ provider,
217
+ model,
218
+ ...baseUrl !== void 0 ? { baseUrl } : {},
219
+ ...command !== void 0 ? { command } : {},
220
+ ...configuredLocal ? { llamaCpp } : {}
221
+ };
222
+ }
223
+ }
224
+ }
225
+ async function resolveIdentity(spec, toError) {
226
+ try {
227
+ return await resolveProviderIdentityAsync(spec);
228
+ } catch (err) {
229
+ throw toError(
230
+ err instanceof InferenceError ? err.message : `Could not resolve provider "${String(spec.provider)}": ${errorMessage(err)}`
231
+ );
232
+ }
233
+ }
234
+
235
+ // src/shared/encryption.ts
236
+ import {
237
+ createCipheriv,
238
+ createDecipheriv,
239
+ createHmac,
240
+ hkdfSync,
241
+ randomBytes,
242
+ timingSafeEqual
243
+ } from "node:crypto";
244
+ import { TextDecoder } from "node:util";
245
+ var ENCRYPTED_VALUE = /^~[A-Za-z0-9_-]{82,}$/;
246
+ function isEncryptedValue(value) {
247
+ return typeof value === "string" && ENCRYPTED_VALUE.test(value);
248
+ }
249
+ var ENCRYPTION_KEY_SHAPE = /^[A-Za-z0-9_-]{32,}$/;
250
+ function isValidEncryptionKey(value) {
251
+ return typeof value === "string" && ENCRYPTION_KEY_SHAPE.test(value);
252
+ }
253
+ function generateEncryptionKey() {
254
+ return randomBytes(32).toString("hex");
255
+ }
256
+ var VERSION = 1;
257
+ var NONCE_BYTES = 12;
258
+ var TAG_BYTES = 16;
259
+ var BLOCK = 32;
260
+ var MIN_BYTES = 1 + NONCE_BYTES + BLOCK + TAG_BYTES;
261
+ var subkeyCache = /* @__PURE__ */ new Map();
262
+ function subkeys(key) {
263
+ const cached = subkeyCache.get(key);
264
+ if (cached !== void 0) return cached;
265
+ const ikm = Buffer.from(key, "utf8");
266
+ const derive = (info) => Buffer.from(hkdfSync("sha256", ikm, Buffer.alloc(0), info, 32));
267
+ const made = {
268
+ encrypt: derive("manni/v1/encrypt"),
269
+ nonce: derive("manni/v1/nonce"),
270
+ pin: derive("manni/v1/pin")
271
+ };
272
+ subkeyCache.set(key, made);
273
+ return made;
274
+ }
275
+ function requireKey(key, caller) {
276
+ if (!isValidEncryptionKey(key)) {
277
+ throw new Error(
278
+ `${caller}: the key must be at least 32 hex or base64url characters.`
279
+ );
280
+ }
281
+ }
282
+ function pad(data) {
283
+ const out = Buffer.alloc((Math.floor(data.length / BLOCK) + 1) * BLOCK);
284
+ data.copy(out);
285
+ out[data.length] = 128;
286
+ return out;
287
+ }
288
+ function unpad(padded) {
289
+ let i = padded.length - 1;
290
+ while (i >= 0 && padded[i] === 0) i--;
291
+ if (i < 0 || padded[i] !== 128) return null;
292
+ if (padded.length !== (Math.floor(i / BLOCK) + 1) * BLOCK) return null;
293
+ return padded.subarray(0, i);
294
+ }
295
+ function nonceFor(keys, context, padded) {
296
+ return createHmac("sha256", keys.nonce).update(context).update(Buffer.from([0])).update(padded).digest().subarray(0, NONCE_BYTES);
297
+ }
298
+ function aad(context) {
299
+ return Buffer.concat([Buffer.from([VERSION]), context]);
300
+ }
301
+ function encryptValue(value, key, context) {
302
+ requireKey(key, "encryptValue");
303
+ const json = JSON.stringify(value);
304
+ if (json === void 0) {
305
+ throw new TypeError("encryptValue: the value is not JSON.");
306
+ }
307
+ const keys = subkeys(key);
308
+ const ctx = Buffer.from(context, "utf8");
309
+ const padded = pad(Buffer.from(json, "utf8"));
310
+ const nonce = nonceFor(keys, ctx, padded);
311
+ const cipher = createCipheriv("aes-256-gcm", keys.encrypt, nonce, {
312
+ authTagLength: TAG_BYTES
313
+ });
314
+ cipher.setAAD(aad(ctx));
315
+ const sealed = Buffer.concat([cipher.update(padded), cipher.final()]);
316
+ const tag = cipher.getAuthTag();
317
+ const bytes = Buffer.concat([Buffer.from([VERSION]), nonce, sealed, tag]);
318
+ return `~${bytes.toString("base64url")}`;
319
+ }
320
+ var FAILED = { ok: false };
321
+ function decryptValue(token, key, context) {
322
+ try {
323
+ return open(token, key, context);
324
+ } catch {
325
+ return FAILED;
326
+ }
327
+ }
328
+ function open(token, key, context) {
329
+ if (!isEncryptedValue(token) || !isValidEncryptionKey(key)) return FAILED;
330
+ const body = token.slice(1);
331
+ const bytes = Buffer.from(body, "base64url");
332
+ if (bytes.toString("base64url") !== body) return FAILED;
333
+ if (bytes.length < MIN_BYTES) return FAILED;
334
+ if ((bytes.length - 1 - NONCE_BYTES - TAG_BYTES) % BLOCK !== 0) return FAILED;
335
+ if (bytes[0] !== VERSION) return FAILED;
336
+ const nonce = bytes.subarray(1, 1 + NONCE_BYTES);
337
+ const sealed = bytes.subarray(1 + NONCE_BYTES, bytes.length - TAG_BYTES);
338
+ const tag = bytes.subarray(bytes.length - TAG_BYTES);
339
+ const keys = subkeys(key);
340
+ const ctx = Buffer.from(context, "utf8");
341
+ const decipher = createDecipheriv("aes-256-gcm", keys.encrypt, nonce, {
342
+ authTagLength: TAG_BYTES
343
+ });
344
+ decipher.setAAD(aad(ctx));
345
+ decipher.setAuthTag(tag);
346
+ const padded = Buffer.concat([decipher.update(sealed), decipher.final()]);
347
+ if (!timingSafeEqual(nonceFor(keys, ctx, padded), nonce)) return FAILED;
348
+ const data = unpad(padded);
349
+ if (data === null) return FAILED;
350
+ const json = new TextDecoder("utf-8", { fatal: true }).decode(data);
351
+ const value = JSON.parse(json);
352
+ return { ok: true, value };
353
+ }
354
+ var KEYED_PIN_PREFIX = "hmac-sha256-";
355
+ function keyedPin(text, key) {
356
+ requireKey(key, "keyedPin");
357
+ const mac = createHmac("sha256", subkeys(key).pin).update(text, "utf8").digest("hex");
358
+ return `${KEYED_PIN_PREFIX}${mac}`;
359
+ }
360
+
361
+ // src/shared/collections.ts
362
+ import picomatch2 from "picomatch";
363
+
364
+ // src/shared/globs.ts
365
+ import picomatch from "picomatch";
366
+ var matcherCache = /* @__PURE__ */ new Map();
367
+ function matchesFileGlob(relPath, glob) {
368
+ const key = JSON.stringify(glob);
369
+ let m = matcherCache.get(key);
370
+ if (!m) {
371
+ m = picomatch(glob, { dot: true });
372
+ matcherCache.set(key, m);
373
+ }
374
+ return m(relPath.replace(/\\/g, "/"));
375
+ }
376
+
377
+ // src/shared/collections.ts
378
+ import { posix as posix2, win32 } from "node:path";
379
+
380
+ // src/shared/page-manifest.ts
381
+ import { readFileSync, realpathSync } from "node:fs";
382
+ import { isAbsolute, posix, relative, resolve as resolve2, sep } from "node:path";
383
+ import fg from "fast-glob";
384
+ import { LineCounter, isMap, isNode, isScalar, parseDocument } from "yaml";
385
+ var PAGE_PLACEHOLDER = "{page}";
386
+ function hasPagePlaceholder(file) {
387
+ return file.includes(PAGE_PLACEHOLDER);
388
+ }
389
+ function slashes(path) {
390
+ return path.split(sep).join("/").replace(/\\/g, "/");
391
+ }
392
+ function normalizeManifestPattern(file) {
393
+ const forward = file.replace(/\\/g, "/");
394
+ return forward.startsWith("./") ? forward.slice(2) : forward;
395
+ }
396
+ function pageManifestPath(file, configDir, pageAbs) {
397
+ const between = relative(configDir, pageAbs);
398
+ const pageRel = slashes(between);
399
+ if (isAbsolute(between) || pageRel === ".." || pageRel.startsWith("../")) {
400
+ return { outside: true, pageRel };
401
+ }
402
+ const ext = posix.extname(pageRel);
403
+ const page = ext === "" ? pageRel : pageRel.slice(0, -ext.length);
404
+ const named = normalizeManifestPattern(file).replace(PAGE_PLACEHOLDER, () => page);
405
+ const abs = resolve2(configDir, named);
406
+ return { abs, rel: slashes(relative(configDir, abs)) };
407
+ }
408
+ function strayManifestGlob(file) {
409
+ const normalized = normalizeManifestPattern(file);
410
+ const at = normalized.indexOf(PAGE_PLACEHOLDER);
411
+ if (at < 0) return literal(normalized);
412
+ const prefix = literal(normalized.slice(0, at));
413
+ const suffix = literal(normalized.slice(at + PAGE_PLACEHOLDER.length));
414
+ return `${prefix}**/*${suffix}`;
415
+ }
416
+ function literal(text) {
417
+ return text === "" ? "" : fg.escapePath(text);
418
+ }
419
+ function findPageManifests(file, configDir, isMember2, skip = /* @__PURE__ */ new Set()) {
420
+ const matched = fg.sync(strayManifestGlob(file), {
421
+ cwd: configDir,
422
+ absolute: true,
423
+ onlyFiles: true,
424
+ dot: false,
425
+ ignore: ["**/node_modules/**", "**/.git/**"]
426
+ }).map((path) => resolve2(path)).sort();
427
+ const found = [];
428
+ for (const abs of matched) {
429
+ if (skip.has(abs)) continue;
430
+ const entries = ownEntries(abs, file, configDir, isMember2);
431
+ if (entries.length > 0) found.push({ abs, entries });
432
+ }
433
+ return found;
434
+ }
435
+ function ownEntries(abs, file, configDir, isMember2) {
436
+ let text;
437
+ try {
438
+ text = readFileSync(abs, "utf8");
439
+ } catch {
440
+ return [];
441
+ }
442
+ const lc = new LineCounter();
443
+ const doc = parseDocument(text, { lineCounter: lc, uniqueKeys: false });
444
+ const root = doc.contents;
445
+ if (doc.errors.length > 0 || !isMap(root)) return [];
446
+ const entries = [];
447
+ for (const pair of root.items) {
448
+ const spelled = isScalar(pair.key) ? String(pair.key.value) : String(pair.key);
449
+ const pageAbs = resolve2(configDir, spelled);
450
+ if (!isMember2(slashes(relative(configDir, pageAbs)))) continue;
451
+ const resolved = pageManifestPath(file, configDir, pageAbs);
452
+ if ("outside" in resolved || !sameFile(resolved.abs, abs)) continue;
453
+ const range = isNode(pair.key) ? pair.key.range : void 0;
454
+ entries.push({
455
+ spelled,
456
+ pageAbs,
457
+ ...range ? { line: lc.linePos(range[0]).line } : {}
458
+ });
459
+ }
460
+ return entries;
461
+ }
462
+ function sameFile(a, b) {
463
+ if (a === b) return true;
464
+ try {
465
+ return realpathSync(a) === realpathSync(b);
466
+ } catch {
467
+ return false;
468
+ }
469
+ }
470
+
471
+ // src/shared/collections.ts
472
+ var COLLECTION_KEYS = [
473
+ "name",
474
+ "paths",
475
+ "exclude",
476
+ "url",
477
+ "externalMetadata"
478
+ ];
479
+ var EXTERNAL_METADATA_KEYS = ["file", "keys", "tokenEnv", "join"];
480
+ var PATH_JOIN = "path";
481
+ var SCHEMA_KEY = "$schema";
482
+ var STDIN = "<stdin>";
483
+ function isUrlRef(ref) {
484
+ return /^https?:\/\//i.test(ref);
485
+ }
486
+ function externalMetadataUrlProblem(url) {
487
+ let parsed;
488
+ try {
489
+ parsed = new URL(url);
490
+ } catch {
491
+ return "is not a valid URL";
492
+ }
493
+ if (parsed.username !== "" || parsed.password !== "") {
494
+ return 'carries a credential in the URL; put the token in an environment variable and name it with "tokenEnv"';
495
+ }
496
+ if (parsed.protocol === "https:") return null;
497
+ if (parsed.protocol === "http:" && isLoopback(parsed.hostname)) return null;
498
+ if (parsed.protocol === "http:") {
499
+ return "is plain http://; a bearer token over plaintext is a leak, and a public manifest is served over https too";
500
+ }
501
+ return `has the unsupported scheme "${parsed.protocol}"`;
502
+ }
503
+ function isLoopback(host) {
504
+ return host === "localhost" || host === "127.0.0.1" || host === "[::1]" || host === "::1" || host.startsWith("127.");
505
+ }
506
+ function rejectUnknownKeys(raw, allowed, where, source, toError) {
507
+ for (const key of Object.keys(raw)) {
508
+ if (!allowed.includes(key)) {
509
+ throw toError(
510
+ `${source}: ${where} has unknown key "${key}". Supported keys: ${allowed.join(", ")}.`
511
+ );
512
+ }
513
+ }
514
+ }
515
+ function isMapping2(value) {
516
+ return typeof value === "object" && value !== null && !Array.isArray(value);
517
+ }
518
+ function isNonEmptyString(value) {
519
+ return typeof value === "string" && value.trim() !== "";
520
+ }
521
+ function parseCollections(raw, source, toError) {
522
+ if (!Array.isArray(raw)) {
523
+ throw toError(`${source}: "collections" must be a list.`);
524
+ }
525
+ if (raw.length === 0) {
526
+ throw toError(
527
+ `${source}: "collections" must name at least one collection; remove the key if there are none.`
528
+ );
529
+ }
530
+ const taken = /* @__PURE__ */ new Map();
531
+ return raw.map((entry, i) => {
532
+ const where = `collections[${i}]`;
533
+ if (!isMapping2(entry)) {
534
+ throw toError(`${source}: ${where} must be a mapping.`);
535
+ }
536
+ rejectUnknownKeys(entry, COLLECTION_KEYS, where, source, toError);
537
+ if (!isNonEmptyString(entry.name)) {
538
+ throw toError(`${source}: ${where}.name must be a non-empty string.`);
539
+ }
540
+ const name = entry.name;
541
+ const folded = name.toLowerCase();
542
+ if (folded === "docs") {
543
+ throw toError(
544
+ `${source}: ${where}.name "${name}" collides with the docs table every query reads. Pick another name, such as "site" or "pages".`
545
+ );
546
+ }
547
+ if (folded === "derived" || folded === "_derived_rows" || folded === "resolved") {
548
+ throw toError(
549
+ `${source}: ${where}.name "${name}" collides with the derived table a query builds beside docs (the derived view, its _derived_rows table, and the resolved view over both). Pick another name.`
550
+ );
551
+ }
552
+ if (folded.startsWith("sqlite_")) {
553
+ throw toError(
554
+ `${source}: ${where}.name "${name}" starts with "sqlite_", which SQLite reserves for its own objects. Pick another name.`
555
+ );
556
+ }
557
+ const owner = taken.get(folded);
558
+ if (owner !== void 0) {
559
+ throw toError(
560
+ `${source}: ${where}.name "${name}" is already taken by collections[${owner}]; names are compared case-insensitively because they become SQL views.`
561
+ );
562
+ }
563
+ taken.set(folded, i);
564
+ if (!Array.isArray(entry.paths) || entry.paths.length === 0 || entry.paths.some((p) => !isNonEmptyString(p))) {
565
+ throw toError(
566
+ `${source}: ${where}.paths must be a non-empty list of files, directories or globs.`
567
+ );
568
+ }
569
+ const paths = entry.paths;
570
+ let exclude = [];
571
+ if (entry.exclude !== void 0) {
572
+ if (!Array.isArray(entry.exclude) || entry.exclude.some((g) => !isNonEmptyString(g))) {
573
+ throw toError(`${source}: ${where}.exclude must be a list of globs.`);
574
+ }
575
+ exclude = entry.exclude;
576
+ }
577
+ let url;
578
+ if (entry.url !== void 0) {
579
+ if (typeof entry.url !== "string" || !isHttpUrl(entry.url)) {
580
+ throw toError(`${source}: ${where}.url must be an http(s) URL.`);
581
+ }
582
+ url = entry.url;
583
+ }
584
+ let externalMetadata = [];
585
+ if (entry.externalMetadata !== void 0) {
586
+ if (!Array.isArray(entry.externalMetadata)) {
587
+ throw toError(`${source}: ${where}.externalMetadata must be a list.`);
588
+ }
589
+ externalMetadata = parseExternalMetadata(
590
+ entry.externalMetadata,
591
+ where,
592
+ source,
593
+ toError
594
+ );
595
+ }
596
+ return {
597
+ name,
598
+ paths,
599
+ exclude,
600
+ externalMetadata,
601
+ ...url === void 0 ? {} : { url }
602
+ };
603
+ });
604
+ }
605
+ function isHttpUrl(value) {
606
+ let parsed;
607
+ try {
608
+ parsed = new URL(value);
609
+ } catch {
610
+ return false;
611
+ }
612
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
613
+ }
614
+ function parseExternalMetadata(raw, collectionWhere, source, toError) {
615
+ const owners = /* @__PURE__ */ new Map();
616
+ const patterns = /* @__PURE__ */ new Map();
617
+ let keyless;
618
+ const parsedEntries = raw.map((entry, i) => {
619
+ const where = `${collectionWhere}.externalMetadata[${i}]`;
620
+ if (!isMapping2(entry)) {
621
+ throw toError(`${source}: ${where} must be a mapping.`);
622
+ }
623
+ rejectUnknownKeys(entry, EXTERNAL_METADATA_KEYS, where, source, toError);
624
+ if (!isNonEmptyString(entry.file)) {
625
+ throw toError(
626
+ `${source}: ${where}.file must be a non-empty string naming the manifest, relative to the config file.`
627
+ );
628
+ }
629
+ const placeholderProblem = pagePlaceholderProblem(entry.file);
630
+ if (placeholderProblem !== null) {
631
+ throw toError(`${source}: ${where}.file ${placeholderProblem}`);
632
+ }
633
+ const perPage = hasPagePlaceholder(entry.file);
634
+ if (perPage && (posix2.isAbsolute(entry.file) || win32.isAbsolute(entry.file))) {
635
+ throw toError(
636
+ `${source}: ${where}.file uses ${PAGE_PLACEHOLDER} in an absolute path, so every manifest would sit outside the repository. Make it relative to the config file.`
637
+ );
638
+ }
639
+ if (entry.keys !== void 0 && (!Array.isArray(entry.keys) || entry.keys.length === 0 || entry.keys.some((k) => !isNonEmptyString(k)))) {
640
+ throw toError(
641
+ `${source}: ${where}.keys must be a non-empty list of key names.`
642
+ );
643
+ }
644
+ if (entry.keys === void 0) {
645
+ if (keyless !== void 0) {
646
+ throw toError(
647
+ `${source}: ${collectionWhere}.externalMetadata: entries ${String(keyless)} and ${String(i)} both omit keys; at most one manifest may own the external-marked fields`
648
+ );
649
+ }
650
+ keyless = i;
651
+ }
652
+ const isUrl = isUrlRef(entry.file);
653
+ if (isUrl) {
654
+ const problem = externalMetadataUrlProblem(entry.file);
655
+ if (problem !== null) {
656
+ throw toError(`${source}: ${where}.file ${problem}.`);
657
+ }
658
+ if (perPage) {
659
+ throw toError(
660
+ `${source}: ${where}.file uses ${PAGE_PLACEHOLDER} in a URL. A placeholder names a file this run can write, and a URL is not one.`
661
+ );
662
+ }
663
+ }
664
+ if (entry.tokenEnv !== void 0) {
665
+ if (!isNonEmptyString(entry.tokenEnv)) {
666
+ throw toError(
667
+ `${source}: ${where}.tokenEnv must be the name of an environment variable.`
668
+ );
669
+ }
670
+ if (!isUrl) {
671
+ throw toError(
672
+ `${source}: ${where}.tokenEnv is set, but "file" is a path, so no request is made and the token would go nowhere. Remove it, or make "file" a URL.`
673
+ );
674
+ }
675
+ }
676
+ const keys = entry.keys === void 0 ? void 0 : entry.keys;
677
+ if (entry.join !== void 0) {
678
+ if (!isNonEmptyString(entry.join)) {
679
+ throw toError(
680
+ `${source}: ${where}.join must be "path" or the name of a top-level frontmatter field.`
681
+ );
682
+ }
683
+ if (entry.join === SCHEMA_KEY) {
684
+ throw toError(`${source}: ${where}.join may not be "${SCHEMA_KEY}".`);
685
+ }
686
+ if (keys?.includes(entry.join) === true) {
687
+ throw toError(
688
+ `${source}: ${where}.join names "${entry.join}", which the same entry owns \u2014 the value that selects an entry cannot come from the entry.`
689
+ );
690
+ }
691
+ }
692
+ if (perPage && typeof entry.join === "string" && entry.join !== PATH_JOIN) {
693
+ throw toError(
694
+ `${source}: ${where}.file uses ${PAGE_PLACEHOLDER}, and join is "${entry.join}". A per-page manifest is found by path, so it cannot be keyed by a field. Remove ${PAGE_PLACEHOLDER}, or remove join.`
695
+ );
696
+ }
697
+ const seen = /* @__PURE__ */ new Set();
698
+ for (const key of keys ?? []) {
699
+ if (key === SCHEMA_KEY) {
700
+ throw toError(
701
+ `${source}: ${where}.keys may not include "${SCHEMA_KEY}" \u2014 a manifest never chooses the schema a document is judged by; use "overrides".`
702
+ );
703
+ }
704
+ if (seen.has(key)) {
705
+ throw toError(`${source}: ${where}.keys lists "${key}" twice.`);
706
+ }
707
+ seen.add(key);
708
+ const owner = owners.get(key);
709
+ if (owner !== void 0) {
710
+ throw toError(
711
+ `${source}: ${where}.keys claims "${key}", which externalMetadata[${owner}] already owns \u2014 a key has exactly one manifest in a collection.`
712
+ );
713
+ }
714
+ owners.set(key, i);
715
+ }
716
+ if (perPage) {
717
+ const pattern = normalizeManifestPattern(entry.file);
718
+ const first = patterns.get(pattern);
719
+ if (first !== void 0) {
720
+ throw toError(
721
+ `${source}: ${where}.file resolves to the same manifest as externalMetadata[${String(first)}].file for every page. Give each entry its own file name.`
722
+ );
723
+ }
724
+ patterns.set(pattern, i);
725
+ }
726
+ const parsed = {
727
+ file: entry.file,
728
+ ...keys === void 0 ? {} : { keys },
729
+ ...typeof entry.tokenEnv === "string" ? { tokenEnv: entry.tokenEnv } : {},
730
+ ...typeof entry.join === "string" ? { join: entry.join } : {}
731
+ };
732
+ origins.set(parsed, { source, where });
733
+ return parsed;
734
+ });
735
+ for (const manifest of parsedEntries) {
736
+ if (manifest.keys === void 0) declaredNever.set(manifest, neverOwnedIn(parsedEntries, manifest));
737
+ }
738
+ return parsedEntries;
739
+ }
740
+ function pagePlaceholderProblem(file) {
741
+ for (const match of file.matchAll(/\{[^{}]*\}/g)) {
742
+ if (match[0] !== PAGE_PLACEHOLDER) {
743
+ return `uses "${match[0]}", which is not a placeholder. The only placeholder is ${PAGE_PLACEHOLDER}.`;
744
+ }
745
+ }
746
+ const tokens = file.split(PAGE_PLACEHOLDER);
747
+ const stray = /[{}]/.exec(tokens.join(""));
748
+ if (stray !== null) {
749
+ return `contains "${stray[0]}" outside a ${PAGE_PLACEHOLDER} placeholder. Remove it, or write ${PAGE_PLACEHOLDER}.`;
750
+ }
751
+ if (tokens.length > 2) {
752
+ return `uses ${PAGE_PLACEHOLDER} twice. One manifest names one page.`;
753
+ }
754
+ return null;
755
+ }
756
+ var origins = /* @__PURE__ */ new WeakMap();
757
+ function externalMetadataOrigin(entry) {
758
+ return origins.get(entry);
759
+ }
760
+ function ownsKey(collection, manifest, key, marked) {
761
+ if (manifest.keys !== void 0) return manifest.keys.includes(key);
762
+ if (marked === void 0 || !marked.has(key)) return false;
763
+ return !neverOwned(collection, manifest).has(key);
764
+ }
765
+ function neverOwned(collection, manifest) {
766
+ return declaredNever.get(manifest) ?? neverOwnedIn(collection.externalMetadata, manifest);
767
+ }
768
+ function neverOwnedIn(manifests, manifest) {
769
+ const out = /* @__PURE__ */ new Set([SCHEMA_KEY]);
770
+ if (manifest.join !== void 0 && manifest.join !== PATH_JOIN) out.add(manifest.join);
771
+ for (const other of manifests) {
772
+ if (other === manifest) continue;
773
+ for (const key of other.keys ?? []) out.add(key);
774
+ }
775
+ return out;
776
+ }
777
+ var declaredNever = /* @__PURE__ */ new WeakMap();
778
+ function selectCollections(collections, names, source, toError) {
779
+ if (names === void 0 || names.length === 0) return [...collections];
780
+ const wanted = new Set(names);
781
+ for (const name of wanted) {
782
+ if (!collections.some((c) => c.name === name)) {
783
+ const configured = collections.length === 0 ? "(none)" : collections.map((c) => c.name).join(", ");
784
+ const nearMiss = collections.find(
785
+ (c) => c.name.toLowerCase() === name.toLowerCase()
786
+ );
787
+ const hint = nearMiss === void 0 ? "" : ` Names are case-sensitive; did you mean "${nearMiss.name}"?`;
788
+ throw toError(
789
+ `no collection named "${name}" in ${source}. Configured: ${configured}.${hint}`
790
+ );
791
+ }
792
+ }
793
+ return collections.filter((c) => wanted.has(c.name));
794
+ }
795
+ function isMember(collection, relPath) {
796
+ if (relPath === STDIN) return false;
797
+ if (relPath.startsWith("../")) return false;
798
+ return matchesCollectionPaths(collection, relPath);
799
+ }
800
+ function matchesCollectionPaths(collection, relPath) {
801
+ const matched = collection.paths.some((entry) => {
802
+ const normalized = normalizeEntry(entry);
803
+ if (normalized === "") return false;
804
+ if (isGlob(normalized)) return matchesFileGlob(relPath, normalized);
805
+ return relPath === normalized || relPath.startsWith(`${normalized}/`);
806
+ });
807
+ if (!matched) return false;
808
+ return !collection.exclude.some((glob) => matchesFileGlob(relPath, glob));
809
+ }
810
+ function normalizeEntry(entry) {
811
+ const posix3 = entry.replace(/\\/g, "/").replace(/^\.\//, "");
812
+ return posix3.endsWith("/") ? posix3.slice(0, -1) : posix3;
813
+ }
814
+ function isGlob(entry) {
815
+ return picomatch2.scan(entry).isGlob;
816
+ }
817
+
818
+ // src/shared/tools.ts
819
+ import { isAbsolute as isAbsolute2, resolve as resolve3 } from "node:path";
820
+ var TOOL_KEYS = ["vale", "dita-ot"];
821
+ var VALE_KEYS = ["config"];
822
+ var DITA_OT_KEYS = ["home"];
823
+ function isMapping3(value) {
824
+ return typeof value === "object" && value !== null && !Array.isArray(value);
825
+ }
826
+ function rejectUnknownKeys2(raw, allowed, where, source, toError) {
827
+ for (const key of Object.keys(raw)) {
828
+ if (!allowed.includes(key)) {
829
+ throw toError(
830
+ `${source}: ${where} has unknown key "${key}". Supported keys: ${allowed.join(", ")}.`
831
+ );
832
+ }
833
+ }
834
+ }
835
+ function parseTools(raw, source, toError) {
836
+ if (raw === null || raw === void 0) return {};
837
+ if (!isMapping3(raw)) {
838
+ throw toError(`${source}: "tools" must be a mapping.`);
839
+ }
840
+ rejectUnknownKeys2(raw, TOOL_KEYS, "tools", source, toError);
841
+ const tools = {};
842
+ if (Object.hasOwn(raw, "vale")) {
843
+ tools.vale = parseVale(raw["vale"], source, toError);
844
+ }
845
+ if (Object.hasOwn(raw, "dita-ot")) {
846
+ tools["dita-ot"] = parseDitaOt(raw["dita-ot"], source, toError);
847
+ }
848
+ return tools;
849
+ }
850
+ function parseVale(raw, source, toError) {
851
+ if (!isMapping3(raw)) {
852
+ throw toError(`${source}: tools.vale must be a mapping.`);
853
+ }
854
+ rejectUnknownKeys2(raw, VALE_KEYS, "tools.vale", source, toError);
855
+ const vale = {};
856
+ if (Object.hasOwn(raw, "config")) {
857
+ const config = raw["config"];
858
+ if (typeof config !== "string" || config.trim() === "") {
859
+ throw toError(`${source}: tools.vale.config must be a non-empty string.`);
860
+ }
861
+ vale.config = config;
862
+ }
863
+ return vale;
864
+ }
865
+ function parseDitaOt(raw, source, toError) {
866
+ if (!isMapping3(raw)) {
867
+ throw toError(`${source}: tools.dita-ot must be a mapping.`);
868
+ }
869
+ rejectUnknownKeys2(raw, DITA_OT_KEYS, "tools.dita-ot", source, toError);
870
+ const ditaOt = {};
871
+ if (Object.hasOwn(raw, "home")) {
872
+ const home = raw["home"];
873
+ if (typeof home !== "string" || home.trim() === "") {
874
+ throw toError(`${source}: tools.dita-ot.home must be a non-empty string.`);
875
+ }
876
+ ditaOt.home = home;
877
+ }
878
+ return ditaOt;
879
+ }
880
+ function valeConfigPath(tools, configDir) {
881
+ const config = tools.vale?.config;
882
+ if (config === void 0) return void 0;
883
+ return isAbsolute2(config) ? config : resolve3(configDir, config);
884
+ }
885
+ function ditaOtHome(tools, configDir) {
886
+ const home = tools["dita-ot"]?.home;
887
+ if (home === void 0) return void 0;
888
+ return isAbsolute2(home) ? home : resolve3(configDir, home);
889
+ }
890
+
891
+ // src/shared/config-file.ts
892
+ import { readFileSync as readFileSync2 } from "node:fs";
893
+ import { basename, dirname as dirname2, join as join2, relative as relative2, resolve as resolve5 } from "node:path";
894
+ import { parse as parseYaml } from "yaml";
895
+
896
+ // src/shared/git-root.ts
897
+ import { existsSync } from "node:fs";
898
+ import { dirname, join, resolve as resolve4 } from "node:path";
899
+ function findGitRoot(cwd) {
900
+ let dir = resolve4(cwd);
901
+ for (; ; ) {
902
+ if (existsSync(join(dir, ".git"))) return dir;
903
+ const parent = dirname(dir);
904
+ if (parent === dir) return null;
905
+ dir = parent;
906
+ }
907
+ }
908
+ function searchPath(cwd) {
909
+ const start = resolve4(cwd);
910
+ const root = findGitRoot(start);
911
+ if (root === null) return [start];
912
+ const chain = [];
913
+ let dir = start;
914
+ for (; ; ) {
915
+ chain.push(dir);
916
+ if (dir === root) return chain;
917
+ const parent = dirname(dir);
918
+ if (parent === dir) return chain;
919
+ dir = parent;
920
+ }
921
+ }
922
+
923
+ // src/shared/config-file.ts
924
+ var FAMILY_CONFIG_NAMES = [
925
+ "manni.config.yaml",
926
+ "manni.config.yml"
927
+ ];
928
+ var ENCRYPTION_KEY_FIELD = "encryptionKey";
929
+ var ENCRYPTION_KEY_PREVIOUS_FIELD = "encryptionKeyPrevious";
930
+ function readDocument(path, source, toError) {
931
+ let text;
932
+ try {
933
+ text = readFileSync2(path, "utf8");
934
+ } catch {
935
+ return null;
936
+ }
937
+ return { text, doc: parseMapping(text, source, toError) };
938
+ }
939
+ function parseMapping(text, source, toError) {
940
+ let raw;
941
+ try {
942
+ raw = parseYaml(text);
943
+ } catch (err) {
944
+ throw toError(`${source}: invalid YAML: ${errorMessage(err)}`);
945
+ }
946
+ if (raw == null) return null;
947
+ if (typeof raw !== "object" || Array.isArray(raw)) {
948
+ throw toError(`${source}: top level must be a mapping.`);
949
+ }
950
+ return raw;
951
+ }
952
+ var COLLECTIONS_KEY = "collections";
953
+ var TOOLS_KEY = "tools";
954
+ var FAMILY_KEYS = [
955
+ COLLECTIONS_KEY,
956
+ TOOLS_KEY,
957
+ ENCRYPTION_KEY_FIELD,
958
+ PROVIDERS_KEY
959
+ ];
960
+ function slice(document, opts) {
961
+ const { doc } = document;
962
+ if (doc !== null && Object.hasOwn(doc, opts.section)) {
963
+ return { value: doc[opts.section] ?? null, wrapped: true };
964
+ }
965
+ if (doc !== null && FAMILY_KEYS.some((key) => Object.hasOwn(doc, key))) {
966
+ return { value: null, wrapped: true };
967
+ }
968
+ return null;
969
+ }
970
+ function collectionsOf(document, source, toError) {
971
+ const { doc } = document;
972
+ if (doc === null || !Object.hasOwn(doc, COLLECTIONS_KEY)) return [];
973
+ return parseCollections(doc[COLLECTIONS_KEY], source, toError);
974
+ }
975
+ function toolsOf(document, source, toError) {
976
+ const { doc } = document;
977
+ if (doc === null || !Object.hasOwn(doc, TOOLS_KEY)) return {};
978
+ return parseTools(doc[TOOLS_KEY], source, toError);
979
+ }
980
+ function encryptionKeyOf(document, source, toError) {
981
+ const { doc } = document;
982
+ if (doc === null) return {};
983
+ const keys = {};
984
+ if (Object.hasOwn(doc, ENCRYPTION_KEY_FIELD)) {
985
+ const value = doc[ENCRYPTION_KEY_FIELD];
986
+ if (!isValidEncryptionKey(value)) {
987
+ throw toError(
988
+ `${source}: "${ENCRYPTION_KEY_FIELD}" must be at least 32 hex or base64url characters. Run \`manni key set\` to generate one.`
989
+ );
990
+ }
991
+ keys.encryptionKey = value;
992
+ }
993
+ if (Object.hasOwn(doc, ENCRYPTION_KEY_PREVIOUS_FIELD)) {
994
+ const value = doc[ENCRYPTION_KEY_PREVIOUS_FIELD];
995
+ if (!isValidEncryptionKey(value)) {
996
+ throw toError(
997
+ `${source}: "${ENCRYPTION_KEY_PREVIOUS_FIELD}" must be at least 32 hex or base64url characters.`
998
+ );
999
+ }
1000
+ keys.encryptionKeyPrevious = value;
1001
+ }
1002
+ return keys;
1003
+ }
1004
+ function providersOf(document, source, dir, toError) {
1005
+ const { doc } = document;
1006
+ if (doc === null || !Object.hasOwn(doc, PROVIDERS_KEY)) return {};
1007
+ return { providers: parseProviders(doc[PROVIDERS_KEY], source, dir, toError) };
1008
+ }
1009
+ function tolerantKeysOf(document) {
1010
+ const key = document.doc?.[ENCRYPTION_KEY_FIELD];
1011
+ const previous = document.doc?.[ENCRYPTION_KEY_PREVIOUS_FIELD];
1012
+ return {
1013
+ ...isValidEncryptionKey(key) ? { encryptionKey: key } : {},
1014
+ ...isValidEncryptionKey(previous) ? { encryptionKeyPrevious: previous } : {}
1015
+ };
1016
+ }
1017
+ function relativeSource(cwd, path) {
1018
+ return relative2(cwd, path).replace(/\\/g, "/");
1019
+ }
1020
+ function findConfigFileSync(cwd, opts) {
1021
+ const start = resolve5(cwd);
1022
+ for (const dir of searchPath(start)) {
1023
+ for (const name of FAMILY_CONFIG_NAMES) {
1024
+ const path = join2(dir, name);
1025
+ const source = relativeSource(start, path);
1026
+ const document = readDocument(path, source, opts.toError);
1027
+ if (document === null) continue;
1028
+ const found = slice(document, opts);
1029
+ if (found === null) continue;
1030
+ return {
1031
+ path,
1032
+ dir,
1033
+ source,
1034
+ text: document.text,
1035
+ kind: "manni",
1036
+ collections: collectionsOf(document, source, opts.toError),
1037
+ tools: toolsOf(document, source, opts.toError),
1038
+ ...encryptionKeyOf(document, source, opts.toError),
1039
+ ...providersOf(document, source, dir, opts.toError),
1040
+ ...found
1041
+ };
1042
+ }
1043
+ for (const name of opts.legacyNames) {
1044
+ const path = join2(dir, name);
1045
+ const source = relativeSource(start, path);
1046
+ const document = readDocument(path, source, opts.toError);
1047
+ if (document === null) continue;
1048
+ warn(
1049
+ `"${name}" is a deprecated config file name. Move its keys under \`${opts.section}:\` in "${FAMILY_CONFIG_NAMES[0] ?? "manni.config.yaml"}", and its paths, exclude and sidecars keys to a top-level collections: list, where sidecars becomes externalMetadata.`
1050
+ );
1051
+ return {
1052
+ path,
1053
+ dir,
1054
+ source,
1055
+ text: document.text,
1056
+ value: document.doc,
1057
+ wrapped: false,
1058
+ kind: "legacy",
1059
+ collections: [],
1060
+ tools: {}
1061
+ };
1062
+ }
1063
+ }
1064
+ return null;
1065
+ }
1066
+ function readConfigFileSync(explicitPath, cwd, opts) {
1067
+ const path = resolve5(cwd, explicitPath);
1068
+ const document = readDocument(path, explicitPath, opts.toError);
1069
+ if (document === null) {
1070
+ throw opts.toError(`Config file not found: "${explicitPath}".`);
1071
+ }
1072
+ const found = slice(document, opts) ?? {
1073
+ value: document.doc,
1074
+ wrapped: false
1075
+ };
1076
+ return {
1077
+ path,
1078
+ dir: dirname2(path),
1079
+ source: explicitPath,
1080
+ text: document.text,
1081
+ kind: "explicit",
1082
+ collections: collectionsOf(document, explicitPath, opts.toError),
1083
+ tools: toolsOf(document, explicitPath, opts.toError),
1084
+ // An unwrapped document carries no family key (else it would be wrapped),
1085
+ // so this only ever reads a family file's key.
1086
+ ...encryptionKeyOf(document, explicitPath, opts.toError),
1087
+ ...providersOf(document, explicitPath, dirname2(path), opts.toError),
1088
+ ...found
1089
+ };
1090
+ }
1091
+ function findConfigFile(cwd, opts) {
1092
+ return Promise.resolve().then(() => findConfigFileSync(cwd, opts));
1093
+ }
1094
+ function readConfigFile(explicitPath, cwd, opts) {
1095
+ return Promise.resolve().then(
1096
+ () => readConfigFileSync(explicitPath, cwd, opts)
1097
+ );
1098
+ }
1099
+ function findFamilyConfigFile(cwd, toError) {
1100
+ return Promise.resolve().then(() => findFamilyConfigFileSync(cwd, toError));
1101
+ }
1102
+ function findFamilyConfigFileSync(cwd, toError) {
1103
+ const start = resolve5(cwd);
1104
+ for (const dir of searchPath(start)) {
1105
+ for (const name of FAMILY_CONFIG_NAMES) {
1106
+ const path = join2(dir, name);
1107
+ const source = relativeSource(start, path);
1108
+ const document = readDocument(path, source, toError);
1109
+ if (document === null) continue;
1110
+ return {
1111
+ path,
1112
+ dir,
1113
+ source,
1114
+ text: document.text,
1115
+ value: null,
1116
+ wrapped: true,
1117
+ kind: "manni",
1118
+ collections: collectionsOf(document, source, toError),
1119
+ tools: toolsOf(document, source, toError),
1120
+ ...tolerantKeysOf(document)
1121
+ };
1122
+ }
1123
+ }
1124
+ return null;
1125
+ }
1126
+ function readFamilyConfigFile(explicitPath, cwd, toError) {
1127
+ return Promise.resolve().then(
1128
+ () => readFamilyConfigFileSync(explicitPath, cwd, toError)
1129
+ );
1130
+ }
1131
+ function readFamilyConfigFileSync(explicitPath, cwd, toError) {
1132
+ const path = resolve5(cwd, explicitPath);
1133
+ const document = readDocument(path, explicitPath, toError);
1134
+ if (document === null) {
1135
+ throw toError(`Config file not found: "${explicitPath}".`);
1136
+ }
1137
+ const { doc } = document;
1138
+ const named = FAMILY_CONFIG_NAMES.includes(basename(path));
1139
+ const family = doc === null || named || FAMILY_KEYS.some((key) => Object.hasOwn(doc, key));
1140
+ return {
1141
+ path,
1142
+ dir: dirname2(path),
1143
+ source: explicitPath,
1144
+ text: document.text,
1145
+ value: family ? null : doc,
1146
+ wrapped: family,
1147
+ kind: "explicit",
1148
+ collections: collectionsOf(document, explicitPath, toError),
1149
+ tools: toolsOf(document, explicitPath, toError),
1150
+ ...tolerantKeysOf(document)
1151
+ };
1152
+ }
1153
+
1154
+ export {
1155
+ warn,
1156
+ notice,
1157
+ noticeOnce,
1158
+ assertKnownProvider,
1159
+ assertModelHasProvider,
1160
+ PROVIDERS_KEY,
1161
+ parseProviders,
1162
+ LOCAL_FLAG_HELP,
1163
+ assertLocalFlag,
1164
+ selectProvider,
1165
+ localNotice,
1166
+ providerSpecFor,
1167
+ resolveIdentity,
1168
+ isEncryptedValue,
1169
+ isValidEncryptionKey,
1170
+ generateEncryptionKey,
1171
+ encryptValue,
1172
+ decryptValue,
1173
+ KEYED_PIN_PREFIX,
1174
+ keyedPin,
1175
+ matchesFileGlob,
1176
+ PAGE_PLACEHOLDER,
1177
+ hasPagePlaceholder,
1178
+ pageManifestPath,
1179
+ findPageManifests,
1180
+ sameFile,
1181
+ externalMetadataUrlProblem,
1182
+ parseCollections,
1183
+ externalMetadataOrigin,
1184
+ ownsKey,
1185
+ neverOwned,
1186
+ selectCollections,
1187
+ isMember,
1188
+ matchesCollectionPaths,
1189
+ findGitRoot,
1190
+ valeConfigPath,
1191
+ ditaOtHome,
1192
+ FAMILY_CONFIG_NAMES,
1193
+ ENCRYPTION_KEY_FIELD,
1194
+ ENCRYPTION_KEY_PREVIOUS_FIELD,
1195
+ findConfigFileSync,
1196
+ readConfigFileSync,
1197
+ findConfigFile,
1198
+ readConfigFile,
1199
+ findFamilyConfigFile,
1200
+ readFamilyConfigFile
1201
+ };
1202
+ //# sourceMappingURL=chunk-Q35YIDBV.js.map