@event4u/agent-config 9.10.0 → 9.12.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 (142) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +111 -0
  3. package/README.md +20 -16
  4. package/agents/templates/.ai-council.yml.example +481 -0
  5. package/agents/templates/.ai-video.xml.example +247 -0
  6. package/dist/agent-src/commands/fix/ci.md +29 -0
  7. package/dist/agent-src/commands/pr/create.md +60 -0
  8. package/dist/agent-src/contexts/authority/kernel-rule-edits.md +18 -2
  9. package/dist/agent-src/rules/design-fidelity.md +2 -2
  10. package/dist/agent-src/skills/ai-code-blindspots/SKILL.md +1 -0
  11. package/dist/agent-src/skills/design-intelligence/SKILL.md +27 -1
  12. package/dist/agent-src/skills/design-intelligence/data/font-pairings-reference.csv +74 -74
  13. package/dist/agent-src/skills/design-intelligence/data/manifest.json +2 -0
  14. package/dist/agent-src/skills/design-tokens/scripts/tokens.ts +0 -7
  15. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +15 -2
  16. package/dist/agent-src/skills/fe-design/SKILL.md +10 -0
  17. package/dist/agent-src/skills/typography-system/SKILL.md +55 -11
  18. package/dist/agent-src/skills/ui-apply-generic/SKILL.md +180 -0
  19. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  20. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +34 -30
  21. package/dist/agent-src/templates/scripts/work_engine/directives/ui/design.ts +88 -33
  22. package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +23 -0
  23. package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +24 -1
  24. package/dist/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +332 -0
  25. package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +449 -11
  26. package/dist/agent-src/templates/scripts/work_engine/state.ts +21 -4
  27. package/dist/cli/initRouting.js +74 -11
  28. package/dist/cli/initRouting.js.map +1 -1
  29. package/dist/cli/main.js +14 -3
  30. package/dist/cli/main.js.map +1 -1
  31. package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
  32. package/dist/cli-delegate/chunk-736JCWLM.js +1104 -0
  33. package/dist/cli-delegate/chunk-7HQ52DHU.js +74 -0
  34. package/dist/cli-delegate/chunk-A3HJ6NTI.js +1084 -0
  35. package/dist/cli-delegate/chunk-EJYIGZ3T.js +125 -0
  36. package/dist/cli-delegate/chunk-FTAISGDQ.js +117 -0
  37. package/dist/cli-delegate/chunk-GME3Q44L.js +82 -0
  38. package/dist/cli-delegate/chunk-GRRPEPRN.js +336 -0
  39. package/dist/cli-delegate/chunk-HJ6DAJHR.js +502 -0
  40. package/dist/cli-delegate/chunk-HYWSRNUX.js +179 -0
  41. package/dist/cli-delegate/chunk-J55WI3RR.js +47 -0
  42. package/dist/cli-delegate/chunk-MCJYCQPF.js +35 -0
  43. package/dist/cli-delegate/chunk-NDJLNC6Q.js +169 -0
  44. package/dist/cli-delegate/chunk-OMIVVCG2.js +26 -0
  45. package/dist/cli-delegate/chunk-OY53J6II.js +7336 -0
  46. package/dist/cli-delegate/chunk-QFKQLQ7X.js +10835 -0
  47. package/dist/cli-delegate/chunk-TEGJU2JX.js +6908 -0
  48. package/dist/cli-delegate/chunk-TJIAM5FW.js +133 -0
  49. package/dist/cli-delegate/chunk-XX6TOPD7.js +715 -0
  50. package/dist/cli-delegate/cmd_analyze_session.js +294 -0
  51. package/dist/cli-delegate/cmd_conformance.js +643 -0
  52. package/dist/cli-delegate/cmd_converge.js +326 -0
  53. package/dist/cli-delegate/cmd_doctor.js +142 -0
  54. package/dist/cli-delegate/cmd_explain.js +2576 -0
  55. package/dist/cli-delegate/cmd_export.js +298 -0
  56. package/dist/cli-delegate/cmd_fleet.js +333 -0
  57. package/dist/cli-delegate/cmd_memory_get.js +3878 -0
  58. package/dist/cli-delegate/cmd_migrate.js +11 -0
  59. package/dist/cli-delegate/cmd_preflight.js +153 -0
  60. package/dist/cli-delegate/cmd_prune.js +446 -0
  61. package/dist/cli-delegate/cmd_refresh.js +28 -0
  62. package/dist/cli-delegate/cmd_settings_check.js +574 -0
  63. package/dist/cli-delegate/cmd_settings_migrate.js +225 -0
  64. package/dist/cli-delegate/cmd_sync.js +266 -0
  65. package/dist/cli-delegate/cmd_uninstall.js +657 -0
  66. package/dist/cli-delegate/cmd_update.js +425 -0
  67. package/dist/cli-delegate/cmd_upgrade.js +444 -0
  68. package/dist/cli-delegate/cmd_validate.js +307 -0
  69. package/dist/cli-delegate/cmd_versions.js +370 -0
  70. package/dist/discovery/deprecation-report.md +1 -1
  71. package/dist/discovery/discovery-manifest.json +45 -16
  72. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  73. package/dist/discovery/discovery-manifest.summary.md +3 -3
  74. package/dist/discovery/orphan-report.md +1 -1
  75. package/dist/discovery/packs.json +13 -7
  76. package/dist/discovery/trust-report.md +2 -2
  77. package/dist/discovery/workspaces.json +6 -4
  78. package/dist/install/emit_host_rules_cli.js +2 -1
  79. package/dist/install/emit_host_rules_cli.js.map +1 -1
  80. package/dist/install/install.mjs +634 -409
  81. package/dist/install/ruleInScope.js +123 -0
  82. package/dist/install/ruleInScope.js.map +1 -0
  83. package/dist/install/rule_scope.js +7 -3
  84. package/dist/install/rule_scope.js.map +1 -1
  85. package/dist/install/toolDetection.js +40 -12
  86. package/dist/install/toolDetection.js.map +1 -1
  87. package/dist/install/wizard-plan.js +12 -3
  88. package/dist/install/wizard-plan.js.map +1 -1
  89. package/dist/mcp/registry-manifest.json +3 -3
  90. package/dist/mcp/server.json +2 -2
  91. package/dist/mcp/server.mjs +1553 -2728
  92. package/dist/router.json +1 -1
  93. package/dist/scripts/condense.js +6 -22
  94. package/dist/scripts/condense.js.map +1 -1
  95. package/docs/guidelines/design-fidelity-mechanics.md +14 -0
  96. package/package.json +4 -2
  97. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +34 -30
  98. package/src/agent-src/templates/scripts/work_engine/directives/ui/design.ts +88 -33
  99. package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +23 -0
  100. package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +24 -1
  101. package/src/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +332 -0
  102. package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +449 -11
  103. package/src/agent-src/templates/scripts/work_engine/state.ts +21 -4
  104. package/src/config/discovery/packs.yml +7 -0
  105. package/src/config/evaluator-budgets.json +1 -1
  106. package/src/config/pack-size-budget.json +27 -0
  107. package/src/install/emit_host_rules_cli.ts +2 -1
  108. package/src/install/ruleInScope.ts +133 -0
  109. package/src/install/rule_scope.ts +7 -3
  110. package/src/install/toolDetection.ts +39 -8
  111. package/src/install/wizard-plan.ts +13 -3
  112. package/src/scripts/_cli/cmd_conformance.ts +2 -1
  113. package/src/scripts/_cli/cmd_converge.ts +2 -1
  114. package/src/scripts/_cli/cmd_doctor.ts +214 -13
  115. package/src/scripts/_cli/cmd_export.ts +2 -1
  116. package/src/scripts/_cli/cmd_fleet.ts +2 -1
  117. package/src/scripts/_cli/cmd_migrate.ts +2 -1
  118. package/src/scripts/_cli/cmd_preflight.ts +2 -1
  119. package/src/scripts/_cli/cmd_refresh.ts +2 -1
  120. package/src/scripts/_cli/cmd_update.ts +2 -1
  121. package/src/scripts/_cli/cmd_upgrade.ts +2 -1
  122. package/src/scripts/_cli/cmd_versions.ts +2 -1
  123. package/src/scripts/_cli/detection_report.ts +480 -0
  124. package/src/scripts/_dispatch.bash +52 -14
  125. package/src/scripts/_lib/changelog_eras.ts +103 -9
  126. package/src/scripts/_lib/environment_detector.ts +441 -0
  127. package/src/scripts/_lib/global_deploy_inventory.ts +38 -2
  128. package/src/scripts/_lib/package_root.ts +72 -0
  129. package/src/scripts/ai_council/config.ts +15 -7
  130. package/src/scripts/ai_council/modes.ts +68 -2
  131. package/src/scripts/ai_council/transport_resolver.ts +331 -0
  132. package/src/scripts/check_bundle_path_leakage.ts +10 -4
  133. package/src/scripts/check_dependency_floors.ts +153 -0
  134. package/src/scripts/check_installer_import_purity.ts +295 -0
  135. package/src/scripts/check_pack_size.ts +188 -0
  136. package/src/scripts/condense.ts +7 -28
  137. package/src/scripts/council_cli.ts +6 -2
  138. package/src/scripts/evaluator_umbrella.sh +34 -1
  139. package/src/scripts/install-hooks.sh +36 -4
  140. package/src/scripts/install.ts +210 -5
  141. package/src/scripts/lint_eval_fixture_citations.ts +113 -0
  142. package/src/scripts/lint_ui_stack_bundles.ts +201 -0
@@ -0,0 +1,3878 @@
1
+ #!/usr/bin/env tsx
2
+ import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
3
+ import {
4
+ require_dist
5
+ } from "./chunk-OY53J6II.js";
6
+ import {
7
+ __toESM
8
+ } from "./chunk-J55WI3RR.js";
9
+
10
+ // src/scripts/_cli/cmd_memory_get.ts
11
+ import process6 from "node:process";
12
+ import * as fs8 from "node:fs";
13
+ import * as path8 from "node:path";
14
+ import { fileURLToPath as fileURLToPath7, pathToFileURL as pathToFileURL5 } from "node:url";
15
+
16
+ // src/scripts/memory_lookup.ts
17
+ var import_yaml2 = __toESM(require_dist(), 1);
18
+ import * as fs7 from "node:fs";
19
+ import * as path7 from "node:path";
20
+ import process5 from "node:process";
21
+ import { fileURLToPath as fileURLToPath6, pathToFileURL as pathToFileURL4 } from "node:url";
22
+ import { createRequire as createRequire2 } from "node:module";
23
+
24
+ // src/scripts/lint_hidden_unicode.ts
25
+ import * as fs2 from "node:fs";
26
+ import * as path2 from "node:path";
27
+ import { spawnSync } from "node:child_process";
28
+ import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
29
+
30
+ // src/scripts/_lib/security_lint.ts
31
+ import * as fs from "node:fs";
32
+ import * as path from "node:path";
33
+ import { fileURLToPath } from "node:url";
34
+ var _HERE = path.dirname(fileURLToPath(import.meta.url));
35
+ var ROOT = path.resolve(_HERE, "..", "..", "..");
36
+ var EXAMPLE_FENCE_LANG = "security-example";
37
+ var GUIDELINE = "docs/guidelines/agent-infra/security-lint-containment.md";
38
+ var GUIDELINE_EPILOG = `False-positive containment (fenced security-example block, confidence weighting, per-file \`security-lint: allow\` pragma \u2014 no global allowlist): see ${GUIDELINE}.`;
39
+ var DEFAULT_SCAN_ROOTS = [
40
+ "src/skills",
41
+ "src/rules",
42
+ "src/agent-src",
43
+ "src/domains",
44
+ "dist/agent-src"
45
+ ];
46
+ var _EXAMPLE_PATH = /(^|\/)(docs|evals|tests?|fixtures?)(\/|$)|example|template|sample|\/_template/i;
47
+ var _PRAGMA = /<!--\s*security-lint:\s*allow\s+([A-Za-z0-9_-]+)\s+"([^"]+)"\s*-->/g;
48
+ var _FENCE = /^(\s*)(`{3,}|~{3,})\s*([A-Za-z0-9_-]*)\s*$/;
49
+ var SEVERITY_RANK = { LOW: 1, MED: 2, HIGH: 3 };
50
+ var Finding = class {
51
+ path;
52
+ // repo-relative
53
+ line;
54
+ // 1-based; 0 = file-level
55
+ check;
56
+ // stable check id, also the pragma key
57
+ severity;
58
+ // HIGH | MED | LOW
59
+ message;
60
+ weight;
61
+ // 1.0 or 0.25
62
+ constructor(pathArg, line, check, severity, message, weight = 1) {
63
+ this.path = pathArg;
64
+ this.line = line;
65
+ this.check = check;
66
+ this.severity = severity;
67
+ this.message = message;
68
+ this.weight = weight;
69
+ }
70
+ /** A HIGH-severity, full-weight finding fails the build. */
71
+ get is_fail() {
72
+ return this.severity === "HIGH" && this.weight >= 1;
73
+ }
74
+ /** Mirror Python dataclass `__dict__` key order for `--json` parity. */
75
+ toDict() {
76
+ return {
77
+ path: this.path,
78
+ line: this.line,
79
+ check: this.check,
80
+ severity: this.severity,
81
+ message: this.message,
82
+ // float in Python → render integer-valued (1.0) as `1.0`.
83
+ weight: new PyFloat(this.weight)
84
+ };
85
+ }
86
+ };
87
+ function is_example_path(rel_path) {
88
+ return _EXAMPLE_PATH.test(rel_path);
89
+ }
90
+ function path_weight(rel_path) {
91
+ return is_example_path(rel_path) ? 0.25 : 1;
92
+ }
93
+ var ScannedFile = class {
94
+ path;
95
+ // absolute or as-given path
96
+ rel;
97
+ lines;
98
+ // per-line flags (1-based index → flag); index 0 unused
99
+ in_example_fence;
100
+ in_any_fence;
101
+ pragmas;
102
+ // check id → reason
103
+ weight;
104
+ constructor(pathArg, rel, lines, in_example_fence, in_any_fence, pragmas, weight) {
105
+ this.path = pathArg;
106
+ this.rel = rel;
107
+ this.lines = lines;
108
+ this.in_example_fence = in_example_fence;
109
+ this.in_any_fence = in_any_fence;
110
+ this.pragmas = pragmas;
111
+ this.weight = weight;
112
+ }
113
+ pragma_allows(check) {
114
+ return Object.prototype.hasOwnProperty.call(this.pragmas, check);
115
+ }
116
+ /** Yield [lineno, text] honouring the fence masks (mirrors iter_lines). */
117
+ *iter_lines(opts = {}) {
118
+ const skip_example_fence = opts.skip_example_fence ?? true;
119
+ const skip_any_fence = opts.skip_any_fence ?? false;
120
+ for (let i = 1; i <= this.lines.length; i++) {
121
+ if (skip_example_fence && this.in_example_fence[i]) {
122
+ continue;
123
+ }
124
+ if (skip_any_fence && this.in_any_fence[i]) {
125
+ continue;
126
+ }
127
+ yield [i, this.lines[i - 1]];
128
+ }
129
+ }
130
+ };
131
+ function _splitlines(text) {
132
+ const out = [];
133
+ let buf = "";
134
+ const n = text.length;
135
+ let i = 0;
136
+ while (i < n) {
137
+ const ch = text[i];
138
+ const code = text.charCodeAt(i);
139
+ if (code === 13) {
140
+ out.push(buf);
141
+ buf = "";
142
+ if (i + 1 < n && text.charCodeAt(i + 1) === 10) {
143
+ i += 2;
144
+ } else {
145
+ i += 1;
146
+ }
147
+ continue;
148
+ }
149
+ if (code === 10 || code === 11 || code === 12 || code === 28 || code === 29 || code === 30 || code === 133 || code === 8232 || code === 8233) {
150
+ out.push(buf);
151
+ buf = "";
152
+ i += 1;
153
+ continue;
154
+ }
155
+ buf += ch;
156
+ i += 1;
157
+ }
158
+ if (buf !== "") {
159
+ out.push(buf);
160
+ }
161
+ return out;
162
+ }
163
+ function scan_file(filePath) {
164
+ let rel;
165
+ if (path.isAbsolute(filePath)) {
166
+ const root = path.resolve(ROOT);
167
+ const resolved = path.resolve(filePath);
168
+ if (resolved === root || resolved.startsWith(root + path.sep)) {
169
+ rel = path.relative(root, resolved).split(path.sep).join("/");
170
+ } else {
171
+ rel = path.basename(filePath);
172
+ }
173
+ } else {
174
+ rel = filePath.split(path.sep).join("/");
175
+ }
176
+ const raw = fs.readFileSync(filePath, "utf-8");
177
+ const lines = _splitlines(raw);
178
+ const n = lines.length;
179
+ const in_example = new Array(n + 1).fill(false);
180
+ const in_any = new Array(n + 1).fill(false);
181
+ let fence_open = false;
182
+ let fence_marker = "";
183
+ let fence_is_example = false;
184
+ for (let idx = 1; idx <= n; idx++) {
185
+ const text = lines[idx - 1];
186
+ const m = _FENCE.exec(text);
187
+ if (m && !fence_open) {
188
+ fence_open = true;
189
+ fence_marker = m[2][0];
190
+ fence_is_example = m[3] === EXAMPLE_FENCE_LANG;
191
+ in_any[idx] = true;
192
+ in_example[idx] = fence_is_example;
193
+ continue;
194
+ }
195
+ if (fence_open) {
196
+ in_any[idx] = true;
197
+ in_example[idx] = fence_is_example;
198
+ const cm = _FENCE.exec(text);
199
+ if (cm && cm[2][0] === fence_marker && cm[3] === "") {
200
+ fence_open = false;
201
+ fence_is_example = false;
202
+ }
203
+ }
204
+ }
205
+ const pragmas = {};
206
+ for (const text of lines) {
207
+ _PRAGMA.lastIndex = 0;
208
+ let m;
209
+ while ((m = _PRAGMA.exec(text)) !== null) {
210
+ pragmas[m[1]] = m[2];
211
+ if (m.index === _PRAGMA.lastIndex) {
212
+ _PRAGMA.lastIndex += 1;
213
+ }
214
+ }
215
+ }
216
+ return new ScannedFile(
217
+ filePath,
218
+ rel,
219
+ lines,
220
+ in_example,
221
+ in_any,
222
+ pragmas,
223
+ path_weight(rel)
224
+ );
225
+ }
226
+ function _rglobSorted(base) {
227
+ const found = [];
228
+ const walk = (dir) => {
229
+ let entries;
230
+ try {
231
+ entries = fs.readdirSync(dir, { withFileTypes: true });
232
+ } catch {
233
+ return;
234
+ }
235
+ for (const e of entries) {
236
+ const full = path.join(dir, e.name);
237
+ found.push(full);
238
+ if (e.isDirectory()) {
239
+ walk(full);
240
+ }
241
+ }
242
+ };
243
+ walk(base);
244
+ found.sort((a, b) => _pathCmp(a, b));
245
+ return found;
246
+ }
247
+ function _pathCmp(a, b) {
248
+ const pa = a.split(path.sep);
249
+ const pb = b.split(path.sep);
250
+ const len = Math.min(pa.length, pb.length);
251
+ for (let i = 0; i < len; i++) {
252
+ if (pa[i] < pb[i]) return -1;
253
+ if (pa[i] > pb[i]) return 1;
254
+ }
255
+ if (pa.length < pb.length) return -1;
256
+ if (pa.length > pb.length) return 1;
257
+ return 0;
258
+ }
259
+ function* iter_corpus(roots = DEFAULT_SCAN_ROOTS, exts = [".md"]) {
260
+ for (const root of roots) {
261
+ const base = path.join(ROOT, root);
262
+ if (!fs.existsSync(base)) {
263
+ continue;
264
+ }
265
+ for (const p of _rglobSorted(base)) {
266
+ let st;
267
+ try {
268
+ st = fs.statSync(p);
269
+ } catch {
270
+ continue;
271
+ }
272
+ if (st.isFile() && exts.includes(path.extname(p))) {
273
+ yield scan_file(p);
274
+ }
275
+ }
276
+ }
277
+ }
278
+ function report(findings, opts) {
279
+ const check_label = opts.check_label;
280
+ if (findings.length === 0) {
281
+ process.stdout.write(`\u2705 ${check_label}: clean (${_corpus_note()}).
282
+ `);
283
+ return 0;
284
+ }
285
+ const fails = findings.filter((f) => f.is_fail);
286
+ const warns = findings.filter((f) => !f.is_fail);
287
+ const sorted = [...findings].sort((a, b) => {
288
+ const ra = -(SEVERITY_RANK[a.severity] ?? 0);
289
+ const rb = -(SEVERITY_RANK[b.severity] ?? 0);
290
+ if (ra !== rb) return ra - rb;
291
+ if (a.path < b.path) return -1;
292
+ if (a.path > b.path) return 1;
293
+ return a.line - b.line;
294
+ });
295
+ for (const f of sorted) {
296
+ const glyph = f.is_fail ? "\u{1F534}" : "\u26A0\uFE0F";
297
+ const loc = f.line ? `${f.path}:${f.line}` : f.path;
298
+ const wnote = f.weight >= 1 ? "" : ` (weight ${_pyG(f.weight)})`;
299
+ process.stdout.write(` ${glyph} [${f.severity}] ${f.check} \u2014 ${loc}${wnote}: ${f.message}
300
+ `);
301
+ }
302
+ process.stdout.write("\n");
303
+ if (fails.length > 0) {
304
+ process.stdout.write(
305
+ `\u274C ${check_label}: ${fails.length} blocking finding(s), ${warns.length} warning(s). Fix, or mark a true teaching example with a \`\`\`${EXAMPLE_FENCE_LANG} fence or a \`security-lint: allow\` pragma.
306
+ `
307
+ );
308
+ return 1;
309
+ }
310
+ process.stdout.write(`\u26A0\uFE0F ${check_label}: ${warns.length} warning(s), 0 blocking.
311
+ `);
312
+ return 0;
313
+ }
314
+ function _pyG(n) {
315
+ let s = n.toPrecision(6);
316
+ if (s.includes(".")) {
317
+ s = s.replace(/0+$/, "").replace(/\.$/, "");
318
+ }
319
+ return s;
320
+ }
321
+ function _corpus_note() {
322
+ return "scanned " + DEFAULT_SCAN_ROOTS.join(", ");
323
+ }
324
+ var PyFloat = class {
325
+ constructor(value) {
326
+ this.value = value;
327
+ }
328
+ value;
329
+ };
330
+ function _isPlainObject(v) {
331
+ return typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof PyFloat);
332
+ }
333
+ function _pyJsonStr(s) {
334
+ let out = '"';
335
+ for (const ch of s) {
336
+ const code = ch.codePointAt(0);
337
+ switch (ch) {
338
+ case '"':
339
+ out += '\\"';
340
+ break;
341
+ case "\\":
342
+ out += "\\\\";
343
+ break;
344
+ case "\n":
345
+ out += "\\n";
346
+ break;
347
+ case "\r":
348
+ out += "\\r";
349
+ break;
350
+ case " ":
351
+ out += "\\t";
352
+ break;
353
+ case "\b":
354
+ out += "\\b";
355
+ break;
356
+ case "\f":
357
+ out += "\\f";
358
+ break;
359
+ default:
360
+ if (code < 32) {
361
+ out += `\\u${code.toString(16).padStart(4, "0")}`;
362
+ } else {
363
+ out += ch;
364
+ }
365
+ }
366
+ }
367
+ return out + '"';
368
+ }
369
+ function _pyJsonFloat(n) {
370
+ if (!Number.isFinite(n)) {
371
+ if (Number.isNaN(n)) return "NaN";
372
+ return n > 0 ? "Infinity" : "-Infinity";
373
+ }
374
+ return Number.isInteger(n) ? `${n}.0` : String(n);
375
+ }
376
+ function _pyJsonScalar(value) {
377
+ if (value === null || value === void 0) return "null";
378
+ if (typeof value === "boolean") return value ? "true" : "false";
379
+ if (value instanceof PyFloat) return _pyJsonFloat(value.value);
380
+ if (typeof value === "number") {
381
+ if (!Number.isFinite(value)) {
382
+ if (Number.isNaN(value)) return "NaN";
383
+ return value > 0 ? "Infinity" : "-Infinity";
384
+ }
385
+ return String(value);
386
+ }
387
+ if (typeof value === "string") return _pyJsonStr(value);
388
+ return null;
389
+ }
390
+ function py_json_dumps_indent2(value, depth = 0) {
391
+ return _dumpIndent2(value, depth, true);
392
+ }
393
+ function _dumpIndent2(value, depth, ensureAscii) {
394
+ const scalar = ensureAscii ? _pyJsonScalarAscii(value) : _pyJsonScalar(value);
395
+ if (scalar !== null) {
396
+ return scalar;
397
+ }
398
+ const pad = " ".repeat(2 * (depth + 1));
399
+ const closePad = " ".repeat(2 * depth);
400
+ if (Array.isArray(value)) {
401
+ if (value.length === 0) return "[]";
402
+ const items = value.map((v) => pad + _dumpIndent2(v, depth + 1, ensureAscii));
403
+ return `[
404
+ ${items.join(",\n")}
405
+ ${closePad}]`;
406
+ }
407
+ if (_isPlainObject(value)) {
408
+ const keys = Object.keys(value);
409
+ if (keys.length === 0) return "{}";
410
+ const items = keys.map(
411
+ (k) => `${pad}${ensureAscii ? _pyJsonStrAscii(k) : _pyJsonStr(k)}: ${_dumpIndent2(value[k], depth + 1, ensureAscii)}`
412
+ );
413
+ return `{
414
+ ${items.join(",\n")}
415
+ ${closePad}}`;
416
+ }
417
+ return ensureAscii ? _pyJsonStrAscii(String(value)) : _pyJsonStr(String(value));
418
+ }
419
+ function _pyJsonStrAscii(s) {
420
+ let out = '"';
421
+ for (const ch of s) {
422
+ const code = ch.codePointAt(0);
423
+ switch (ch) {
424
+ case '"':
425
+ out += '\\"';
426
+ break;
427
+ case "\\":
428
+ out += "\\\\";
429
+ break;
430
+ case "\n":
431
+ out += "\\n";
432
+ break;
433
+ case "\r":
434
+ out += "\\r";
435
+ break;
436
+ case " ":
437
+ out += "\\t";
438
+ break;
439
+ case "\b":
440
+ out += "\\b";
441
+ break;
442
+ case "\f":
443
+ out += "\\f";
444
+ break;
445
+ default:
446
+ if (code < 32) {
447
+ out += `\\u${code.toString(16).padStart(4, "0")}`;
448
+ } else if (code < 127) {
449
+ out += ch;
450
+ } else if (code <= 65535) {
451
+ out += `\\u${code.toString(16).padStart(4, "0")}`;
452
+ } else {
453
+ const cp = code - 65536;
454
+ const hi = 55296 + (cp >> 10);
455
+ const lo = 56320 + (cp & 1023);
456
+ out += `\\u${hi.toString(16).padStart(4, "0")}\\u${lo.toString(16).padStart(4, "0")}`;
457
+ }
458
+ }
459
+ }
460
+ return out + '"';
461
+ }
462
+ function _pyJsonScalarAscii(value) {
463
+ if (typeof value === "string") return _pyJsonStrAscii(value);
464
+ return _pyJsonScalar(value);
465
+ }
466
+
467
+ // src/scripts/lint_hidden_unicode.ts
468
+ var CHECK = "hidden-unicode";
469
+ var _BIDI = /* @__PURE__ */ new Set([
470
+ 8234,
471
+ 8235,
472
+ 8236,
473
+ 8237,
474
+ 8238,
475
+ 8294,
476
+ 8295,
477
+ 8296,
478
+ 8297,
479
+ 8206,
480
+ 8207,
481
+ 1564
482
+ ]);
483
+ var _ZERO_WIDTH = /* @__PURE__ */ new Set([8203, 8204, 8205, 8288, 65279, 173]);
484
+ var _DEPRECATED = /* @__PURE__ */ new Set([
485
+ 8298,
486
+ 8299,
487
+ 8300,
488
+ 8301,
489
+ 8302,
490
+ 8303,
491
+ 65529,
492
+ 65530,
493
+ 65531
494
+ ]);
495
+ function _classify(cp) {
496
+ if (_BIDI.has(cp)) {
497
+ return "bidi-control";
498
+ }
499
+ if (_ZERO_WIDTH.has(cp)) {
500
+ return "zero-width";
501
+ }
502
+ if (cp >= 917504 && cp <= 917631) {
503
+ return "unicode-tag";
504
+ }
505
+ if (_DEPRECATED.has(cp)) {
506
+ return "deprecated-format";
507
+ }
508
+ if (cp >= 57344 && cp <= 63743 || cp >= 983040 && cp <= 1048573 || cp >= 1048576 && cp <= 1114109) {
509
+ return "private-use-area";
510
+ }
511
+ if ((cp >= 0 && cp <= 31 || cp >= 127 && cp <= 159) && cp !== 9 && cp !== 10 && cp !== 13) {
512
+ return "control-char";
513
+ }
514
+ return null;
515
+ }
516
+ var _VS_LO = 917760;
517
+ var _VS_HI = 917999;
518
+ function _isVS(cp) {
519
+ return cp >= _VS_LO && cp <= _VS_HI;
520
+ }
521
+ var _CP_NAME = {
522
+ 173: "SOFT HYPHEN",
523
+ 1564: "ARABIC LETTER MARK",
524
+ 8203: "ZERO WIDTH SPACE",
525
+ 8204: "ZERO WIDTH NON-JOINER",
526
+ 8205: "ZERO WIDTH JOINER",
527
+ 8206: "LEFT-TO-RIGHT MARK",
528
+ 8207: "RIGHT-TO-LEFT MARK",
529
+ 8234: "LEFT-TO-RIGHT EMBEDDING",
530
+ 8235: "RIGHT-TO-LEFT EMBEDDING",
531
+ 8236: "POP DIRECTIONAL FORMATTING",
532
+ 8237: "LEFT-TO-RIGHT OVERRIDE",
533
+ 8238: "RIGHT-TO-LEFT OVERRIDE",
534
+ 8288: "WORD JOINER",
535
+ 8294: "LEFT-TO-RIGHT ISOLATE",
536
+ 8295: "RIGHT-TO-LEFT ISOLATE",
537
+ 8296: "FIRST STRONG ISOLATE",
538
+ 8297: "POP DIRECTIONAL ISOLATE",
539
+ 8298: "INHIBIT SYMMETRIC SWAPPING",
540
+ 8299: "ACTIVATE SYMMETRIC SWAPPING",
541
+ 8300: "INHIBIT ARABIC FORM SHAPING",
542
+ 8301: "ACTIVATE ARABIC FORM SHAPING",
543
+ 8302: "NATIONAL DIGIT SHAPES",
544
+ 8303: "NOMINAL DIGIT SHAPES",
545
+ 65279: "ZERO WIDTH NO-BREAK SPACE",
546
+ 65529: "INTERLINEAR ANNOTATION ANCHOR",
547
+ 65530: "INTERLINEAR ANNOTATION SEPARATOR",
548
+ 65531: "INTERLINEAR ANNOTATION TERMINATOR",
549
+ 917505: "LANGUAGE TAG",
550
+ 917536: "TAG SPACE",
551
+ 917537: "TAG EXCLAMATION MARK",
552
+ 917538: "TAG QUOTATION MARK",
553
+ 917539: "TAG NUMBER SIGN",
554
+ 917540: "TAG DOLLAR SIGN",
555
+ 917541: "TAG PERCENT SIGN",
556
+ 917542: "TAG AMPERSAND",
557
+ 917543: "TAG APOSTROPHE",
558
+ 917544: "TAG LEFT PARENTHESIS",
559
+ 917545: "TAG RIGHT PARENTHESIS",
560
+ 917546: "TAG ASTERISK",
561
+ 917547: "TAG PLUS SIGN",
562
+ 917548: "TAG COMMA",
563
+ 917549: "TAG HYPHEN-MINUS",
564
+ 917550: "TAG FULL STOP",
565
+ 917551: "TAG SOLIDUS",
566
+ 917552: "TAG DIGIT ZERO",
567
+ 917553: "TAG DIGIT ONE",
568
+ 917554: "TAG DIGIT TWO",
569
+ 917555: "TAG DIGIT THREE",
570
+ 917556: "TAG DIGIT FOUR",
571
+ 917557: "TAG DIGIT FIVE",
572
+ 917558: "TAG DIGIT SIX",
573
+ 917559: "TAG DIGIT SEVEN",
574
+ 917560: "TAG DIGIT EIGHT",
575
+ 917561: "TAG DIGIT NINE",
576
+ 917562: "TAG COLON",
577
+ 917563: "TAG SEMICOLON",
578
+ 917564: "TAG LESS-THAN SIGN",
579
+ 917565: "TAG EQUALS SIGN",
580
+ 917566: "TAG GREATER-THAN SIGN",
581
+ 917567: "TAG QUESTION MARK",
582
+ 917568: "TAG COMMERCIAL AT",
583
+ 917569: "TAG LATIN CAPITAL LETTER A",
584
+ 917570: "TAG LATIN CAPITAL LETTER B",
585
+ 917571: "TAG LATIN CAPITAL LETTER C",
586
+ 917572: "TAG LATIN CAPITAL LETTER D",
587
+ 917573: "TAG LATIN CAPITAL LETTER E",
588
+ 917574: "TAG LATIN CAPITAL LETTER F",
589
+ 917575: "TAG LATIN CAPITAL LETTER G",
590
+ 917576: "TAG LATIN CAPITAL LETTER H",
591
+ 917577: "TAG LATIN CAPITAL LETTER I",
592
+ 917578: "TAG LATIN CAPITAL LETTER J",
593
+ 917579: "TAG LATIN CAPITAL LETTER K",
594
+ 917580: "TAG LATIN CAPITAL LETTER L",
595
+ 917581: "TAG LATIN CAPITAL LETTER M",
596
+ 917582: "TAG LATIN CAPITAL LETTER N",
597
+ 917583: "TAG LATIN CAPITAL LETTER O",
598
+ 917584: "TAG LATIN CAPITAL LETTER P",
599
+ 917585: "TAG LATIN CAPITAL LETTER Q",
600
+ 917586: "TAG LATIN CAPITAL LETTER R",
601
+ 917587: "TAG LATIN CAPITAL LETTER S",
602
+ 917588: "TAG LATIN CAPITAL LETTER T",
603
+ 917589: "TAG LATIN CAPITAL LETTER U",
604
+ 917590: "TAG LATIN CAPITAL LETTER V",
605
+ 917591: "TAG LATIN CAPITAL LETTER W",
606
+ 917592: "TAG LATIN CAPITAL LETTER X",
607
+ 917593: "TAG LATIN CAPITAL LETTER Y",
608
+ 917594: "TAG LATIN CAPITAL LETTER Z",
609
+ 917595: "TAG LEFT SQUARE BRACKET",
610
+ 917596: "TAG REVERSE SOLIDUS",
611
+ 917597: "TAG RIGHT SQUARE BRACKET",
612
+ 917598: "TAG CIRCUMFLEX ACCENT",
613
+ 917599: "TAG LOW LINE",
614
+ 917600: "TAG GRAVE ACCENT",
615
+ 917601: "TAG LATIN SMALL LETTER A",
616
+ 917602: "TAG LATIN SMALL LETTER B",
617
+ 917603: "TAG LATIN SMALL LETTER C",
618
+ 917604: "TAG LATIN SMALL LETTER D",
619
+ 917605: "TAG LATIN SMALL LETTER E",
620
+ 917606: "TAG LATIN SMALL LETTER F",
621
+ 917607: "TAG LATIN SMALL LETTER G",
622
+ 917608: "TAG LATIN SMALL LETTER H",
623
+ 917609: "TAG LATIN SMALL LETTER I",
624
+ 917610: "TAG LATIN SMALL LETTER J",
625
+ 917611: "TAG LATIN SMALL LETTER K",
626
+ 917612: "TAG LATIN SMALL LETTER L",
627
+ 917613: "TAG LATIN SMALL LETTER M",
628
+ 917614: "TAG LATIN SMALL LETTER N",
629
+ 917615: "TAG LATIN SMALL LETTER O",
630
+ 917616: "TAG LATIN SMALL LETTER P",
631
+ 917617: "TAG LATIN SMALL LETTER Q",
632
+ 917618: "TAG LATIN SMALL LETTER R",
633
+ 917619: "TAG LATIN SMALL LETTER S",
634
+ 917620: "TAG LATIN SMALL LETTER T",
635
+ 917621: "TAG LATIN SMALL LETTER U",
636
+ 917622: "TAG LATIN SMALL LETTER V",
637
+ 917623: "TAG LATIN SMALL LETTER W",
638
+ 917624: "TAG LATIN SMALL LETTER X",
639
+ 917625: "TAG LATIN SMALL LETTER Y",
640
+ 917626: "TAG LATIN SMALL LETTER Z",
641
+ 917627: "TAG LEFT CURLY BRACKET",
642
+ 917628: "TAG VERTICAL LINE",
643
+ 917629: "TAG RIGHT CURLY BRACKET",
644
+ 917630: "TAG TILDE",
645
+ 917631: "CANCEL TAG"
646
+ };
647
+ function _cpName(cp) {
648
+ const n = _CP_NAME[cp];
649
+ return n !== void 0 ? n : "<unnamed>";
650
+ }
651
+ function _scan(sf) {
652
+ if (sf.pragma_allows(CHECK)) {
653
+ return [];
654
+ }
655
+ const out = [];
656
+ for (const [lineno, text] of sf.iter_lines({ skip_example_fence: true })) {
657
+ let vs_run = 0;
658
+ for (const ch of text) {
659
+ const cp = ch.codePointAt(0);
660
+ if (_isVS(cp)) {
661
+ vs_run += 1;
662
+ continue;
663
+ }
664
+ const kind = _classify(cp);
665
+ if (kind) {
666
+ const name = _cpName(cp);
667
+ out.push(
668
+ new Finding(
669
+ sf.rel,
670
+ lineno,
671
+ CHECK,
672
+ "HIGH",
673
+ `${kind} U+${cp.toString(16).toUpperCase().padStart(4, "0")} (${name})`,
674
+ sf.weight
675
+ )
676
+ );
677
+ }
678
+ }
679
+ if (vs_run >= 3) {
680
+ out.push(
681
+ new Finding(
682
+ sf.rel,
683
+ lineno,
684
+ CHECK,
685
+ "HIGH",
686
+ `variation-selector run x${vs_run} (steganography signature)`,
687
+ sf.weight
688
+ )
689
+ );
690
+ }
691
+ }
692
+ return out;
693
+ }
694
+ var _BINARY_EXT = /* @__PURE__ */ new Set([
695
+ "png",
696
+ "jpg",
697
+ "jpeg",
698
+ "gif",
699
+ "ico",
700
+ "webp",
701
+ "avif",
702
+ "bmp",
703
+ "tiff",
704
+ "pdf",
705
+ "woff",
706
+ "woff2",
707
+ "ttf",
708
+ "otf",
709
+ "eot",
710
+ "zip",
711
+ "gz",
712
+ "tgz",
713
+ "bz2",
714
+ "xz",
715
+ "zst",
716
+ "7z",
717
+ "rar",
718
+ "wasm",
719
+ "mp3",
720
+ "mp4",
721
+ "wav",
722
+ "ogg",
723
+ "webm",
724
+ "mov",
725
+ "avi",
726
+ "so",
727
+ "dylib",
728
+ "dll",
729
+ "node",
730
+ "a",
731
+ "o",
732
+ "exe",
733
+ "jar",
734
+ "class",
735
+ "pyc",
736
+ "pyo",
737
+ "db",
738
+ "sqlite",
739
+ "sqlite3",
740
+ "bin",
741
+ "dat",
742
+ "docx",
743
+ "xlsx",
744
+ "pptx",
745
+ "doc",
746
+ "xls",
747
+ "ppt",
748
+ "odt",
749
+ "ods",
750
+ "odp"
751
+ ]);
752
+ var _GENERATED_PREFIXES = [
753
+ "dist/",
754
+ ".augment/",
755
+ ".claude/",
756
+ ".cursor/",
757
+ ".clinerules/",
758
+ // Verbatim captured tool output, not authored text. These are RECORDINGS:
759
+ // escaping a control byte inside one would falsify the record it exists to
760
+ // preserve. If a capture needs to stay grep-readable, the fix is to escape
761
+ // at capture time — never to edit the artifact afterwards.
762
+ "agents/evidence/analysis/",
763
+ // The frozen hostile-encoding corpus. Its positives ARE raw control
764
+ // characters — that is the thing under test, so flagging them would demand
765
+ // escaping the fixtures and thereby delete the test. The corpus has its own,
766
+ // stronger integrity controls: a sha256 freeze plus a scope-guard test that
767
+ // is itself falsified by an out-of-scope fixture. Excluded here because it
768
+ // is DATA, on the same principle as the captures above.
769
+ "internal/bench/corpora/encoding-channels/"
770
+ ];
771
+ function _isGenerated(rel) {
772
+ return _GENERATED_PREFIXES.some((p) => rel.startsWith(p));
773
+ }
774
+ function _isRawControlCodepoint(cp) {
775
+ if (cp === 9 || cp === 10 || cp === 13) return false;
776
+ return cp <= 31 || cp >= 127 && cp <= 159;
777
+ }
778
+ function _extensionOf(rel) {
779
+ const base = rel.slice(rel.lastIndexOf("/") + 1);
780
+ const dot = base.lastIndexOf(".");
781
+ return dot <= 0 ? "" : base.slice(dot + 1).toLowerCase();
782
+ }
783
+ function _trackedFiles() {
784
+ const res = spawnSync("git", ["ls-files", "-z"], {
785
+ cwd: ROOT,
786
+ maxBuffer: 64 * 1024 * 1024
787
+ });
788
+ if (res.status !== 0 || res.stdout === null) return null;
789
+ return res.stdout.toString("utf-8").split("\0").filter((p) => p !== "");
790
+ }
791
+ function _eligibleSourceFiles() {
792
+ const tracked = _trackedFiles();
793
+ if (tracked === null) return null;
794
+ return tracked.filter((rel) => !_isGenerated(rel) && !_BINARY_EXT.has(_extensionOf(rel)));
795
+ }
796
+ function _scanSourceControlBytes(files = null) {
797
+ const tracked = files ?? _eligibleSourceFiles();
798
+ if (tracked === null) return [];
799
+ const out = [];
800
+ for (const rel of tracked) {
801
+ if (_isGenerated(rel)) continue;
802
+ if (_BINARY_EXT.has(_extensionOf(rel))) continue;
803
+ let data;
804
+ try {
805
+ data = fs2.readFileSync(path2.join(ROOT, rel));
806
+ } catch {
807
+ continue;
808
+ }
809
+ try {
810
+ new TextDecoder("utf-8", { fatal: true }).decode(data);
811
+ } catch {
812
+ continue;
813
+ }
814
+ let line = 1;
815
+ for (const ch of data.toString("utf-8")) {
816
+ const cp = ch.codePointAt(0);
817
+ if (cp === void 0) continue;
818
+ if (cp === 10) {
819
+ line += 1;
820
+ continue;
821
+ }
822
+ if (!_isRawControlCodepoint(cp)) continue;
823
+ const hex = cp.toString(16).toUpperCase().padStart(2, "0");
824
+ out.push(
825
+ new Finding(
826
+ rel,
827
+ line,
828
+ CHECK,
829
+ "HIGH",
830
+ `raw control character U+${hex} in a text source \u2014 makes grep/file(1)/git treat this file as binary and skip it silently; use the language escape (e.g. \\0, \\x7F) instead`,
831
+ 1
832
+ )
833
+ );
834
+ }
835
+ }
836
+ return out;
837
+ }
838
+ function _sanitize(filePath) {
839
+ const raw = fs2.readFileSync(filePath, "utf-8");
840
+ let cleaned = "";
841
+ for (const ch of raw) {
842
+ const cp = ch.codePointAt(0);
843
+ if (_classify(cp) === null && !_isVS(cp)) {
844
+ cleaned += ch;
845
+ }
846
+ }
847
+ cleaned = cleaned.normalize("NFKC");
848
+ const out = filePath + ".sanitized";
849
+ fs2.writeFileSync(out, cleaned, "utf-8");
850
+ return out;
851
+ }
852
+ function _argError(msg) {
853
+ process.stderr.write("usage: lint_hidden_unicode [-h] [--json] [--fix]\n");
854
+ process.stderr.write(`lint_hidden_unicode: error: ${msg}
855
+ `);
856
+ process.exit(2);
857
+ }
858
+ function parse_args(argv) {
859
+ const out = { json: false, fix: false };
860
+ const extra = [];
861
+ for (const a of argv) {
862
+ if (a === "-h" || a === "--help") {
863
+ process.stdout.write("usage: lint_hidden_unicode [-h] [--json] [--fix]\n");
864
+ process.exit(0);
865
+ } else if (a === "--json") {
866
+ out.json = true;
867
+ } else if (a === "--fix") {
868
+ out.fix = true;
869
+ } else {
870
+ extra.push(a);
871
+ }
872
+ }
873
+ if (extra.length > 0) {
874
+ _argError(`unrecognized arguments: ${extra.join(" ")}`);
875
+ }
876
+ return out;
877
+ }
878
+ function main(argv = null) {
879
+ const args = parse_args(argv ?? process.argv.slice(2));
880
+ const findings = [];
881
+ const flagged = /* @__PURE__ */ new Set();
882
+ for (const sf of iter_corpus()) {
883
+ const hits = _scan(sf);
884
+ for (const h of hits) {
885
+ findings.push(h);
886
+ }
887
+ if (hits.length > 0) {
888
+ flagged.add(sf.path);
889
+ }
890
+ }
891
+ const sourceFiles = _eligibleSourceFiles();
892
+ for (const h of _scanSourceControlBytes(sourceFiles)) {
893
+ findings.push(h);
894
+ }
895
+ if (!args.json) {
896
+ const n = sourceFiles === null ? 0 : sourceFiles.length;
897
+ process.stdout.write(
898
+ sourceFiles === null ? " source pass: SKIPPED \u2014 `git ls-files` unavailable, 0 files read for raw control bytes\n" : ` source pass: ${n} tracked text file(s) read for raw C0 control bytes (generated trees + verbatim captures excluded)
899
+ `
900
+ );
901
+ }
902
+ if (args.fix) {
903
+ for (const p of [...flagged].sort()) {
904
+ const sanitized = _sanitize(p);
905
+ const rel = path2.relative(path2.resolve(ROOT), path2.resolve(sanitized));
906
+ process.stdout.write(` fixed \u2192 ${rel.split(path2.sep).join("/")}
907
+ `);
908
+ }
909
+ }
910
+ if (args.json) {
911
+ const payload = py_json_dumps_indent2(findings.map((f) => f.toDict()));
912
+ process.stdout.write(payload + "\n");
913
+ return findings.some((f) => f.is_fail) ? 1 : 0;
914
+ }
915
+ return report(findings, { check_label: "hidden-unicode" });
916
+ }
917
+ function _isCliEntry() {
918
+ if (true) {
919
+ return false;
920
+ }
921
+ if (process.argv[1] === void 0) {
922
+ return false;
923
+ }
924
+ const argvUrl = pathToFileURL(path2.resolve(process.argv[1])).href;
925
+ if (import.meta.url === argvUrl) {
926
+ return true;
927
+ }
928
+ try {
929
+ const here = fs2.realpathSync(fileURLToPath2(import.meta.url));
930
+ const argv = fs2.realpathSync(path2.resolve(process.argv[1]));
931
+ return here === argv;
932
+ } catch {
933
+ return false;
934
+ }
935
+ }
936
+ if (_isCliEntry()) {
937
+ process.exitCode = main();
938
+ }
939
+
940
+ // src/scripts/_lib/confusables.ts
941
+ var _LATIN = new RegExp("\\p{Script=Latin}", "u");
942
+ var _CYRILLIC = new RegExp("\\p{Script=Cyrillic}", "u");
943
+ var _GREEK = new RegExp("\\p{Script=Greek}", "u");
944
+ var _LETTER = new RegExp("\\p{L}", "u");
945
+
946
+ // src/scripts/_lib/retrieval_sanitize.ts
947
+ var MAX_FIELD_CHARS = 8192;
948
+ var _INVISIBLE_FILLERS = /* @__PURE__ */ new Set([
949
+ 12644,
950
+ // HANGUL FILLER
951
+ 4447,
952
+ // HANGUL CHOSEONG FILLER
953
+ 4448
954
+ // HANGUL JUNGSEONG FILLER
955
+ ]);
956
+ function _isStrippableControl(cp) {
957
+ if (cp === 9 || cp === 10) return false;
958
+ if (cp <= 31) return true;
959
+ if (cp === 127) return true;
960
+ if (cp >= 128 && cp <= 159) return true;
961
+ return false;
962
+ }
963
+ function sanitize_text(s) {
964
+ let out = "";
965
+ for (const ch of s) {
966
+ const cp = ch.codePointAt(0);
967
+ if (cp === void 0) continue;
968
+ if (_classify(cp) !== null) continue;
969
+ if (_isStrippableControl(cp)) continue;
970
+ if (_INVISIBLE_FILLERS.has(cp)) continue;
971
+ out += ch;
972
+ }
973
+ return out.length > MAX_FIELD_CHARS ? out.slice(0, MAX_FIELD_CHARS) : out;
974
+ }
975
+ function sanitize_entry(entry) {
976
+ const out = {};
977
+ for (const [k, v] of Object.entries(entry)) {
978
+ out[k] = _sanitize_value(v);
979
+ }
980
+ return out;
981
+ }
982
+ function _sanitize_value(v) {
983
+ if (typeof v === "string") return sanitize_text(v);
984
+ if (Array.isArray(v)) return v.map((x) => typeof x === "string" ? sanitize_text(x) : _sanitize_value(x));
985
+ if (v && typeof v === "object" && v.constructor === Object) {
986
+ return sanitize_entry(v);
987
+ }
988
+ return v;
989
+ }
990
+
991
+ // src/scripts/_lib/lexical_index.ts
992
+ var BM25_K1 = 1.5;
993
+ var BM25_B = 0.75;
994
+ var MIN_TOKEN_LEN = 2;
995
+ function tokenize(s) {
996
+ const out = [];
997
+ for (const raw of s.toLowerCase().split(/[^a-z0-9]+/)) {
998
+ if (raw.length >= MIN_TOKEN_LEN) out.push(raw);
999
+ }
1000
+ return out;
1001
+ }
1002
+ function trigrams(s) {
1003
+ const norm = s.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
1004
+ const out = /* @__PURE__ */ new Set();
1005
+ if (norm.length < 3) {
1006
+ if (norm.length > 0) out.add(norm);
1007
+ return out;
1008
+ }
1009
+ for (let i = 0; i + 3 <= norm.length; i++) {
1010
+ out.add(norm.slice(i, i + 3));
1011
+ }
1012
+ return out;
1013
+ }
1014
+ var LexicalIndex = class {
1015
+ docs = /* @__PURE__ */ new Map();
1016
+ df = /* @__PURE__ */ new Map();
1017
+ n;
1018
+ avgdl;
1019
+ constructor(documents) {
1020
+ let totalLen = 0;
1021
+ for (const d of documents) {
1022
+ const tokens = tokenize(d.text);
1023
+ const tf = /* @__PURE__ */ new Map();
1024
+ for (const t of tokens) tf.set(t, (tf.get(t) ?? 0) + 1);
1025
+ for (const t of tf.keys()) this.df.set(t, (this.df.get(t) ?? 0) + 1);
1026
+ this.docs.set(d.id, { id: d.id, len: tokens.length, tf, trigrams: trigrams(d.text) });
1027
+ totalLen += tokens.length;
1028
+ }
1029
+ this.n = documents.length;
1030
+ this.avgdl = this.n > 0 ? totalLen / this.n : 0;
1031
+ }
1032
+ /** Robertson–Spärck-Jones IDF, floored at 0 so common terms never subtract. */
1033
+ idf(term) {
1034
+ const df = this.df.get(term) ?? 0;
1035
+ if (df === 0) return 0;
1036
+ return Math.max(0, Math.log(1 + (this.n - df + 0.5) / (df + 0.5)));
1037
+ }
1038
+ /**
1039
+ * BM25 score of one document against the tokenised query terms, weighted by
1040
+ * **term coverage**: the raw BM25 sum is multiplied by `coverage²`, where
1041
+ * `coverage = distinct-query-terms-matched / distinct-query-terms`. This
1042
+ * stops a single generic, high-TF term match from outranking a document that
1043
+ * matches several rarer query terms once each (Phase 1 refinement). A
1044
+ * single-term query has `coverage == 1`, so its score is unchanged — the
1045
+ * weighting only re-orders genuinely multi-term queries.
1046
+ */
1047
+ score(queryTerms, docId) {
1048
+ const doc = this.docs.get(docId);
1049
+ if (doc === void 0 || doc.len === 0) return 0;
1050
+ let s = 0;
1051
+ const matched = /* @__PURE__ */ new Set();
1052
+ for (const term of queryTerms) {
1053
+ const tf = doc.tf.get(term);
1054
+ if (tf === void 0) continue;
1055
+ const idf = this.idf(term);
1056
+ if (idf === 0) continue;
1057
+ const denom = tf + BM25_K1 * (1 - BM25_B + BM25_B * doc.len / (this.avgdl || 1));
1058
+ s += idf * (tf * (BM25_K1 + 1)) / denom;
1059
+ matched.add(term);
1060
+ }
1061
+ if (s === 0) return 0;
1062
+ const distinctQuery = new Set(queryTerms).size;
1063
+ const coverage = distinctQuery > 0 ? matched.size / distinctQuery : 1;
1064
+ return s * coverage * coverage;
1065
+ }
1066
+ /**
1067
+ * Rank documents for a set of query keys (phrases are tokenised). A
1068
+ * character-trigram prefilter selects candidates cheaply; the guard keeps
1069
+ * any doc sharing a query TOKEN even when no trigram overlaps, so the
1070
+ * prefilter never drops a genuine lexical match. Ties break by id for a
1071
+ * stable, deterministic order.
1072
+ */
1073
+ rank(queryKeys) {
1074
+ const queryText = queryKeys.join(" ");
1075
+ const qTerms = tokenize(queryText);
1076
+ const qTermSet = new Set(qTerms);
1077
+ const qTrigrams = trigrams(queryText);
1078
+ const candidates = [];
1079
+ for (const [id, doc] of this.docs) {
1080
+ let hit = false;
1081
+ for (const t of qTrigrams) {
1082
+ if (doc.trigrams.has(t)) {
1083
+ hit = true;
1084
+ break;
1085
+ }
1086
+ }
1087
+ if (!hit) {
1088
+ for (const t of qTermSet) {
1089
+ if (doc.tf.has(t)) {
1090
+ hit = true;
1091
+ break;
1092
+ }
1093
+ }
1094
+ }
1095
+ if (hit) candidates.push(id);
1096
+ }
1097
+ const scored = candidates.map((id) => ({ id, score: this.score(qTerms, id) })).filter((r) => r.score > 0);
1098
+ scored.sort((a, b) => b.score - a.score || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
1099
+ return scored;
1100
+ }
1101
+ /** Document count — for the caller's tripwire / diagnostics. */
1102
+ get size() {
1103
+ return this.n;
1104
+ }
1105
+ };
1106
+
1107
+ // src/scripts/_lib/memory_fts_index.ts
1108
+ import * as fs5 from "node:fs";
1109
+ import * as path5 from "node:path";
1110
+
1111
+ // src/scripts/_lib/sqlite_guard.ts
1112
+ import { createRequire } from "node:module";
1113
+ var SqliteUnavailableError = class extends Error {
1114
+ constructor(callerName, cause) {
1115
+ super(
1116
+ `node:sqlite is unavailable in this runtime (${cause}). ${callerName} requires Node with the built-in SQLite module.`
1117
+ );
1118
+ this.name = "SqliteUnavailableError";
1119
+ }
1120
+ };
1121
+ var _sqliteWarningSilenced = false;
1122
+ function _silenceSqliteExperimentalWarning() {
1123
+ if (_sqliteWarningSilenced) return;
1124
+ _sqliteWarningSilenced = true;
1125
+ const orig = process.emitWarning.bind(process);
1126
+ process.emitWarning = ((warning, ...rest) => {
1127
+ const text = typeof warning === "string" ? warning : warning?.message ?? "";
1128
+ if (/SQLite is an experimental/i.test(text)) return;
1129
+ orig(warning, ...rest);
1130
+ });
1131
+ }
1132
+ var _require;
1133
+ function _syncRequire() {
1134
+ if (_require === void 0) {
1135
+ _require = createRequire(import.meta.url);
1136
+ }
1137
+ return _require;
1138
+ }
1139
+ function loadSqliteSync(callerName) {
1140
+ _silenceSqliteExperimentalWarning();
1141
+ try {
1142
+ return _syncRequire()("node:sqlite");
1143
+ } catch (exc) {
1144
+ const message = exc instanceof Error ? exc.message : String(exc);
1145
+ throw new SqliteUnavailableError(callerName, message);
1146
+ }
1147
+ }
1148
+ function probeFts5Sync() {
1149
+ let sqlite;
1150
+ try {
1151
+ sqlite = loadSqliteSync("sqlite_guard.probeFts5Sync");
1152
+ } catch {
1153
+ return false;
1154
+ }
1155
+ let conn = null;
1156
+ try {
1157
+ conn = new sqlite.DatabaseSync(":memory:");
1158
+ conn.exec("CREATE VIRTUAL TABLE _fts5_probe USING fts5(body)");
1159
+ conn.exec("INSERT INTO _fts5_probe(body) VALUES ('probe')");
1160
+ const row = conn.prepare("SELECT body FROM _fts5_probe WHERE _fts5_probe MATCH 'probe'").get();
1161
+ return row !== void 0;
1162
+ } catch {
1163
+ return false;
1164
+ } finally {
1165
+ try {
1166
+ conn?.close();
1167
+ } catch {
1168
+ }
1169
+ }
1170
+ }
1171
+ function readUserVersion(conn) {
1172
+ const row = conn.prepare("PRAGMA user_version").get();
1173
+ const v = row?.["user_version"];
1174
+ if (typeof v === "bigint") return Number(v);
1175
+ if (typeof v === "number") return v;
1176
+ return 0;
1177
+ }
1178
+ function stampUserVersion(conn, version) {
1179
+ if (!Number.isInteger(version) || version < 0 || version > 2 ** 31 - 1) {
1180
+ throw new RangeError(`stampUserVersion: version out of range: ${version}`);
1181
+ }
1182
+ conn.exec(`PRAGMA user_version = ${version}`);
1183
+ }
1184
+
1185
+ // src/scripts/memory_report.ts
1186
+ var import_yaml = __toESM(require_dist(), 1);
1187
+ import * as fs4 from "node:fs";
1188
+ import * as path4 from "node:path";
1189
+ import process3 from "node:process";
1190
+ import { fileURLToPath as fileURLToPath4, pathToFileURL as pathToFileURL3 } from "node:url";
1191
+
1192
+ // src/scripts/memory_status.ts
1193
+ import process2 from "node:process";
1194
+ import * as fs3 from "node:fs";
1195
+ import * as path3 from "node:path";
1196
+ import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL2 } from "node:url";
1197
+ var CONTRACT_VERSION = 1;
1198
+ var _FILE_BACKEND_VERSION = "0.0.0-file";
1199
+ var _FILE_BACKEND_FEATURES = ["file-fallback"];
1200
+ function status(_refresh = false) {
1201
+ return {
1202
+ status: "file",
1203
+ // always "file" — no external backend
1204
+ backend: "file",
1205
+ reason: "file-backed memory (no external backend)",
1206
+ elapsed_ms: 0
1207
+ };
1208
+ }
1209
+ function health(_refresh = false) {
1210
+ return {
1211
+ contract_version: CONTRACT_VERSION,
1212
+ status: "ok",
1213
+ backend_version: _FILE_BACKEND_VERSION,
1214
+ features: [..._FILE_BACKEND_FEATURES]
1215
+ };
1216
+ }
1217
+ function _pyJsonDumps(value) {
1218
+ return _escapeNonAscii(_pyCompactSeparators(JSON.stringify(value)));
1219
+ }
1220
+ function _pyCompactSeparators(json) {
1221
+ let result = "";
1222
+ let inString = false;
1223
+ let escaped = false;
1224
+ for (let i = 0; i < json.length; i += 1) {
1225
+ const ch = json[i];
1226
+ result += ch;
1227
+ if (escaped) {
1228
+ escaped = false;
1229
+ continue;
1230
+ }
1231
+ if (ch === "\\") {
1232
+ escaped = true;
1233
+ continue;
1234
+ }
1235
+ if (ch === '"') {
1236
+ inString = !inString;
1237
+ continue;
1238
+ }
1239
+ if (!inString && (ch === "," || ch === ":")) {
1240
+ result += " ";
1241
+ }
1242
+ }
1243
+ return result;
1244
+ }
1245
+ function _escapeNonAscii(s) {
1246
+ let out = "";
1247
+ for (const ch of s) {
1248
+ const code = ch.codePointAt(0) ?? 0;
1249
+ if (code > 127) {
1250
+ for (let i = 0; i < ch.length; i += 1) {
1251
+ out += `\\u${ch.charCodeAt(i).toString(16).padStart(4, "0")}`;
1252
+ }
1253
+ } else {
1254
+ out += ch;
1255
+ }
1256
+ }
1257
+ return out;
1258
+ }
1259
+ var _PROG = "memory_status.py";
1260
+ var _USAGE = "usage: memory_status.py [-h] [--format {text,json}] [--refresh] [--health]\n";
1261
+ var _ExitError = class extends Error {
1262
+ constructor(code) {
1263
+ super(`exit ${code}`);
1264
+ this.code = code;
1265
+ }
1266
+ code;
1267
+ };
1268
+ function _usageError(msg) {
1269
+ process2.stderr.write(_USAGE);
1270
+ process2.stderr.write(`${_PROG}: error: ${msg}
1271
+ `);
1272
+ throw new _ExitError(2);
1273
+ }
1274
+ function _parseArgs(argv) {
1275
+ const args = { format: "text", refresh: false, health: false };
1276
+ const checkFormat = (v) => {
1277
+ if (v !== "text" && v !== "json") {
1278
+ _usageError(`argument --format: invalid choice: '${v}' (choose from 'text', 'json')`);
1279
+ }
1280
+ return v;
1281
+ };
1282
+ for (let i = 0; i < argv.length; i += 1) {
1283
+ const a = argv[i];
1284
+ const takeValue = (flag) => {
1285
+ const v = argv[++i];
1286
+ if (v === void 0) {
1287
+ _usageError(`argument ${flag}: expected one argument`);
1288
+ }
1289
+ return v;
1290
+ };
1291
+ if (a === "--format") {
1292
+ args.format = checkFormat(takeValue("--format"));
1293
+ } else if (a.startsWith("--format=")) {
1294
+ args.format = checkFormat(a.slice("--format=".length));
1295
+ } else if (a === "--refresh") {
1296
+ args.refresh = true;
1297
+ } else if (a === "--health") {
1298
+ args.health = true;
1299
+ } else if (a === "-h" || a === "--help") {
1300
+ process2.stdout.write(_USAGE);
1301
+ throw new _ExitError(0);
1302
+ } else {
1303
+ _usageError(`unrecognized arguments: ${a}`);
1304
+ }
1305
+ }
1306
+ return args;
1307
+ }
1308
+ function main2() {
1309
+ const args = _parseArgs(process2.argv.slice(2));
1310
+ if (args.health) {
1311
+ process2.stdout.write(`${_pyJsonDumps(health())}
1312
+ `);
1313
+ return 0;
1314
+ }
1315
+ const r = status();
1316
+ if (args.format === "json") {
1317
+ process2.stdout.write(`${_pyJsonDumps(r)}
1318
+ `);
1319
+ } else {
1320
+ process2.stdout.write(` \u2139\uFE0F backend=${r.backend} status=${r.status} reason=${r.reason}
1321
+ `);
1322
+ }
1323
+ return 0;
1324
+ }
1325
+ function _isCliEntry2() {
1326
+ if (true) {
1327
+ return false;
1328
+ }
1329
+ if (process2.argv[1] === void 0) {
1330
+ return false;
1331
+ }
1332
+ const argvUrl = pathToFileURL2(path3.resolve(process2.argv[1])).href;
1333
+ if (import.meta.url === argvUrl) {
1334
+ return true;
1335
+ }
1336
+ try {
1337
+ const here = fs3.realpathSync(fileURLToPath3(import.meta.url));
1338
+ const argv = fs3.realpathSync(path3.resolve(process2.argv[1]));
1339
+ return here === argv;
1340
+ } catch {
1341
+ return false;
1342
+ }
1343
+ }
1344
+ var _isMain = _isCliEntry2();
1345
+ if (_isMain) {
1346
+ try {
1347
+ process2.exitCode = main2();
1348
+ } catch (e) {
1349
+ if (e instanceof _ExitError) {
1350
+ process2.exitCode = e.code;
1351
+ } else {
1352
+ throw e;
1353
+ }
1354
+ }
1355
+ }
1356
+
1357
+ // src/scripts/memory_report.ts
1358
+ var MEMORY_ROOT = path4.join("agents", "memory");
1359
+ var INTAKE_ROOT = path4.join(MEMORY_ROOT, "intake");
1360
+ function _setMemoryRoot(p) {
1361
+ MEMORY_ROOT = p;
1362
+ }
1363
+ var CURATED_TYPES = [
1364
+ "ownership",
1365
+ "historical-patterns",
1366
+ "domain-invariants",
1367
+ "incident-learnings",
1368
+ "product-rules"
1369
+ ];
1370
+ var _MODE_MARKER_PATTERN = /<!--\s*role-mode:\s*([a-z0-9][a-z0-9-]*)\s*\|\s*contract:[^>]*-->/g;
1371
+ var _MODE_SCAN_DIRS = [
1372
+ path4.join("agents", "sessions"),
1373
+ path4.join("agents", "runtime", "reports"),
1374
+ path4.join("agents", "handoffs"),
1375
+ path4.join("agents", "learnings")
1376
+ ];
1377
+ var _KNOWN_MODES = /* @__PURE__ */ new Set([
1378
+ "developer",
1379
+ "reviewer",
1380
+ "tester",
1381
+ "po",
1382
+ "incident",
1383
+ "planner"
1384
+ ]);
1385
+ var PYYAML_DATE_ONLY_RE = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/;
1386
+ var PYYAML_TIMESTAMP_RE = /^(?:[0-9]{4}-[0-9]{2}-[0-9]{2}|[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}(?:[Tt]|[ \t]+)[0-9]{1,2}:[0-9]{2}:[0-9]{2}(?:\.[0-9]*)?(?:[ \t]*(?:Z|[-+][0-9]{1,2}(?::[0-9]{2})?))?)$/;
1387
+ var PyTimestamp = class {
1388
+ constructor(pyStr) {
1389
+ this.pyStr = pyStr;
1390
+ }
1391
+ pyStr;
1392
+ toString() {
1393
+ return this.pyStr;
1394
+ }
1395
+ };
1396
+ var PyFloat2 = class {
1397
+ constructor(value) {
1398
+ this.value = value;
1399
+ }
1400
+ value;
1401
+ };
1402
+ function _isPlainObject2(v) {
1403
+ return typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof PyTimestamp) && !(v instanceof PyFloat2);
1404
+ }
1405
+ function _isFile(p) {
1406
+ try {
1407
+ return fs4.statSync(p).isFile();
1408
+ } catch {
1409
+ return false;
1410
+ }
1411
+ }
1412
+ function _isDir(p) {
1413
+ try {
1414
+ return fs4.statSync(p).isDirectory();
1415
+ } catch {
1416
+ return false;
1417
+ }
1418
+ }
1419
+ function _load_yaml(p) {
1420
+ const text = fs4.readFileSync(p, "utf-8");
1421
+ const doc = (0, import_yaml.parseDocument)(text, { version: "1.1", prettyErrors: false });
1422
+ if (doc.errors.length > 0) {
1423
+ const err = doc.errors[0];
1424
+ throw new Error(err ? err.message : "YAML parse error");
1425
+ }
1426
+ import_yaml.default.visit(doc, {
1427
+ Scalar(_key, node) {
1428
+ if (!node.range) {
1429
+ return;
1430
+ }
1431
+ const raw = text.slice(node.range[0], node.range[1]);
1432
+ if (node.value instanceof Date) {
1433
+ if (PYYAML_DATE_ONLY_RE.test(raw)) {
1434
+ node.value = new PyTimestamp(raw);
1435
+ } else if (PYYAML_TIMESTAMP_RE.test(raw)) {
1436
+ node.value = new PyTimestamp(_pyYamlDatetimeStr(raw));
1437
+ }
1438
+ return;
1439
+ }
1440
+ if (typeof node.value === "boolean" && node.type === "PLAIN" && /^[ynYN]$/.test(raw)) {
1441
+ node.value = raw;
1442
+ }
1443
+ }
1444
+ });
1445
+ const data = doc.toJS({ mapAsMap: false });
1446
+ return data == null ? {} : data;
1447
+ }
1448
+ function _pad2(v) {
1449
+ return v.padStart(2, "0");
1450
+ }
1451
+ function _pyYamlDatetimeStr(raw) {
1452
+ const m = /^(\d{4})-(\d{1,2})-(\d{1,2})(?:[Tt]|[ \t]+)(\d{1,2}):(\d{2}):(\d{2})(?:\.(\d*))?(?:[ \t]*(Z|[-+]\d{1,2}(?::\d{2})?))?$/.exec(
1453
+ raw
1454
+ );
1455
+ if (!m) {
1456
+ return raw;
1457
+ }
1458
+ const [, y, mo, d, h, mi, s, frac, tz] = m;
1459
+ const date = `${y}-${_pad2(mo)}-${_pad2(d)}`;
1460
+ let time = `${_pad2(h)}:${mi}:${s}`;
1461
+ if (frac && /[1-9]/.test(frac)) {
1462
+ time += `.${(frac + "000000").slice(0, 6)}`;
1463
+ }
1464
+ let off = "";
1465
+ if (tz) {
1466
+ if (tz === "Z") {
1467
+ off = "+00:00";
1468
+ } else {
1469
+ const tm = /^([-+])(\d{1,2})(?::(\d{2}))?$/.exec(tz);
1470
+ if (tm) {
1471
+ off = `${tm[1]}${_pad2(tm[2])}:${tm[3] ?? "00"}`;
1472
+ }
1473
+ }
1474
+ }
1475
+ return `${date} ${time}${off}`;
1476
+ }
1477
+ function _iter_curated_entries() {
1478
+ const out = [];
1479
+ for (const mtype of CURATED_TYPES) {
1480
+ const single = path4.join(MEMORY_ROOT, `${mtype}.yml`);
1481
+ if (_isFile(single)) {
1482
+ const data = _load_yaml(single);
1483
+ const entries = data["entries"];
1484
+ if (Array.isArray(entries)) {
1485
+ for (const e of entries) {
1486
+ if (_isPlainObject2(e)) {
1487
+ out.push([single, mtype, e]);
1488
+ }
1489
+ }
1490
+ }
1491
+ }
1492
+ const type_dir = path4.join(MEMORY_ROOT, mtype);
1493
+ if (_isDir(type_dir)) {
1494
+ for (const yml of _rglobYml(type_dir)) {
1495
+ const data = _load_yaml(yml) ?? {};
1496
+ const entries = data["entries"];
1497
+ if (Array.isArray(entries)) {
1498
+ for (const e of entries) {
1499
+ if (_isPlainObject2(e)) {
1500
+ out.push([yml, mtype, e]);
1501
+ }
1502
+ }
1503
+ } else if (_isPlainObject2(data) && data["id"]) {
1504
+ out.push([yml, mtype, data]);
1505
+ }
1506
+ }
1507
+ }
1508
+ }
1509
+ return out;
1510
+ }
1511
+ function _rglobYml(root) {
1512
+ const found = [];
1513
+ const walk = (dir) => {
1514
+ let entries;
1515
+ try {
1516
+ entries = fs4.readdirSync(dir, { withFileTypes: true });
1517
+ } catch {
1518
+ return;
1519
+ }
1520
+ for (const ent of entries) {
1521
+ const full = path4.join(dir, ent.name);
1522
+ if (ent.isDirectory()) {
1523
+ walk(full);
1524
+ } else if (ent.isFile() && ent.name.endsWith(".yml")) {
1525
+ found.push(full);
1526
+ }
1527
+ }
1528
+ };
1529
+ walk(root);
1530
+ found.sort();
1531
+ return found;
1532
+ }
1533
+ function _globJsonl(root) {
1534
+ let names;
1535
+ try {
1536
+ names = fs4.readdirSync(root);
1537
+ } catch {
1538
+ return [];
1539
+ }
1540
+ return names.filter((n) => n.endsWith(".jsonl")).sort().map((n) => path4.join(root, n));
1541
+ }
1542
+ function _intake_stats() {
1543
+ let total = 0;
1544
+ const by_type = /* @__PURE__ */ new Map();
1545
+ const by_month = /* @__PURE__ */ new Map();
1546
+ let superseded = 0;
1547
+ if (_isDir(INTAKE_ROOT)) {
1548
+ for (const jsonl of _globJsonl(INTAKE_ROOT)) {
1549
+ const stem = path4.basename(jsonl).replace(/\.jsonl$/, "");
1550
+ const month = stem.replace("signals-", "");
1551
+ for (const obj of _readJsonl(jsonl)) {
1552
+ if (obj["type"] === "supersede") {
1553
+ superseded += 1;
1554
+ continue;
1555
+ }
1556
+ total += 1;
1557
+ const t = obj["entry_type"];
1558
+ if (typeof t === "string") {
1559
+ by_type.set(t, (by_type.get(t) ?? 0) + 1);
1560
+ }
1561
+ by_month.set(month, (by_month.get(month) ?? 0) + 1);
1562
+ }
1563
+ }
1564
+ }
1565
+ return {
1566
+ total_active: total,
1567
+ superseded,
1568
+ by_type: _mapToObj(by_type),
1569
+ by_month: _mapToObj(by_month)
1570
+ };
1571
+ }
1572
+ function _readJsonl(p) {
1573
+ let text;
1574
+ try {
1575
+ text = fs4.readFileSync(p, "utf-8");
1576
+ } catch {
1577
+ return [];
1578
+ }
1579
+ const out = [];
1580
+ for (const rawLine of text.split("\n")) {
1581
+ const line = rawLine.trim();
1582
+ if (!line) {
1583
+ continue;
1584
+ }
1585
+ let obj;
1586
+ try {
1587
+ obj = JSON.parse(line);
1588
+ } catch {
1589
+ continue;
1590
+ }
1591
+ if (_isPlainObject2(obj)) {
1592
+ out.push(obj);
1593
+ }
1594
+ }
1595
+ return out;
1596
+ }
1597
+ function _mapToObj(m) {
1598
+ const obj = {};
1599
+ for (const [k, v] of m) {
1600
+ obj[k] = v;
1601
+ }
1602
+ return obj;
1603
+ }
1604
+ function _todayDate() {
1605
+ const now = /* @__PURE__ */ new Date();
1606
+ return { y: now.getFullYear(), m: now.getMonth() + 1, d: now.getDate() };
1607
+ }
1608
+ function _dateDiffDays(a, b) {
1609
+ const am = Date.UTC(a.y, a.m - 1, a.d);
1610
+ const bm = Date.UTC(b.y, b.m - 1, b.d);
1611
+ return Math.round((am - bm) / (24 * 60 * 60 * 1e3));
1612
+ }
1613
+ function _parseIsoDate(s) {
1614
+ const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
1615
+ if (!m) {
1616
+ return null;
1617
+ }
1618
+ const y = Number(m[1]);
1619
+ const mo = Number(m[2]);
1620
+ const d = Number(m[3]);
1621
+ if (mo < 1 || mo > 12 || d < 1 || d > 31) {
1622
+ return null;
1623
+ }
1624
+ return { y, m: mo, d };
1625
+ }
1626
+ function _addDays(date, days) {
1627
+ const ms = Date.UTC(date.y, date.m - 1, date.d) + days * 24 * 60 * 60 * 1e3;
1628
+ const dt = new Date(ms);
1629
+ return { y: dt.getUTCFullYear(), m: dt.getUTCMonth() + 1, d: dt.getUTCDate() };
1630
+ }
1631
+ function _staleness_report() {
1632
+ const today = _todayDate();
1633
+ const stale = [];
1634
+ for (const [p, mtype, entry] of _iter_curated_entries()) {
1635
+ const lv = entry["last_validated"];
1636
+ const review_after = entry["review_after_days"];
1637
+ const lvIsDateLike = typeof lv === "string" || lv instanceof PyTimestamp;
1638
+ const reviewIsInt = typeof review_after === "number" && Number.isInteger(review_after) && typeof review_after !== "boolean";
1639
+ if (!lvIsDateLike || !reviewIsInt) {
1640
+ continue;
1641
+ }
1642
+ const lvStr = lv instanceof PyTimestamp ? lv.pyStr : lv;
1643
+ const parsed = _parseIsoDate(lvStr);
1644
+ if (parsed === null) {
1645
+ continue;
1646
+ }
1647
+ const due = _addDays(parsed, review_after);
1648
+ if (_dateDiffDays(due, today) < 0) {
1649
+ stale.push({
1650
+ file: p,
1651
+ type: mtype,
1652
+ id: entry["id"] ?? "?",
1653
+ overdue_days: _dateDiffDays(today, due)
1654
+ });
1655
+ }
1656
+ }
1657
+ stale.sort((a, b) => b.overdue_days - a.overdue_days);
1658
+ return stale;
1659
+ }
1660
+ function _quarter_of(d) {
1661
+ let parsed = null;
1662
+ if (typeof d === "string") {
1663
+ const pd = _parseIsoDate(d.slice(0, 10));
1664
+ if (pd === null) {
1665
+ return "unknown";
1666
+ }
1667
+ parsed = pd;
1668
+ } else if (d instanceof PyTimestamp) {
1669
+ const pd = _parseIsoDate(d.pyStr.slice(0, 10));
1670
+ if (pd === null) {
1671
+ return "unknown";
1672
+ }
1673
+ parsed = pd;
1674
+ } else {
1675
+ return "unknown";
1676
+ }
1677
+ return `${parsed.y}Q${Math.floor((parsed.m - 1) / 3) + 1}`;
1678
+ }
1679
+ function _quarterly_stats() {
1680
+ const accepted = /* @__PURE__ */ new Map();
1681
+ for (const [, , entry] of _iter_curated_entries()) {
1682
+ const created = entry["created"] ?? entry["last_validated"];
1683
+ if (created !== void 0 && created !== null) {
1684
+ const q = _quarter_of(created);
1685
+ accepted.set(q, (accepted.get(q) ?? 0) + 1);
1686
+ }
1687
+ }
1688
+ const retired = /* @__PURE__ */ new Map();
1689
+ if (_isDir(INTAKE_ROOT)) {
1690
+ for (const jsonl of _globJsonl(INTAKE_ROOT)) {
1691
+ for (const obj of _readJsonl(jsonl)) {
1692
+ if (obj["type"] !== "supersede") {
1693
+ continue;
1694
+ }
1695
+ const ts = obj["ts"];
1696
+ if (typeof ts === "string") {
1697
+ const q = _quarter_of(ts);
1698
+ retired.set(q, (retired.get(q) ?? 0) + 1);
1699
+ }
1700
+ }
1701
+ }
1702
+ }
1703
+ const total = _iter_curated_entries().length;
1704
+ const overdue = _staleness_report().length;
1705
+ const rate = total ? overdue / total : 0;
1706
+ return {
1707
+ accepted_by_quarter: _mapToObj(accepted),
1708
+ retired_by_quarter: _mapToObj(retired),
1709
+ staleness_rate: new PyFloat2(_pyRound3(rate)),
1710
+ curated_total: total,
1711
+ curated_overdue: overdue
1712
+ };
1713
+ }
1714
+ function _pyRound3(x) {
1715
+ const factor = 1e3;
1716
+ const scaled = x * factor;
1717
+ const floor = Math.floor(scaled);
1718
+ const diff = scaled - floor;
1719
+ let rounded;
1720
+ if (Math.abs(diff - 0.5) < 1e-9) {
1721
+ rounded = floor % 2 === 0 ? floor : floor + 1;
1722
+ } else {
1723
+ rounded = Math.round(scaled);
1724
+ }
1725
+ return rounded / factor;
1726
+ }
1727
+ function _role_mode_stats() {
1728
+ const counts = /* @__PURE__ */ new Map();
1729
+ let files_scanned = 0;
1730
+ const unknown_modes = /* @__PURE__ */ new Set();
1731
+ for (const root of _MODE_SCAN_DIRS) {
1732
+ if (!_exists(root)) {
1733
+ continue;
1734
+ }
1735
+ for (const md of _rglobMd(root)) {
1736
+ let text;
1737
+ try {
1738
+ text = fs4.readFileSync(md, "utf-8");
1739
+ } catch {
1740
+ continue;
1741
+ }
1742
+ files_scanned += 1;
1743
+ _MODE_MARKER_PATTERN.lastIndex = 0;
1744
+ let m;
1745
+ while ((m = _MODE_MARKER_PATTERN.exec(text)) !== null) {
1746
+ const slug = m[1].toLowerCase();
1747
+ counts.set(slug, (counts.get(slug) ?? 0) + 1);
1748
+ if (!_KNOWN_MODES.has(slug)) {
1749
+ unknown_modes.add(slug);
1750
+ }
1751
+ }
1752
+ }
1753
+ }
1754
+ let total = 0;
1755
+ for (const v of counts.values()) {
1756
+ total += v;
1757
+ }
1758
+ return {
1759
+ total_markers: total,
1760
+ files_scanned,
1761
+ by_mode: _mapToObj(counts),
1762
+ unknown_modes: [...unknown_modes].sort()
1763
+ };
1764
+ }
1765
+ function _exists(p) {
1766
+ try {
1767
+ fs4.statSync(p);
1768
+ return true;
1769
+ } catch {
1770
+ return false;
1771
+ }
1772
+ }
1773
+ function _rglobMd(root) {
1774
+ const found = [];
1775
+ const walk = (dir) => {
1776
+ let entries;
1777
+ try {
1778
+ entries = fs4.readdirSync(dir, { withFileTypes: true });
1779
+ } catch {
1780
+ return;
1781
+ }
1782
+ for (const ent of entries) {
1783
+ const full = path4.join(dir, ent.name);
1784
+ if (ent.isDirectory()) {
1785
+ walk(full);
1786
+ } else if (ent.isFile() && ent.name.endsWith(".md")) {
1787
+ found.push(full);
1788
+ }
1789
+ }
1790
+ };
1791
+ walk(root);
1792
+ found.sort();
1793
+ return found;
1794
+ }
1795
+ function build_report() {
1796
+ const st = status();
1797
+ return {
1798
+ backend: {
1799
+ status: st.status,
1800
+ backend: st.backend,
1801
+ reason: st.reason
1802
+ },
1803
+ intake: _intake_stats(),
1804
+ staleness: _staleness_report(),
1805
+ quarterly: _quarterly_stats(),
1806
+ role_modes: _role_mode_stats()
1807
+ };
1808
+ }
1809
+ function _print_text(report2) {
1810
+ const out = [];
1811
+ const b = report2.backend;
1812
+ out.push(`Backend: ${b.status} (backend=${b.backend})`);
1813
+ if (b.reason) {
1814
+ out.push(` reason: ${b.reason}`);
1815
+ }
1816
+ const intake = report2.intake;
1817
+ out.push(`Intake: ${intake.total_active} active, ${intake.superseded} superseded`);
1818
+ for (const [t, n] of _sortedEntries(intake.by_type)) {
1819
+ out.push(` - ${t}: ${n}`);
1820
+ }
1821
+ const stale = report2.staleness;
1822
+ if (stale.length === 0) {
1823
+ out.push("Staleness: no curated entries past review_after_days");
1824
+ } else {
1825
+ out.push(`Staleness: ${stale.length} entrie(s) overdue`);
1826
+ for (const row of stale.slice(0, 5)) {
1827
+ out.push(` - ${row.id} (${row.type}) +${row.overdue_days}d ${row.file}`);
1828
+ }
1829
+ if (stale.length > 5) {
1830
+ out.push(` (+${stale.length - 5} more)`);
1831
+ }
1832
+ }
1833
+ const q = report2.quarterly;
1834
+ out.push(`Quarterly: staleness-rate=${_pyPercent1(q.staleness_rate.value)} (${q.curated_overdue}/${q.curated_total})`);
1835
+ if (Object.keys(q.accepted_by_quarter).length > 0) {
1836
+ const acc = _sortedEntries(q.accepted_by_quarter).map(([k, v]) => `${k}:${v}`).join(", ");
1837
+ out.push(` accepted: ${acc}`);
1838
+ }
1839
+ if (Object.keys(q.retired_by_quarter).length > 0) {
1840
+ const ret = _sortedEntries(q.retired_by_quarter).map(([k, v]) => `${k}:${v}`).join(", ");
1841
+ out.push(` retired: ${ret}`);
1842
+ }
1843
+ const rm = report2.role_modes;
1844
+ if (rm.files_scanned) {
1845
+ out.push(`Role modes: ${rm.total_markers} marker(s) in ${rm.files_scanned} file(s)`);
1846
+ if (Object.keys(rm.by_mode).length > 0) {
1847
+ const modes = _sortedEntries(rm.by_mode).map(([k, v]) => `${k}:${v}`).join(", ");
1848
+ out.push(` by mode: ${modes}`);
1849
+ }
1850
+ if (rm.unknown_modes.length > 0) {
1851
+ out.push(` unknown: ${rm.unknown_modes.join(", ")} (not in the six reserved slugs)`);
1852
+ }
1853
+ }
1854
+ process3.stdout.write(out.map((line) => `${line}
1855
+ `).join(""));
1856
+ }
1857
+ function _sortedEntries(obj) {
1858
+ return Object.entries(obj).sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
1859
+ }
1860
+ function _pyPercent1(x) {
1861
+ return `${(x * 100).toFixed(1)}%`;
1862
+ }
1863
+ function pyJsonDumps(value, indent) {
1864
+ return _escapeNonAscii2(_dumpsIndent(value, indent, 0));
1865
+ }
1866
+ function _dumpsIndent(value, indent, depth) {
1867
+ const pad = " ".repeat(indent * (depth + 1));
1868
+ const closePad = " ".repeat(indent * depth);
1869
+ if (value === null || value === void 0) {
1870
+ return "null";
1871
+ }
1872
+ if (typeof value === "boolean") {
1873
+ return value ? "true" : "false";
1874
+ }
1875
+ if (typeof value === "number") {
1876
+ return _jsonNum(value);
1877
+ }
1878
+ if (typeof value === "string") {
1879
+ return _jsonStrAscii(value);
1880
+ }
1881
+ if (value instanceof PyTimestamp) {
1882
+ return _jsonStrAscii(value.pyStr);
1883
+ }
1884
+ if (value instanceof PyFloat2) {
1885
+ return _jsonFloat(value.value);
1886
+ }
1887
+ if (Array.isArray(value)) {
1888
+ if (value.length === 0) {
1889
+ return "[]";
1890
+ }
1891
+ const items = value.map((v) => pad + _dumpsIndent(v, indent, depth + 1));
1892
+ return `[
1893
+ ${items.join(",\n")}
1894
+ ${closePad}]`;
1895
+ }
1896
+ if (_isPlainObject2(value)) {
1897
+ const keys = Object.keys(value);
1898
+ if (keys.length === 0) {
1899
+ return "{}";
1900
+ }
1901
+ const items = keys.map((k) => `${pad}${_jsonStrAscii(k)}: ${_dumpsIndent(value[k], indent, depth + 1)}`);
1902
+ return `{
1903
+ ${items.join(",\n")}
1904
+ ${closePad}}`;
1905
+ }
1906
+ return _jsonStrAscii(String(value));
1907
+ }
1908
+ function _jsonNum(n) {
1909
+ if (!Number.isFinite(n)) {
1910
+ if (Number.isNaN(n)) {
1911
+ return "NaN";
1912
+ }
1913
+ return n > 0 ? "Infinity" : "-Infinity";
1914
+ }
1915
+ return String(n);
1916
+ }
1917
+ function _jsonFloat(n) {
1918
+ if (!Number.isFinite(n)) {
1919
+ if (Number.isNaN(n)) {
1920
+ return "NaN";
1921
+ }
1922
+ return n > 0 ? "Infinity" : "-Infinity";
1923
+ }
1924
+ return Number.isInteger(n) ? `${n}.0` : String(n);
1925
+ }
1926
+ function _jsonStrAscii(s) {
1927
+ let out = '"';
1928
+ for (const ch of s) {
1929
+ const code = ch.codePointAt(0) ?? 0;
1930
+ switch (ch) {
1931
+ case '"':
1932
+ out += '\\"';
1933
+ break;
1934
+ case "\\":
1935
+ out += "\\\\";
1936
+ break;
1937
+ case "\n":
1938
+ out += "\\n";
1939
+ break;
1940
+ case "\r":
1941
+ out += "\\r";
1942
+ break;
1943
+ case " ":
1944
+ out += "\\t";
1945
+ break;
1946
+ case "\b":
1947
+ out += "\\b";
1948
+ break;
1949
+ case "\f":
1950
+ out += "\\f";
1951
+ break;
1952
+ default:
1953
+ if (code < 32) {
1954
+ out += `\\u${code.toString(16).padStart(4, "0")}`;
1955
+ } else {
1956
+ out += ch;
1957
+ }
1958
+ }
1959
+ }
1960
+ return `${out}"`;
1961
+ }
1962
+ function _escapeNonAscii2(s) {
1963
+ let out = "";
1964
+ for (const ch of s) {
1965
+ const code = ch.codePointAt(0) ?? 0;
1966
+ if (code > 127) {
1967
+ for (let i = 0; i < ch.length; i += 1) {
1968
+ out += `\\u${ch.charCodeAt(i).toString(16).padStart(4, "0")}`;
1969
+ }
1970
+ } else {
1971
+ out += ch;
1972
+ }
1973
+ }
1974
+ return out;
1975
+ }
1976
+ var _PROG2 = "memory_report.py";
1977
+ var _USAGE2 = "usage: memory_report.py [-h] [--format {text,json}]\n";
1978
+ function _argError2(msg) {
1979
+ process3.stderr.write(_USAGE2);
1980
+ process3.stderr.write(`${_PROG2}: error: ${msg}
1981
+ `);
1982
+ process3.exit(2);
1983
+ }
1984
+ function _parseArgs2(argv) {
1985
+ const args = { format: "text" };
1986
+ for (let i = 0; i < argv.length; i += 1) {
1987
+ const a = argv[i];
1988
+ if (a === "--format") {
1989
+ args.format = _checkChoice(argv[++i], ["text", "json"], "--format");
1990
+ } else if (a.startsWith("--format=")) {
1991
+ args.format = _checkChoice(a.slice("--format=".length), ["text", "json"], "--format");
1992
+ } else if (a === "-h" || a === "--help") {
1993
+ process3.stdout.write(_USAGE2);
1994
+ process3.exit(0);
1995
+ } else {
1996
+ _argError2(`unrecognized arguments: ${a}`);
1997
+ }
1998
+ }
1999
+ return args;
2000
+ }
2001
+ function _checkChoice(value, choices, flag) {
2002
+ if (value === void 0 || !choices.includes(value)) {
2003
+ _argError2(
2004
+ `argument ${flag}: invalid choice: '${value ?? ""}' (choose from ${choices.map((c) => `'${c}'`).join(", ")})`
2005
+ );
2006
+ }
2007
+ return value;
2008
+ }
2009
+ function main3() {
2010
+ const args = _parseArgs2(process3.argv.slice(2));
2011
+ const report2 = build_report();
2012
+ if (args.format === "json") {
2013
+ process3.stdout.write(`${pyJsonDumps(report2, 2)}
2014
+ `);
2015
+ } else {
2016
+ _print_text(report2);
2017
+ }
2018
+ return 0;
2019
+ }
2020
+ function _isCliEntry3() {
2021
+ if (true) {
2022
+ return false;
2023
+ }
2024
+ if (process3.argv[1] === void 0) {
2025
+ return false;
2026
+ }
2027
+ const argvUrl = pathToFileURL3(path4.resolve(process3.argv[1])).href;
2028
+ if (import.meta.url === argvUrl) {
2029
+ return true;
2030
+ }
2031
+ try {
2032
+ const here = fs4.realpathSync(fileURLToPath4(import.meta.url));
2033
+ const argv = fs4.realpathSync(path4.resolve(process3.argv[1]));
2034
+ return here === argv;
2035
+ } catch {
2036
+ return false;
2037
+ }
2038
+ }
2039
+ var _isMain2 = _isCliEntry3();
2040
+ if (_isMain2) {
2041
+ process3.exit(main3());
2042
+ }
2043
+
2044
+ // src/scripts/_lib/memory_fts_index.ts
2045
+ var MEMORY_INDEX_REL = path5.join("agents", "runtime", "state", "memory-index-v1.sqlite3");
2046
+ var SCHEMA_VERSION = 1;
2047
+ var CURATED_STATUS_EXCLUDE = /* @__PURE__ */ new Set(["deprecated", "archived", "superseded"]);
2048
+ var INDEX_TEXT_FIELDS = ["path", "key", "symptom", "feature", "rule", "body"];
2049
+ function resolveIndexPath(consumerRoot) {
2050
+ return path5.join(consumerRoot ?? process.cwd(), MEMORY_INDEX_REL);
2051
+ }
2052
+ function _pyStr(v) {
2053
+ if (v === void 0 || v === null) return "";
2054
+ if (typeof v === "string") return v;
2055
+ return String(v);
2056
+ }
2057
+ function _entryIndexText(entry) {
2058
+ const parts = [];
2059
+ for (const f of INDEX_TEXT_FIELDS) {
2060
+ const v = entry[f];
2061
+ if (typeof v === "string") parts.push(v);
2062
+ else if (Array.isArray(v)) for (const x of v) parts.push(_pyStr(x));
2063
+ }
2064
+ return parts.join(" | ");
2065
+ }
2066
+ function _isPlainObject3(v) {
2067
+ return typeof v === "object" && v !== null && !Array.isArray(v);
2068
+ }
2069
+ function _collectCurated(memoryRoot) {
2070
+ const out = [];
2071
+ const previous = MEMORY_ROOT;
2072
+ _setMemoryRoot(memoryRoot);
2073
+ try {
2074
+ for (const [file, mtype, entry] of _iter_curated_entries()) {
2075
+ const status2 = entry["status"];
2076
+ if (typeof status2 === "string" && CURATED_STATUS_EXCLUDE.has(status2)) continue;
2077
+ const id = typeof entry["id"] === "string" ? entry["id"] : "";
2078
+ if (id === "") continue;
2079
+ out.push({ id, mtype, source: "curated", path: file, text: _entryIndexText(entry) });
2080
+ }
2081
+ } finally {
2082
+ _setMemoryRoot(previous);
2083
+ }
2084
+ return out;
2085
+ }
2086
+ function _globJsonl2(intakeRoot) {
2087
+ let names;
2088
+ try {
2089
+ names = fs5.readdirSync(intakeRoot);
2090
+ } catch {
2091
+ return [];
2092
+ }
2093
+ return names.filter((n) => n.endsWith(".jsonl")).sort().map((n) => path5.join(intakeRoot, n));
2094
+ }
2095
+ function _collectIntake(intakeRoot) {
2096
+ const out = [];
2097
+ for (const jsonl of _globJsonl2(intakeRoot)) {
2098
+ const by_id = /* @__PURE__ */ new Map();
2099
+ const superseded = /* @__PURE__ */ new Set();
2100
+ let content;
2101
+ try {
2102
+ content = fs5.readFileSync(jsonl, "utf-8");
2103
+ } catch {
2104
+ continue;
2105
+ }
2106
+ for (let line of content.split(/\n/)) {
2107
+ line = line.trim();
2108
+ if (!line) continue;
2109
+ let obj;
2110
+ try {
2111
+ obj = JSON.parse(line);
2112
+ } catch {
2113
+ continue;
2114
+ }
2115
+ if (!_isPlainObject3(obj)) continue;
2116
+ if (obj["type"] === "supersede") {
2117
+ const target = obj["supersedes"];
2118
+ if (typeof target === "string") superseded.add(target);
2119
+ continue;
2120
+ }
2121
+ const eid = obj["id"];
2122
+ if (typeof eid === "string") by_id.set(eid, obj);
2123
+ }
2124
+ for (const [eid, obj] of by_id.entries()) {
2125
+ if (superseded.has(eid)) continue;
2126
+ const mtype = typeof obj["entry_type"] === "string" ? obj["entry_type"] : "";
2127
+ out.push({ id: eid, mtype, source: "intake", path: jsonl, text: _entryIndexText(obj) });
2128
+ }
2129
+ }
2130
+ return out;
2131
+ }
2132
+ function collectEntries(memoryRoot, intakeRoot) {
2133
+ return [..._collectCurated(memoryRoot), ..._collectIntake(intakeRoot)];
2134
+ }
2135
+ function _sourcesMaxMtimeMs(memoryRoot, intakeRoot) {
2136
+ let max = 0;
2137
+ const bump = (p) => {
2138
+ try {
2139
+ const st = fs5.statSync(p);
2140
+ if (st.mtimeMs > max) max = st.mtimeMs;
2141
+ } catch {
2142
+ }
2143
+ };
2144
+ const walk = (dir) => {
2145
+ let entries;
2146
+ try {
2147
+ entries = fs5.readdirSync(dir, { withFileTypes: true });
2148
+ } catch {
2149
+ return;
2150
+ }
2151
+ for (const e of entries) {
2152
+ const full = path5.join(dir, e.name);
2153
+ if (e.isDirectory()) walk(full);
2154
+ else if (e.isFile() && e.name.endsWith(".yml")) bump(full);
2155
+ }
2156
+ };
2157
+ let memEntries;
2158
+ try {
2159
+ memEntries = fs5.readdirSync(memoryRoot, { withFileTypes: true });
2160
+ for (const e of memEntries) {
2161
+ const full = path5.join(memoryRoot, e.name);
2162
+ if (e.isFile() && e.name.endsWith(".yml")) bump(full);
2163
+ else if (e.isDirectory()) walk(full);
2164
+ }
2165
+ } catch {
2166
+ }
2167
+ for (const f of _globJsonl2(intakeRoot)) bump(f);
2168
+ return max;
2169
+ }
2170
+ var META_KEY_SOURCES_MTIME = "sources_mtime_ms";
2171
+ function isStale(indexPath, memoryRoot, intakeRoot) {
2172
+ if (!fs5.existsSync(indexPath)) return true;
2173
+ let sqlite;
2174
+ try {
2175
+ sqlite = loadSqliteSync("memory_fts_index.isStale");
2176
+ } catch {
2177
+ return true;
2178
+ }
2179
+ let conn = null;
2180
+ try {
2181
+ conn = new sqlite.DatabaseSync(indexPath, { readOnly: true });
2182
+ if (readUserVersion(conn) !== SCHEMA_VERSION) return true;
2183
+ const row = conn.prepare("SELECT value FROM _meta WHERE key = ?").get(META_KEY_SOURCES_MTIME);
2184
+ const stored = row?.["value"];
2185
+ const storedMs = typeof stored === "string" ? Number(stored) : NaN;
2186
+ if (!Number.isFinite(storedMs)) return true;
2187
+ return _sourcesMaxMtimeMs(memoryRoot, intakeRoot) > storedMs;
2188
+ } catch {
2189
+ return true;
2190
+ } finally {
2191
+ try {
2192
+ conn?.close();
2193
+ } catch {
2194
+ }
2195
+ }
2196
+ }
2197
+ function buildIndex(indexPath, memoryRoot, intakeRoot) {
2198
+ fs5.mkdirSync(path5.dirname(indexPath), { recursive: true });
2199
+ try {
2200
+ fs5.rmSync(indexPath, { force: true });
2201
+ fs5.rmSync(`${indexPath}-wal`, { force: true });
2202
+ fs5.rmSync(`${indexPath}-shm`, { force: true });
2203
+ } catch {
2204
+ }
2205
+ const { DatabaseSync } = loadSqliteSync("memory_fts_index.buildIndex");
2206
+ const conn = new DatabaseSync(indexPath);
2207
+ let count = 0;
2208
+ try {
2209
+ conn.exec("PRAGMA journal_mode=WAL");
2210
+ conn.exec(
2211
+ "CREATE VIRTUAL TABLE entries USING fts5(id UNINDEXED, mtype UNINDEXED, source UNINDEXED, path UNINDEXED, text)"
2212
+ );
2213
+ conn.exec("CREATE TABLE _meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
2214
+ const stmt = conn.prepare(
2215
+ "INSERT INTO entries (id, mtype, source, path, text) VALUES (?, ?, ?, ?, ?)"
2216
+ );
2217
+ for (const e of collectEntries(memoryRoot, intakeRoot)) {
2218
+ stmt.run(e.id, e.mtype, e.source, e.path, e.text);
2219
+ count += 1;
2220
+ }
2221
+ const metaStmt = conn.prepare("INSERT INTO _meta (key, value) VALUES (?, ?)");
2222
+ metaStmt.run(META_KEY_SOURCES_MTIME, String(_sourcesMaxMtimeMs(memoryRoot, intakeRoot)));
2223
+ stampUserVersion(conn, SCHEMA_VERSION);
2224
+ conn.exec("PRAGMA wal_checkpoint(TRUNCATE)");
2225
+ conn.exec("PRAGMA journal_mode=DELETE");
2226
+ } finally {
2227
+ conn.close();
2228
+ }
2229
+ return { indexPath, entriesIndexed: count };
2230
+ }
2231
+ function ensureIndex(indexPath, memoryRoot, intakeRoot) {
2232
+ if (!probeFts5Sync()) return false;
2233
+ try {
2234
+ if (isStale(indexPath, memoryRoot, intakeRoot)) {
2235
+ buildIndex(indexPath, memoryRoot, intakeRoot);
2236
+ }
2237
+ return true;
2238
+ } catch {
2239
+ return false;
2240
+ }
2241
+ }
2242
+ function _sanitizeFtsClause(key) {
2243
+ return key.replace(/[^\p{L}\p{N}]+/gu, " ").trim();
2244
+ }
2245
+ function queryIndex(indexPath, keys, types) {
2246
+ const clauses = keys.map(_sanitizeFtsClause).filter((c) => c.length > 0);
2247
+ if (clauses.length === 0) return [];
2248
+ let sqlite;
2249
+ try {
2250
+ sqlite = loadSqliteSync("memory_fts_index.queryIndex");
2251
+ } catch {
2252
+ return [];
2253
+ }
2254
+ let conn = null;
2255
+ try {
2256
+ conn = new sqlite.DatabaseSync(indexPath, { readOnly: true });
2257
+ const matchQuery = clauses.map((c) => `(${c})`).join(" OR ");
2258
+ const typeFilter = types.length > 0 ? `AND mtype IN (${types.map(() => "?").join(",")})` : "";
2259
+ const rows = conn.prepare(
2260
+ `SELECT id, mtype, source, path, bm25(entries) AS score
2261
+ FROM entries
2262
+ WHERE entries MATCH ? ${typeFilter}
2263
+ ORDER BY score ASC`
2264
+ ).all(matchQuery, ...types);
2265
+ return rows.map((r) => {
2266
+ const bm25 = typeof r["score"] === "number" ? r["score"] : Number(r["score"] ?? 0);
2267
+ return {
2268
+ id: String(r["id"]),
2269
+ mtype: String(r["mtype"]),
2270
+ source: r["source"] ?? "curated",
2271
+ path: String(r["path"]),
2272
+ relevance: Math.max(0, -bm25)
2273
+ };
2274
+ });
2275
+ } catch {
2276
+ return [];
2277
+ } finally {
2278
+ try {
2279
+ conn?.close();
2280
+ } catch {
2281
+ }
2282
+ }
2283
+ }
2284
+
2285
+ // src/scripts/learning_sidecar.ts
2286
+ import { createHash } from "node:crypto";
2287
+ import * as fs6 from "node:fs";
2288
+ import * as path6 from "node:path";
2289
+ import process4 from "node:process";
2290
+ import { fileURLToPath as fileURLToPath5 } from "node:url";
2291
+ var PROG = "learning_sidecar.ts";
2292
+ var HALF_LIFE_DAYS = 30;
2293
+ var MIN_CORROBORATIONS = 2;
2294
+ var SIDECAR_NAME = ".agent-learning.json";
2295
+ var LESSONS_NAME = "LESSONS.md";
2296
+ var SCHEMA_VERSION2 = 1;
2297
+ function _parseTsMs(ts) {
2298
+ const t = Date.parse(ts);
2299
+ return Number.isNaN(t) ? null : t;
2300
+ }
2301
+ function _bodyHash(body) {
2302
+ return createHash("sha256").update(body.trim().toLowerCase()).digest("hex").slice(0, 12);
2303
+ }
2304
+ function _globSignalFiles(dir) {
2305
+ let names;
2306
+ try {
2307
+ names = fs6.readdirSync(dir);
2308
+ } catch {
2309
+ return [];
2310
+ }
2311
+ return names.filter((n) => n.startsWith("signals-") && n.endsWith(".jsonl")).sort().map((n) => path6.join(dir, n));
2312
+ }
2313
+ function readSignals(intakeDir) {
2314
+ const out = [];
2315
+ for (const file of _globSignalFiles(intakeDir)) {
2316
+ let content;
2317
+ try {
2318
+ content = fs6.readFileSync(file, "utf-8");
2319
+ } catch {
2320
+ continue;
2321
+ }
2322
+ for (const raw of content.split("\n")) {
2323
+ const line = raw.trim();
2324
+ if (!line) continue;
2325
+ let obj;
2326
+ try {
2327
+ obj = JSON.parse(line);
2328
+ } catch {
2329
+ continue;
2330
+ }
2331
+ if (typeof obj !== "object" || obj === null) continue;
2332
+ const r = obj;
2333
+ const ts = typeof r["ts"] === "string" ? r["ts"] : "";
2334
+ const tsMs = _parseTsMs(ts);
2335
+ const entryType = typeof r["entry_type"] === "string" ? r["entry_type"] : "";
2336
+ const p = typeof r["path"] === "string" ? r["path"] : "";
2337
+ const body = typeof r["body"] === "string" ? r["body"] : "";
2338
+ if (tsMs === null || !entryType || !p || !body) continue;
2339
+ const pol = r["polarity"] === "dead_end" ? "dead_end" : "preferred";
2340
+ out.push({
2341
+ id: typeof r["id"] === "string" ? r["id"] : "",
2342
+ ts,
2343
+ tsMs,
2344
+ origin: typeof r["origin"] === "string" ? r["origin"] : "unknown",
2345
+ entryType,
2346
+ path: p,
2347
+ body,
2348
+ polarity: pol
2349
+ });
2350
+ }
2351
+ }
2352
+ return out;
2353
+ }
2354
+ function aggregate(signals, nowMs) {
2355
+ const halfLifeMs = HALF_LIFE_DAYS * 24 * 60 * 60 * 1e3;
2356
+ const groups = /* @__PURE__ */ new Map();
2357
+ for (const s of signals) {
2358
+ const gk = `${s.entryType}\0${s.path}`;
2359
+ let g = groups.get(gk);
2360
+ if (g === void 0) {
2361
+ g = { entryType: s.entryType, path: s.path, claims: /* @__PURE__ */ new Map() };
2362
+ groups.set(gk, g);
2363
+ }
2364
+ const ck = _bodyHash(s.body);
2365
+ let c = g.claims.get(ck);
2366
+ if (c === void 0) {
2367
+ c = { body: s.body, polarity: s.polarity, origins: /* @__PURE__ */ new Set(), score: 0, lastMs: -1, lastTs: "" };
2368
+ g.claims.set(ck, c);
2369
+ }
2370
+ const ageMs = Math.max(0, nowMs - s.tsMs);
2371
+ c.score += Math.pow(0.5, ageMs / halfLifeMs);
2372
+ c.origins.add(s.origin);
2373
+ if (s.tsMs > c.lastMs) {
2374
+ c.lastMs = s.tsMs;
2375
+ c.lastTs = s.ts;
2376
+ c.polarity = s.polarity;
2377
+ }
2378
+ }
2379
+ const lessons = [];
2380
+ for (const g of groups.values()) {
2381
+ const claims = [...g.claims.values()].sort(
2382
+ (a, b) => b.score - a.score || b.lastMs - a.lastMs || (a.body < b.body ? -1 : 1)
2383
+ );
2384
+ const top = claims[0];
2385
+ if (top === void 0) continue;
2386
+ const corroborations = top.origins.size;
2387
+ if (corroborations < MIN_CORROBORATIONS) continue;
2388
+ const secondCorroborated = claims.slice(1).some((c) => c.origins.size >= MIN_CORROBORATIONS);
2389
+ let verdict;
2390
+ if (secondCorroborated) verdict = "contested";
2391
+ else if (top.polarity === "dead_end") verdict = "dead_end";
2392
+ else verdict = "preferred";
2393
+ lessons.push({
2394
+ entry_type: g.entryType,
2395
+ path: g.path,
2396
+ verdict,
2397
+ score: Math.round(top.score * 1e3) / 1e3,
2398
+ corroborations,
2399
+ last_ts: top.lastTs,
2400
+ body: top.body
2401
+ });
2402
+ }
2403
+ lessons.sort(
2404
+ (a, b) => a.entry_type < b.entry_type ? -1 : a.entry_type > b.entry_type ? 1 : a.path < b.path ? -1 : a.path > b.path ? 1 : 0
2405
+ );
2406
+ return lessons;
2407
+ }
2408
+ function buildSidecar(intakeDir, nowIso) {
2409
+ const nowMs = _parseTsMs(nowIso) ?? 0;
2410
+ const lessons = aggregate(readSignals(intakeDir), nowMs);
2411
+ return { schema_version: SCHEMA_VERSION2, generated_at: nowIso, lessons };
2412
+ }
2413
+ function renderLessonsMd(sidecar) {
2414
+ const dead = sidecar.lessons.filter((l) => l.verdict === "dead_end");
2415
+ const contested = sidecar.lessons.filter((l) => l.verdict === "contested");
2416
+ const L = [];
2417
+ L.push("# Lessons (generated \u2014 do not edit)");
2418
+ L.push("");
2419
+ L.push(`> Regenerated from the intake signal log by \`${PROG}\`. Local-only,`);
2420
+ L.push("> intake-derived; never a substitute for curated memory. Proven lessons");
2421
+ L.push("> should be MANUALLY promoted into `agents/memory/<type>/*.yml`.");
2422
+ L.push("");
2423
+ L.push("## Known dead ends \u2014 don't re-derive");
2424
+ L.push("");
2425
+ if (dead.length === 0) {
2426
+ L.push("_None corroborated yet._");
2427
+ } else {
2428
+ for (const l of dead) L.push(`- \`${l.entry_type}\` @ \`${l.path}\` \u2014 ${l.body}`);
2429
+ }
2430
+ L.push("");
2431
+ L.push("## Contested \u2014 recency-resolved, verify before relying");
2432
+ L.push("");
2433
+ if (contested.length === 0) {
2434
+ L.push("_None._");
2435
+ } else {
2436
+ for (const l of contested) L.push(`- \`${l.entry_type}\` @ \`${l.path}\` \u2014 ${l.body}`);
2437
+ }
2438
+ L.push("");
2439
+ return L.join("\n");
2440
+ }
2441
+ function main4(argv) {
2442
+ let intakeDir = path6.join("agents", "memory", "intake");
2443
+ let outDir = path6.join("agents", "memory");
2444
+ let nowIso = "";
2445
+ let write = false;
2446
+ let format = "text";
2447
+ for (let i = 0; i < argv.length; i++) {
2448
+ const a = argv[i];
2449
+ if (a === "--intake-dir") intakeDir = argv[++i] ?? intakeDir;
2450
+ else if (a === "--out-dir") outDir = argv[++i] ?? outDir;
2451
+ else if (a === "--now") nowIso = argv[++i] ?? "";
2452
+ else if (a === "--write") write = true;
2453
+ else if (a === "--format") format = argv[++i] ?? "text";
2454
+ else if (a === "-h" || a === "--help") {
2455
+ process4.stdout.write(
2456
+ `usage: ${PROG} [--intake-dir DIR] [--out-dir DIR] [--now ISO] [--write] [--format text|json]
2457
+ `
2458
+ );
2459
+ return 0;
2460
+ } else {
2461
+ process4.stderr.write(`${PROG}: error: unknown argument ${a}
2462
+ `);
2463
+ return 2;
2464
+ }
2465
+ }
2466
+ if (!nowIso) {
2467
+ nowIso = (/* @__PURE__ */ new Date(0)).toISOString();
2468
+ }
2469
+ let sidecar;
2470
+ try {
2471
+ sidecar = buildSidecar(intakeDir, nowIso);
2472
+ } catch (exc) {
2473
+ process4.stderr.write(`${PROG}: internal error: ${String(exc)}
2474
+ `);
2475
+ return 3;
2476
+ }
2477
+ if (write) {
2478
+ fs6.mkdirSync(outDir, { recursive: true });
2479
+ fs6.writeFileSync(path6.join(outDir, SIDECAR_NAME), JSON.stringify(sidecar, null, 2) + "\n");
2480
+ fs6.writeFileSync(path6.join(outDir, LESSONS_NAME), renderLessonsMd(sidecar));
2481
+ }
2482
+ if (format === "json") {
2483
+ process4.stdout.write(JSON.stringify(sidecar, null, 2) + "\n");
2484
+ return 0;
2485
+ }
2486
+ const byVerdict = { preferred: 0, contested: 0, dead_end: 0 };
2487
+ for (const l of sidecar.lessons) byVerdict[l.verdict] += 1;
2488
+ process4.stdout.write(
2489
+ `${PROG}: ${sidecar.lessons.length} lesson(s) \u2014 ${byVerdict.preferred} preferred, ${byVerdict.contested} contested, ${byVerdict.dead_end} dead-end${write ? " (written)" : ""}
2490
+ `
2491
+ );
2492
+ return 0;
2493
+ }
2494
+ var _bundled = true;
2495
+ if (!_bundled && fileURLToPath5(import.meta.url) === path6.resolve(process4.argv[1] ?? "")) {
2496
+ process4.exit(main4(process4.argv.slice(2)));
2497
+ }
2498
+
2499
+ // src/scripts/memory_lookup.ts
2500
+ var PYYAML_DATE_ONLY_RE2 = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/;
2501
+ var PYYAML_TIMESTAMP_RE2 = /^(?:[0-9]{4}-[0-9]{2}-[0-9]{2}|[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}(?:[Tt]|[ \t]+)[0-9]{1,2}:[0-9]{2}:[0-9]{2}(?:\.[0-9]*)?(?:[ \t]*(?:Z|[-+][0-9]{1,2}(?::[0-9]{2})?))?)$/;
2502
+ var PyTimestamp2 = class {
2503
+ constructor(pyStr) {
2504
+ this.pyStr = pyStr;
2505
+ }
2506
+ pyStr;
2507
+ toString() {
2508
+ return this.pyStr;
2509
+ }
2510
+ };
2511
+ var PyFloat3 = class {
2512
+ constructor(value) {
2513
+ this.value = value;
2514
+ }
2515
+ value;
2516
+ };
2517
+ var MEMORY_ROOT2 = path7.join("agents", "memory");
2518
+ var INTAKE_ROOT2 = path7.join(MEMORY_ROOT2, "intake");
2519
+ var KNOWLEDGE_ROOT = path7.join(MEMORY_ROOT2, "knowledge");
2520
+ var _LEX_TYPE_FILES_MAX = 200;
2521
+ var _LEX_CORPUS_FILES_MAX = 500;
2522
+ var _LEX_SOURCE_FACTOR = { curated: 1, intake: 0.9, knowledge: 0.85 };
2523
+ var _lexTripwireOverride = null;
2524
+ var _corpusFileCounts = null;
2525
+ var _learningOverlay;
2526
+ function _loadLearningOverlay() {
2527
+ if (_learningOverlay !== void 0) return _learningOverlay;
2528
+ const file = path7.join(MEMORY_ROOT2, SIDECAR_NAME);
2529
+ try {
2530
+ const parsed = JSON.parse(fs7.readFileSync(file, "utf-8"));
2531
+ const lessons = _isPlainObject4(parsed) ? parsed["lessons"] : null;
2532
+ if (!Array.isArray(lessons)) {
2533
+ _learningOverlay = null;
2534
+ return null;
2535
+ }
2536
+ const map = /* @__PURE__ */ new Map();
2537
+ for (const l of lessons) {
2538
+ if (!_isPlainObject4(l)) continue;
2539
+ const t = l["entry_type"];
2540
+ const p = l["path"];
2541
+ const v = l["verdict"];
2542
+ if (typeof t === "string" && typeof p === "string" && (v === "preferred" || v === "contested" || v === "dead_end")) {
2543
+ map.set(`${t}\0${p}`, v);
2544
+ }
2545
+ }
2546
+ _learningOverlay = map.size > 0 ? map : null;
2547
+ } catch {
2548
+ _learningOverlay = null;
2549
+ }
2550
+ return _learningOverlay;
2551
+ }
2552
+ function _learningVerdictFor(mtype, entry) {
2553
+ const overlay = _loadLearningOverlay();
2554
+ if (overlay === null) return void 0;
2555
+ const anchor = typeof entry["path"] === "string" ? entry["path"] : entry["key"];
2556
+ if (typeof anchor !== "string" || anchor === "") return void 0;
2557
+ return overlay.get(`${mtype}\0${anchor}`);
2558
+ }
2559
+ var CURATED_TYPES2 = /* @__PURE__ */ new Set([
2560
+ "ownership",
2561
+ "historical-patterns",
2562
+ "domain-invariants",
2563
+ "incident-learnings",
2564
+ "product-rules"
2565
+ ]);
2566
+ var KNOWLEDGE_TYPE = "knowledge";
2567
+ var CROSS_REPO_TYPE = "cross-repo";
2568
+ var _CURATED_STATUS_EXCLUDE = /* @__PURE__ */ new Set(["deprecated", "archived", "superseded"]);
2569
+ function _statusExcluded(entry) {
2570
+ const status2 = entry["status"];
2571
+ return typeof status2 === "string" && _CURATED_STATUS_EXCLUDE.has(status2);
2572
+ }
2573
+ var Hit = class {
2574
+ id;
2575
+ type;
2576
+ source;
2577
+ // HitSource, kept as string to mirror the Python dataclass
2578
+ path;
2579
+ // file (or logical locator) that produced the hit
2580
+ score;
2581
+ // naive, content-match based [0..1]
2582
+ entry;
2583
+ constructor(id, type, source, p, score, entry = {}) {
2584
+ this.id = id;
2585
+ this.type = type;
2586
+ this.source = source;
2587
+ this.path = p;
2588
+ this.score = score;
2589
+ this.entry = entry;
2590
+ }
2591
+ as_dict() {
2592
+ return {
2593
+ id: this.id,
2594
+ type: this.type,
2595
+ source: this.source,
2596
+ path: this.path,
2597
+ // score is a Python float — render `0.0` not `0` for whole numbers.
2598
+ score: new PyFloat3(this.score),
2599
+ entry: this.entry
2600
+ };
2601
+ }
2602
+ };
2603
+ function _isPlainObject4(v) {
2604
+ return typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof PyTimestamp2) && !(v instanceof PyFloat3);
2605
+ }
2606
+ function _isFile2(p) {
2607
+ try {
2608
+ return fs7.statSync(p).isFile();
2609
+ } catch {
2610
+ return false;
2611
+ }
2612
+ }
2613
+ function _isDir2(p) {
2614
+ try {
2615
+ return fs7.statSync(p).isDirectory();
2616
+ } catch {
2617
+ return false;
2618
+ }
2619
+ }
2620
+ function _load_yaml2(p) {
2621
+ const text = fs7.readFileSync(p, "utf-8");
2622
+ const doc = (0, import_yaml2.parseDocument)(text, { version: "1.1", prettyErrors: false });
2623
+ if (doc.errors.length > 0) {
2624
+ const err = doc.errors[0];
2625
+ throw new Error(err ? err.message : "YAML parse error");
2626
+ }
2627
+ import_yaml2.default.visit(doc, {
2628
+ Scalar(_key, node) {
2629
+ if (!node.range) {
2630
+ return;
2631
+ }
2632
+ const raw = text.slice(node.range[0], node.range[1]);
2633
+ if (node.value instanceof Date) {
2634
+ if (PYYAML_DATE_ONLY_RE2.test(raw)) {
2635
+ node.value = new PyTimestamp2(raw);
2636
+ } else if (PYYAML_TIMESTAMP_RE2.test(raw)) {
2637
+ node.value = new PyTimestamp2(_pyYamlDatetimeStr2(raw));
2638
+ }
2639
+ return;
2640
+ }
2641
+ if (typeof node.value === "boolean" && node.type === "PLAIN" && /^[ynYN]$/.test(raw)) {
2642
+ node.value = raw;
2643
+ }
2644
+ }
2645
+ });
2646
+ const data = doc.toJS({ mapAsMap: false });
2647
+ return data == null ? {} : data;
2648
+ }
2649
+ function _pyYamlDatetimeStr2(raw) {
2650
+ const m = /^(\d{4})-(\d{1,2})-(\d{1,2})(?:[Tt]|[ \t]+)(\d{1,2}):(\d{2}):(\d{2})(?:\.(\d*))?(?:[ \t]*(Z|[-+]\d{1,2}(?::\d{2})?))?$/.exec(
2651
+ raw
2652
+ );
2653
+ if (!m) {
2654
+ return raw;
2655
+ }
2656
+ const [, y, mo, d, h, mi, s, frac, tz] = m;
2657
+ const date = `${y}-${_pad22(mo)}-${_pad22(d)}`;
2658
+ let time = `${_pad22(h)}:${mi}:${s}`;
2659
+ if (frac && /[1-9]/.test(frac)) {
2660
+ time += `.${(frac + "000000").slice(0, 6)}`;
2661
+ }
2662
+ let off = "";
2663
+ if (tz) {
2664
+ if (tz === "Z") {
2665
+ off = "+00:00";
2666
+ } else {
2667
+ const tm = /^([-+])(\d{1,2})(?::(\d{2}))?$/.exec(tz);
2668
+ if (tm) {
2669
+ off = `${tm[1]}${_pad22(tm[2])}:${tm[3] ?? "00"}`;
2670
+ }
2671
+ }
2672
+ }
2673
+ return `${date} ${time}${off}`;
2674
+ }
2675
+ function _pad22(v) {
2676
+ return v.length === 1 ? `0${v}` : v;
2677
+ }
2678
+ function* _iter_curated_entries2(mtype) {
2679
+ const type_dir = path7.join(MEMORY_ROOT2, mtype);
2680
+ const single_file = path7.join(MEMORY_ROOT2, `${mtype}.yml`);
2681
+ if (_isFile2(single_file)) {
2682
+ const data = _load_yaml2(single_file);
2683
+ const entries = _isPlainObject4(data) ? data["entries"] : null;
2684
+ for (const e of Array.isArray(entries) ? entries : []) {
2685
+ if (_isPlainObject4(e)) {
2686
+ if (_statusExcluded(e)) {
2687
+ continue;
2688
+ }
2689
+ yield [single_file, e];
2690
+ }
2691
+ }
2692
+ }
2693
+ if (_isDir2(type_dir)) {
2694
+ for (const yml of _rglobYmlSorted(type_dir)) {
2695
+ const dataRaw = _load_yaml2(yml);
2696
+ const data = dataRaw == null ? {} : dataRaw;
2697
+ const entries = _isPlainObject4(data) ? data["entries"] : void 0;
2698
+ if (Array.isArray(entries)) {
2699
+ for (const e of entries) {
2700
+ if (_isPlainObject4(e)) {
2701
+ if (_statusExcluded(e)) {
2702
+ continue;
2703
+ }
2704
+ yield [yml, e];
2705
+ }
2706
+ }
2707
+ } else if (_isPlainObject4(data) && data["id"]) {
2708
+ if (_statusExcluded(data)) {
2709
+ continue;
2710
+ }
2711
+ yield [yml, data];
2712
+ }
2713
+ }
2714
+ }
2715
+ }
2716
+ function* _iter_curated_entries_all(mtype) {
2717
+ const type_dir = path7.join(MEMORY_ROOT2, mtype);
2718
+ const single_file = path7.join(MEMORY_ROOT2, `${mtype}.yml`);
2719
+ if (_isFile2(single_file)) {
2720
+ const data = _load_yaml2(single_file);
2721
+ const entries = _isPlainObject4(data) ? data["entries"] : null;
2722
+ for (const e of Array.isArray(entries) ? entries : []) {
2723
+ if (_isPlainObject4(e)) {
2724
+ yield [single_file, e];
2725
+ }
2726
+ }
2727
+ }
2728
+ if (_isDir2(type_dir)) {
2729
+ for (const yml of _rglobYmlSorted(type_dir)) {
2730
+ const dataRaw = _load_yaml2(yml);
2731
+ const data = dataRaw == null ? {} : dataRaw;
2732
+ const entries = _isPlainObject4(data) ? data["entries"] : void 0;
2733
+ if (Array.isArray(entries)) {
2734
+ for (const e of entries) {
2735
+ if (_isPlainObject4(e)) {
2736
+ yield [yml, e];
2737
+ }
2738
+ }
2739
+ } else if (_isPlainObject4(data) && data["id"]) {
2740
+ yield [yml, data];
2741
+ }
2742
+ }
2743
+ }
2744
+ }
2745
+ function* _iter_intake_entries(mtype) {
2746
+ if (!_isDir2(INTAKE_ROOT2)) {
2747
+ return;
2748
+ }
2749
+ for (const jsonl of _globJsonlSorted(INTAKE_ROOT2)) {
2750
+ const by_id = /* @__PURE__ */ new Map();
2751
+ const superseded = /* @__PURE__ */ new Set();
2752
+ const content = fs7.readFileSync(jsonl, "utf-8");
2753
+ for (let line of content.split(/\n/)) {
2754
+ line = line.trim();
2755
+ if (!line) {
2756
+ continue;
2757
+ }
2758
+ let obj;
2759
+ try {
2760
+ obj = JSON.parse(line);
2761
+ } catch {
2762
+ continue;
2763
+ }
2764
+ if (!_isPlainObject4(obj)) {
2765
+ continue;
2766
+ }
2767
+ if (obj["type"] === "supersede") {
2768
+ const target = obj["supersedes"];
2769
+ if (typeof target === "string") {
2770
+ superseded.add(target);
2771
+ }
2772
+ continue;
2773
+ }
2774
+ const eid = obj["id"];
2775
+ if (typeof eid === "string") {
2776
+ by_id.set(eid, obj);
2777
+ }
2778
+ }
2779
+ for (const [eid, obj] of by_id.entries()) {
2780
+ if (superseded.has(eid)) {
2781
+ continue;
2782
+ }
2783
+ if (mtype && obj["entry_type"] && obj["entry_type"] !== mtype) {
2784
+ continue;
2785
+ }
2786
+ yield [jsonl, obj];
2787
+ }
2788
+ }
2789
+ }
2790
+ function* _iter_knowledge_entries() {
2791
+ if (!_isDir2(KNOWLEDGE_ROOT)) {
2792
+ return;
2793
+ }
2794
+ for (const ingest_dir of _iterdirSorted(KNOWLEDGE_ROOT)) {
2795
+ if (!_isDir2(ingest_dir)) {
2796
+ continue;
2797
+ }
2798
+ const manifest_path = path7.join(ingest_dir, "manifest.json");
2799
+ let manifest = {};
2800
+ if (_isFile2(manifest_path)) {
2801
+ try {
2802
+ const parsed = JSON.parse(fs7.readFileSync(manifest_path, "utf-8"));
2803
+ manifest = _isPlainObject4(parsed) ? parsed : {};
2804
+ } catch {
2805
+ manifest = {};
2806
+ }
2807
+ }
2808
+ const ingest_id = String(manifest["ingest_id"] || path7.basename(ingest_dir));
2809
+ const source = String(manifest["source"] || "");
2810
+ const pinned = Boolean(manifest["pinned"] ?? false);
2811
+ const chunks_dir = path7.join(ingest_dir, "chunks");
2812
+ if (!_isDir2(chunks_dir)) {
2813
+ continue;
2814
+ }
2815
+ for (const chunk of _globMdSorted(chunks_dir)) {
2816
+ let body;
2817
+ try {
2818
+ body = fs7.readFileSync(chunk, "utf-8");
2819
+ } catch {
2820
+ continue;
2821
+ }
2822
+ const entry = {
2823
+ id: `${ingest_id}:${_stem(chunk)}`,
2824
+ ingest_id,
2825
+ source,
2826
+ path: source,
2827
+ body,
2828
+ pinned,
2829
+ source_kind: "knowledge"
2830
+ };
2831
+ yield [chunk, entry];
2832
+ }
2833
+ }
2834
+ }
2835
+ function _score(entry, keys) {
2836
+ if (keys.length === 0) {
2837
+ return 0.1;
2838
+ }
2839
+ const hay_parts = [];
2840
+ for (const field_name of ["path", "key", "symptom", "feature", "rule", "body"]) {
2841
+ const v = entry[field_name];
2842
+ if (typeof v === "string") {
2843
+ hay_parts.push(v);
2844
+ } else if (Array.isArray(v)) {
2845
+ for (const x of v) {
2846
+ hay_parts.push(_pyStr2(x));
2847
+ }
2848
+ }
2849
+ }
2850
+ const hay = hay_parts.join(" | ").toLowerCase();
2851
+ let best = 0;
2852
+ for (const k of keys) {
2853
+ const kl = k.toLowerCase();
2854
+ if (_fnmatch(hay, `*${kl}*`)) {
2855
+ best = Math.max(best, 0.8);
2856
+ } else if (hay.includes(kl)) {
2857
+ best = Math.max(best, 0.6);
2858
+ }
2859
+ }
2860
+ return best;
2861
+ }
2862
+ function _cross_repo_hits(keys, limit) {
2863
+ const query = keys.filter((k) => k).join(" ").trim();
2864
+ if (!query) {
2865
+ return [];
2866
+ }
2867
+ void limit;
2868
+ return [];
2869
+ }
2870
+ function _today() {
2871
+ const now = /* @__PURE__ */ new Date();
2872
+ return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
2873
+ }
2874
+ function _parseIsoDate2(s) {
2875
+ const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
2876
+ if (!m) {
2877
+ return null;
2878
+ }
2879
+ const y = Number(m[1]);
2880
+ const mo = Number(m[2]);
2881
+ const d = Number(m[3]);
2882
+ if (mo < 1 || mo > 12 || d < 1 || d > 31) {
2883
+ return null;
2884
+ }
2885
+ const date = new Date(Date.UTC(y, mo - 1, d));
2886
+ if (date.getUTCFullYear() !== y || date.getUTCMonth() !== mo - 1 || date.getUTCDate() !== d) {
2887
+ return null;
2888
+ }
2889
+ return date;
2890
+ }
2891
+ function _pyStrDateLike(lv) {
2892
+ if (lv instanceof PyTimestamp2) {
2893
+ return lv.pyStr;
2894
+ }
2895
+ return _pyStr2(lv);
2896
+ }
2897
+ function _isStale(entry, today) {
2898
+ const lv = entry["last_validated"];
2899
+ const rad = entry["review_after_days"];
2900
+ if (!lv || typeof rad !== "number" || !Number.isInteger(rad)) {
2901
+ return false;
2902
+ }
2903
+ const validated = _parseIsoDate2(_pyStrDateLike(lv));
2904
+ if (validated === null) {
2905
+ return false;
2906
+ }
2907
+ const days = Math.round((today.getTime() - validated.getTime()) / 864e5);
2908
+ return days > rad;
2909
+ }
2910
+ function _entry_id(mtype, file, entry, ordinals) {
2911
+ const ordinal = ordinals.get(file) ?? 0;
2912
+ ordinals.set(file, ordinal + 1);
2913
+ const own = entry["id"];
2914
+ if (typeof own === "string" && own.trim() !== "") {
2915
+ return own;
2916
+ }
2917
+ if (own !== void 0 && own !== null && String(own).trim() !== "") {
2918
+ return String(own);
2919
+ }
2920
+ return `${mtype}:${path7.basename(file)}:${ordinal}`;
2921
+ }
2922
+ function _entry_title(entry, id) {
2923
+ for (const field of ["title", "key", "path"]) {
2924
+ const v = entry[field];
2925
+ if (typeof v === "string" && v.trim() !== "") {
2926
+ return v.trim().slice(0, 120);
2927
+ }
2928
+ }
2929
+ const body = entry["body"];
2930
+ if (typeof body === "string" && body.trim() !== "") {
2931
+ const first = body.trim().split("\n")[0];
2932
+ return first.trim().slice(0, 120);
2933
+ }
2934
+ return id;
2935
+ }
2936
+ function _compact_line(hit, safeEntry) {
2937
+ const head = _entry_title(safeEntry, hit.id).replace(/\s+/g, " ").trim();
2938
+ const score = hit.score.toFixed(2);
2939
+ return `HIT ${hit.type}/${hit.id} [src=${hit.path} score=${score}] ${head}`;
2940
+ }
2941
+ var _tokenizer;
2942
+ function _lazy_tokenizer() {
2943
+ if (_tokenizer === void 0) {
2944
+ try {
2945
+ const req = createRequire2(import.meta.url);
2946
+ _tokenizer = req("./_lib/token_count.js");
2947
+ } catch {
2948
+ _tokenizer = null;
2949
+ }
2950
+ }
2951
+ return _tokenizer;
2952
+ }
2953
+ function _entry_tokens_estimate(envelope_entry) {
2954
+ const serialized = JSON.stringify(envelope_entry);
2955
+ const tok = _lazy_tokenizer();
2956
+ if (tok !== null && tok !== void 0 && tok.TIKTOKEN_AVAILABLE) {
2957
+ return tok.gpt_tokens(serialized).tokens;
2958
+ }
2959
+ return Math.ceil(serialized.length / 4);
2960
+ }
2961
+ function _countMdFiles(dir) {
2962
+ let entries;
2963
+ try {
2964
+ entries = fs7.readdirSync(dir, { withFileTypes: true });
2965
+ } catch {
2966
+ return 0;
2967
+ }
2968
+ let n = 0;
2969
+ for (const e of entries) {
2970
+ if (e.isDirectory()) n += _countMdFiles(path7.join(dir, e.name));
2971
+ else if (e.isFile() && e.name.endsWith(".md")) n += 1;
2972
+ }
2973
+ return n;
2974
+ }
2975
+ function _countCorpusFiles() {
2976
+ if (_corpusFileCounts !== null) return _corpusFileCounts;
2977
+ const perType = /* @__PURE__ */ new Map();
2978
+ let total = 0;
2979
+ for (const t of CURATED_TYPES2) {
2980
+ const exists = fs7.existsSync(path7.join(MEMORY_ROOT2, `${t}.yml`));
2981
+ const c = exists ? 1 : 0;
2982
+ perType.set(t, c);
2983
+ total += c;
2984
+ }
2985
+ const kn = _countMdFiles(KNOWLEDGE_ROOT);
2986
+ perType.set(KNOWLEDGE_TYPE, kn);
2987
+ total += kn;
2988
+ try {
2989
+ for (const e of fs7.readdirSync(INTAKE_ROOT2, { withFileTypes: true })) {
2990
+ if (e.isFile() && e.name.endsWith(".jsonl")) total += 1;
2991
+ }
2992
+ } catch {
2993
+ }
2994
+ _corpusFileCounts = { perType, total };
2995
+ return _corpusFileCounts;
2996
+ }
2997
+ function _lexicalRankActive(types) {
2998
+ const typeMax = _lexTripwireOverride ?? _LEX_TYPE_FILES_MAX;
2999
+ const corpusMax = _lexTripwireOverride ?? _LEX_CORPUS_FILES_MAX;
3000
+ const counts = _countCorpusFiles();
3001
+ if (counts.total > corpusMax) return true;
3002
+ for (const t of types) {
3003
+ if ((counts.perType.get(t) ?? 0) > typeMax) return true;
3004
+ }
3005
+ return false;
3006
+ }
3007
+ function _entryIndexText2(entry) {
3008
+ const parts = [];
3009
+ for (const f of ["path", "key", "symptom", "feature", "rule", "body"]) {
3010
+ const v = entry[f];
3011
+ if (typeof v === "string") parts.push(v);
3012
+ else if (Array.isArray(v)) for (const x of v) parts.push(_pyStr2(x));
3013
+ }
3014
+ return parts.join(" | ");
3015
+ }
3016
+ function _lexicalRerank(hits, keys) {
3017
+ if (hits.length === 0) return;
3018
+ const index = new LexicalIndex(hits.map((h) => ({ id: h.id, text: _entryIndexText2(h.entry) })));
3019
+ const qTerms = tokenize(keys.join(" "));
3020
+ for (const h of hits) {
3021
+ const bm25 = index.score(qTerms, h.id);
3022
+ const factor = _LEX_SOURCE_FACTOR[h.source] ?? 0.8;
3023
+ h.score = bm25 > 0 ? bm25 / (bm25 + 1) * factor : h.score * 1e-6;
3024
+ }
3025
+ }
3026
+ var _FTS_RECALL_GAP_SOURCE_FACTOR = { curated: 0.5, intake: 0.45, knowledge: 0.4 };
3027
+ function _ftsRecallGapHits(repo_hits, keys) {
3028
+ if (repo_hits.length === 0 || keys.length === 0) return [];
3029
+ const indexPath = resolveIndexPath();
3030
+ if (!ensureIndex(indexPath, MEMORY_ROOT2, INTAKE_ROOT2)) return [];
3031
+ const types = [...new Set(repo_hits.map((h) => h.type))];
3032
+ const ftsHits = queryIndex(indexPath, keys, types);
3033
+ if (ftsHits.length === 0) return [];
3034
+ const byId = new Map(ftsHits.map((h) => [h.id, h]));
3035
+ const out = [];
3036
+ for (const h of repo_hits) {
3037
+ const fts = byId.get(h.id);
3038
+ if (fts === void 0) continue;
3039
+ const factor = _FTS_RECALL_GAP_SOURCE_FACTOR[h.source] ?? 0.4;
3040
+ const score = fts.relevance / (fts.relevance + 1) * factor;
3041
+ if (score > 0) out.push(new Hit(h.id, h.type, h.source, h.path, score, h.entry));
3042
+ }
3043
+ return out;
3044
+ }
3045
+ function _retrieve_internal(types, keys, limit, today) {
3046
+ const repo_hits = [];
3047
+ const skipped = [];
3048
+ for (const mtype of types) {
3049
+ if (mtype === KNOWLEDGE_TYPE) {
3050
+ for (const [p, entry] of _iter_knowledge_entries()) {
3051
+ let base = _score(entry, keys);
3052
+ if (entry["pinned"]) {
3053
+ base = Math.min(1, base + 0.05);
3054
+ }
3055
+ repo_hits.push(
3056
+ new Hit(
3057
+ String(entry["id"] ?? ""),
3058
+ KNOWLEDGE_TYPE,
3059
+ "knowledge",
3060
+ String(p),
3061
+ // Discount vs curated/intake so hand-reviewed repo
3062
+ // entries still win on equal relevance.
3063
+ base * 0.85,
3064
+ entry
3065
+ )
3066
+ );
3067
+ }
3068
+ continue;
3069
+ }
3070
+ if (mtype === CROSS_REPO_TYPE) {
3071
+ for (const h of _cross_repo_hits(keys, limit)) {
3072
+ repo_hits.push(h);
3073
+ }
3074
+ continue;
3075
+ }
3076
+ if (!CURATED_TYPES2.has(mtype)) {
3077
+ continue;
3078
+ }
3079
+ const _cur_ordinals = /* @__PURE__ */ new Map();
3080
+ for (const [p, entry] of _iter_curated_entries2(mtype)) {
3081
+ const eid = _entry_id(mtype, String(p), entry, _cur_ordinals);
3082
+ if (_isStale(entry, today)) {
3083
+ const lv = "last_validated" in entry ? _pyStrDateLike(entry["last_validated"]) : "unknown";
3084
+ const rad = "review_after_days" in entry ? _pyStr2(entry["review_after_days"]) : "?";
3085
+ skipped.push({
3086
+ id: eid,
3087
+ type: mtype,
3088
+ reason: "stale",
3089
+ details: `last_validated=${lv}, review_after_days=${rad}`
3090
+ });
3091
+ continue;
3092
+ }
3093
+ repo_hits.push(new Hit(eid, mtype, "curated", String(p), _score(entry, keys), entry));
3094
+ }
3095
+ for (const [p, entry] of _iter_curated_entries_all(mtype)) {
3096
+ void p;
3097
+ const status2 = "status" in entry && entry["status"] !== void 0 ? entry["status"] : "active";
3098
+ if (typeof status2 === "string" && _CURATED_STATUS_EXCLUDE.has(status2)) {
3099
+ skipped.push({
3100
+ id: String(entry["id"] ?? ""),
3101
+ type: mtype,
3102
+ reason: "superseded",
3103
+ details: `status=${_pyStr2(status2)}`
3104
+ });
3105
+ }
3106
+ }
3107
+ for (const [p, entry] of _iter_intake_entries(mtype)) {
3108
+ repo_hits.push(
3109
+ new Hit(
3110
+ String(entry["id"] ?? ""),
3111
+ mtype,
3112
+ "intake",
3113
+ String(p),
3114
+ _score(entry, keys) * 0.9,
3115
+ // slight discount vs curated
3116
+ entry
3117
+ )
3118
+ );
3119
+ }
3120
+ }
3121
+ _sortMerged(repo_hits);
3122
+ let positives = repo_hits.filter((h) => h.score > 0);
3123
+ if (positives.length > 0 && _lexicalRankActive(types)) {
3124
+ _lexicalRerank(positives, keys);
3125
+ _sortMerged(positives);
3126
+ positives = positives.filter((h) => h.score > 0);
3127
+ }
3128
+ if (positives.length === 0 && repo_hits.length > 0) {
3129
+ const ftsHits = _ftsRecallGapHits(repo_hits, keys);
3130
+ if (ftsHits.length > 0) {
3131
+ _sortMerged(ftsHits);
3132
+ positives = ftsHits.filter((h) => h.score > 0);
3133
+ }
3134
+ }
3135
+ const hits = (positives.length > 0 ? positives : repo_hits).slice(0, limit);
3136
+ for (const h of hits) {
3137
+ if (_isPlainObject4(h.entry)) h.entry = sanitize_entry(h.entry);
3138
+ }
3139
+ return [hits, skipped];
3140
+ }
3141
+ function retrieve(types, keys, limit = 5) {
3142
+ const [hits] = _retrieve_internal(types, keys, limit, _today());
3143
+ return hits;
3144
+ }
3145
+ function _sortMerged(merged) {
3146
+ const indexed = merged.map((h, i) => ({ h, i }));
3147
+ indexed.sort((a, b) => {
3148
+ if (a.h.score !== b.h.score) {
3149
+ return b.h.score - a.h.score;
3150
+ }
3151
+ const ac = a.h.source === "curated" ? 1 : 0;
3152
+ const bc = b.h.source === "curated" ? 1 : 0;
3153
+ if (ac !== bc) {
3154
+ return bc - ac;
3155
+ }
3156
+ return a.i - b.i;
3157
+ });
3158
+ for (let i = 0; i < indexed.length; i += 1) {
3159
+ merged[i] = indexed[i].h;
3160
+ }
3161
+ }
3162
+ var _CONTRACT_VERSION = 1;
3163
+ var _KNOWN_TYPES = /* @__PURE__ */ new Set([...CURATED_TYPES2, KNOWLEDGE_TYPE, CROSS_REPO_TYPE]);
3164
+ function retrieve_v1(types, keys, limit = 20, options = {}) {
3165
+ const detail = options.detail ?? "full";
3166
+ const rawBudget = options.token_budget;
3167
+ const budgeted = typeof rawBudget === "number" && Number.isFinite(rawBudget) && rawBudget > 0;
3168
+ const budgetChars = budgeted ? Math.floor(rawBudget * 4) : 0;
3169
+ const known = types.filter((t) => _KNOWN_TYPES.has(t));
3170
+ const unknown = types.filter((t) => !_KNOWN_TYPES.has(t));
3171
+ const hits = retrieve(known, keys, limit);
3172
+ const slice_counts = {};
3173
+ for (const t of known) {
3174
+ slice_counts[t] = 0;
3175
+ }
3176
+ const entries = [];
3177
+ let usedChars = 0;
3178
+ let truncation = null;
3179
+ for (let hi = 0; hi < hits.length; hi++) {
3180
+ const h = hits[hi];
3181
+ const safeEntry = _isPlainObject4(h.entry) ? sanitize_entry(h.entry) : {};
3182
+ const learning = _learningVerdictFor(h.type, safeEntry);
3183
+ if (learning !== void 0) safeEntry["learning"] = learning;
3184
+ if (budgeted) {
3185
+ const line = _compact_line(h, safeEntry);
3186
+ const cost = line.length + 1;
3187
+ if (usedChars + cost > budgetChars && entries.length > 0) {
3188
+ const omitted = hits.length - hi;
3189
+ const nextPath = h.path || "<path>";
3190
+ truncation = {
3191
+ omitted,
3192
+ hint: `${omitted} more hit(s) \u2014 narrow with --key or read ${nextPath}`
3193
+ };
3194
+ break;
3195
+ }
3196
+ usedChars += cost;
3197
+ entries.push({
3198
+ id: h.id,
3199
+ type: h.type,
3200
+ source: "repo",
3201
+ confidence: new PyFloat3(_round4(h.score)),
3202
+ line,
3203
+ ...learning !== void 0 ? { learning } : {}
3204
+ });
3205
+ if (h.type in slice_counts) {
3206
+ slice_counts[h.type] = slice_counts[h.type] + 1;
3207
+ }
3208
+ continue;
3209
+ }
3210
+ const envelope_entry = {
3211
+ id: h.id,
3212
+ type: h.type,
3213
+ source: "repo",
3214
+ // confidence = round(float(score), 4) — always a Python float.
3215
+ confidence: new PyFloat3(_round4(h.score)),
3216
+ body: safeEntry
3217
+ };
3218
+ if (h.type in slice_counts) {
3219
+ slice_counts[h.type] = slice_counts[h.type] + 1;
3220
+ }
3221
+ if (detail === "index") {
3222
+ entries.push({
3223
+ id: h.id,
3224
+ type: h.type,
3225
+ source: "repo",
3226
+ confidence: new PyFloat3(_round4(h.score)),
3227
+ title: _entry_title(safeEntry, h.id),
3228
+ tokens_estimate: _entry_tokens_estimate(envelope_entry),
3229
+ // Knowledge chunks carry their pinned flag in the index row
3230
+ // (roadmap Phase 4) — pinned entries rank higher, so the
3231
+ // caller should see why before fetching.
3232
+ ...h.type === KNOWLEDGE_TYPE ? { pinned: Boolean(safeEntry["pinned"] ?? false) } : {},
3233
+ ...learning !== void 0 ? { learning } : {}
3234
+ });
3235
+ } else {
3236
+ entries.push(envelope_entry);
3237
+ }
3238
+ }
3239
+ const slices = {};
3240
+ for (const t of known) {
3241
+ slices[t] = { status: "ok", count: slice_counts[t] ?? 0 };
3242
+ }
3243
+ const errors = [];
3244
+ for (const t of unknown) {
3245
+ slices[t] = { status: "unknown_type", count: 0 };
3246
+ errors.push({
3247
+ type: t,
3248
+ code: "unknown_type",
3249
+ message: `file-backed backend does not know type ${_pyRepr(t)}`
3250
+ });
3251
+ }
3252
+ const sliceVals = Object.values(slices);
3253
+ const oks = sliceVals.filter((s) => s["status"] === "ok");
3254
+ const fails = sliceVals.filter((s) => s["status"] !== "ok");
3255
+ const envelope_status = fails.length === 0 ? "ok" : oks.length === 0 ? "error" : "partial";
3256
+ const envelope = {
3257
+ contract_version: _CONTRACT_VERSION,
3258
+ status: envelope_status,
3259
+ entries,
3260
+ slices
3261
+ };
3262
+ if (errors.length > 0) {
3263
+ envelope["errors"] = errors;
3264
+ }
3265
+ if (truncation !== null) {
3266
+ envelope["truncation"] = truncation;
3267
+ }
3268
+ return envelope;
3269
+ }
3270
+ function memory_get_v1(ids) {
3271
+ const wanted = new Set(ids);
3272
+ const found = /* @__PURE__ */ new Map();
3273
+ const record = (id, mtype, entry) => {
3274
+ if (wanted.has(id) && !found.has(id)) {
3275
+ const safeEntry = _isPlainObject4(entry) ? sanitize_entry(entry) : {};
3276
+ const learning = _learningVerdictFor(mtype, safeEntry);
3277
+ if (learning !== void 0) safeEntry["learning"] = learning;
3278
+ found.set(id, {
3279
+ id,
3280
+ type: mtype,
3281
+ source: "repo",
3282
+ body: safeEntry
3283
+ });
3284
+ }
3285
+ };
3286
+ for (const mtype of CURATED_TYPES2) {
3287
+ const ordinals = /* @__PURE__ */ new Map();
3288
+ for (const [p, entry] of _iter_curated_entries2(mtype)) {
3289
+ record(_entry_id(mtype, String(p), entry, ordinals), mtype, entry);
3290
+ }
3291
+ for (const [p, entry] of _iter_intake_entries(mtype)) {
3292
+ void p;
3293
+ const own = entry["id"];
3294
+ if (typeof own === "string" && own.trim() !== "") {
3295
+ record(own, mtype, entry);
3296
+ }
3297
+ }
3298
+ }
3299
+ for (const [p, entry] of _iter_knowledge_entries()) {
3300
+ void p;
3301
+ record(String(entry["id"] ?? ""), KNOWLEDGE_TYPE, entry);
3302
+ }
3303
+ const entries = [];
3304
+ const idStatus = {};
3305
+ for (const id of ids) {
3306
+ const e = found.get(id);
3307
+ if (e !== void 0) {
3308
+ entries.push(e);
3309
+ idStatus[id] = "ok";
3310
+ } else {
3311
+ idStatus[id] = "unknown";
3312
+ }
3313
+ }
3314
+ const okCount = entries.length;
3315
+ const status2 = okCount === ids.length ? "ok" : okCount === 0 ? "error" : "partial";
3316
+ return {
3317
+ contract_version: _CONTRACT_VERSION,
3318
+ status: status2,
3319
+ entries,
3320
+ ids: idStatus
3321
+ };
3322
+ }
3323
+ function _parseArgs3(argv) {
3324
+ const args = {
3325
+ types: "",
3326
+ key: [],
3327
+ limit: 5,
3328
+ format: "text",
3329
+ envelope: "legacy",
3330
+ detail: "full",
3331
+ tokenBudget: null
3332
+ };
3333
+ for (let i = 0; i < argv.length; i += 1) {
3334
+ const a = argv[i];
3335
+ if (a === "--types") {
3336
+ args.types = argv[++i];
3337
+ } else if (a.startsWith("--types=")) {
3338
+ args.types = a.slice("--types=".length);
3339
+ } else if (a === "--key") {
3340
+ args.key.push(argv[++i]);
3341
+ } else if (a.startsWith("--key=")) {
3342
+ args.key.push(a.slice("--key=".length));
3343
+ } else if (a === "--limit") {
3344
+ args.limit = _parseInt(argv[++i], "--limit");
3345
+ } else if (a.startsWith("--limit=")) {
3346
+ args.limit = _parseInt(a.slice("--limit=".length), "--limit");
3347
+ } else if (a === "--format") {
3348
+ args.format = _checkChoice2(argv[++i], ["text", "json"], "--format");
3349
+ } else if (a.startsWith("--format=")) {
3350
+ args.format = _checkChoice2(a.slice("--format=".length), ["text", "json"], "--format");
3351
+ } else if (a === "--envelope") {
3352
+ args.envelope = _checkChoice2(argv[++i], ["legacy", "v1"], "--envelope");
3353
+ } else if (a.startsWith("--envelope=")) {
3354
+ args.envelope = _checkChoice2(a.slice("--envelope=".length), ["legacy", "v1"], "--envelope");
3355
+ } else if (a === "--detail") {
3356
+ args.detail = _checkChoice2(argv[++i], ["index", "full"], "--detail");
3357
+ } else if (a.startsWith("--detail=")) {
3358
+ args.detail = _checkChoice2(a.slice("--detail=".length), ["index", "full"], "--detail");
3359
+ } else if (a === "--token-budget") {
3360
+ args.tokenBudget = _parseInt(argv[++i], "--token-budget");
3361
+ } else if (a.startsWith("--token-budget=")) {
3362
+ args.tokenBudget = _parseInt(a.slice("--token-budget=".length), "--token-budget");
3363
+ } else if (a === "-h" || a === "--help") {
3364
+ _printUsage();
3365
+ process5.exit(0);
3366
+ } else {
3367
+ process5.stderr.write(`memory_lookup: error: unrecognized arguments: ${a}
3368
+ `);
3369
+ process5.exit(2);
3370
+ }
3371
+ }
3372
+ return args;
3373
+ }
3374
+ function _parseInt(value, flag) {
3375
+ const n = Number(value);
3376
+ if (value === void 0 || !Number.isInteger(n)) {
3377
+ process5.stderr.write(
3378
+ `memory_lookup: error: argument ${flag}: invalid int value: '${value ?? ""}'
3379
+ `
3380
+ );
3381
+ process5.exit(2);
3382
+ }
3383
+ return n;
3384
+ }
3385
+ function _checkChoice2(value, choices, flag) {
3386
+ if (value === void 0 || !choices.includes(value)) {
3387
+ process5.stderr.write(
3388
+ `memory_lookup: error: argument ${flag}: invalid choice: '${value ?? ""}' (choose from ${choices.map((c) => `'${c}'`).join(", ")})
3389
+ `
3390
+ );
3391
+ process5.exit(2);
3392
+ }
3393
+ return value;
3394
+ }
3395
+ function _printUsage() {
3396
+ process5.stdout.write(
3397
+ "usage: memory_lookup [-h] [--types TYPES] [--key KEY] [--limit LIMIT] [--format {text,json}] [--envelope {legacy,v1}] [--detail {index,full}] [--token-budget N]\n"
3398
+ );
3399
+ }
3400
+ function main5() {
3401
+ const args = _parseArgs3(process5.argv.slice(2));
3402
+ const types = args.types.split(",").map((t) => t.trim()).filter((t) => t);
3403
+ if (types.length === 0) {
3404
+ process5.stderr.write("error: --types is required\n");
3405
+ return 2;
3406
+ }
3407
+ if (args.envelope === "v1") {
3408
+ const opts = { detail: args.detail };
3409
+ if (args.tokenBudget !== null) {
3410
+ opts.token_budget = args.tokenBudget;
3411
+ }
3412
+ const envelope = retrieve_v1(types, args.key, args.limit, opts);
3413
+ process5.stdout.write(`${pyJsonDumps2(envelope, 2)}
3414
+ `);
3415
+ return 0;
3416
+ }
3417
+ const hits = retrieve(types, args.key, args.limit);
3418
+ if (args.format === "json") {
3419
+ const payload = {
3420
+ hits: hits.map((h) => h.as_dict())
3421
+ };
3422
+ process5.stdout.write(`${pyJsonDumps2(payload, 2)}
3423
+ `);
3424
+ } else {
3425
+ if (hits.length === 0) {
3426
+ process5.stdout.write(" (no hits)\n");
3427
+ }
3428
+ for (const h of hits) {
3429
+ process5.stdout.write(
3430
+ ` [${h.source}] ${h.type} score=${_fixed2(h.score)} id=${h.id || "-"} path=${h.path}
3431
+ `
3432
+ );
3433
+ }
3434
+ }
3435
+ return 0;
3436
+ }
3437
+ function _fixed2(n) {
3438
+ return n.toFixed(2);
3439
+ }
3440
+ function _round4(n) {
3441
+ return _pyRound(n, 4);
3442
+ }
3443
+ function _pyRound(value, ndigits) {
3444
+ const factor = 10 ** ndigits;
3445
+ const scaled = value * factor;
3446
+ const floor = Math.floor(scaled);
3447
+ const diff = scaled - floor;
3448
+ let rounded;
3449
+ if (diff > 0.5) {
3450
+ rounded = floor + 1;
3451
+ } else if (diff < 0.5) {
3452
+ rounded = floor;
3453
+ } else {
3454
+ rounded = floor % 2 === 0 ? floor : floor + 1;
3455
+ }
3456
+ return rounded / factor;
3457
+ }
3458
+ function _fnmatch(name, pattern) {
3459
+ const re = _fnmatchTranslate(pattern);
3460
+ return re.test(name);
3461
+ }
3462
+ function _fnmatchTranslate(pat) {
3463
+ let res = "";
3464
+ let i = 0;
3465
+ const n = pat.length;
3466
+ while (i < n) {
3467
+ const c = pat[i];
3468
+ i += 1;
3469
+ if (c === "*") {
3470
+ res += ".*";
3471
+ } else if (c === "?") {
3472
+ res += ".";
3473
+ } else if (c === "[") {
3474
+ let j = i;
3475
+ if (j < n && pat[j] === "!") {
3476
+ j += 1;
3477
+ }
3478
+ if (j < n && pat[j] === "]") {
3479
+ j += 1;
3480
+ }
3481
+ while (j < n && pat[j] !== "]") {
3482
+ j += 1;
3483
+ }
3484
+ if (j >= n) {
3485
+ res += "\\[";
3486
+ } else {
3487
+ let stuff = pat.slice(i, j);
3488
+ if (!stuff.includes("-")) {
3489
+ stuff = stuff.replace(/\\/g, "\\\\");
3490
+ } else {
3491
+ stuff = stuff.replace(/\\/g, "\\\\");
3492
+ }
3493
+ i = j + 1;
3494
+ if (stuff.startsWith("!")) {
3495
+ stuff = `^${stuff.slice(1)}`;
3496
+ } else if (stuff.startsWith("^") || stuff.startsWith("[")) {
3497
+ stuff = `\\${stuff}`;
3498
+ }
3499
+ res += `[${stuff}]`;
3500
+ }
3501
+ } else {
3502
+ res += _reEscape(c);
3503
+ }
3504
+ }
3505
+ return new RegExp(`^(?:${res})$`, "s");
3506
+ }
3507
+ function _reEscape(ch) {
3508
+ return ch.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3509
+ }
3510
+ function _pyStr2(value) {
3511
+ if (value === null || value === void 0) {
3512
+ return "None";
3513
+ }
3514
+ if (value === true) {
3515
+ return "True";
3516
+ }
3517
+ if (value === false) {
3518
+ return "False";
3519
+ }
3520
+ return String(value);
3521
+ }
3522
+ function _pyRepr(s) {
3523
+ const hasSingle = s.includes("'");
3524
+ const hasDouble = s.includes('"');
3525
+ const quote = hasSingle && !hasDouble ? '"' : "'";
3526
+ let out = quote;
3527
+ for (const ch of s) {
3528
+ if (ch === quote || ch === "\\") {
3529
+ out += `\\${ch}`;
3530
+ } else if (ch === "\n") {
3531
+ out += "\\n";
3532
+ } else if (ch === "\r") {
3533
+ out += "\\r";
3534
+ } else if (ch === " ") {
3535
+ out += "\\t";
3536
+ } else {
3537
+ out += ch;
3538
+ }
3539
+ }
3540
+ return out + quote;
3541
+ }
3542
+ function _rglobYmlSorted(root) {
3543
+ const out = [];
3544
+ const walk = (dir) => {
3545
+ let entries;
3546
+ try {
3547
+ entries = fs7.readdirSync(dir, { withFileTypes: true });
3548
+ } catch {
3549
+ return;
3550
+ }
3551
+ for (const ent of entries) {
3552
+ const full = path7.join(dir, ent.name);
3553
+ if (ent.name.endsWith(".yml")) {
3554
+ out.push(full);
3555
+ }
3556
+ if (ent.isDirectory() || ent.isSymbolicLink() && _isDir2(full)) {
3557
+ walk(full);
3558
+ }
3559
+ }
3560
+ };
3561
+ walk(root);
3562
+ out.sort();
3563
+ return out;
3564
+ }
3565
+ function _globJsonlSorted(dir) {
3566
+ return _globSorted(dir, ".jsonl");
3567
+ }
3568
+ function _globMdSorted(dir) {
3569
+ return _globSorted(dir, ".md");
3570
+ }
3571
+ function _globSorted(dir, suffix) {
3572
+ let entries;
3573
+ try {
3574
+ entries = fs7.readdirSync(dir, { withFileTypes: true });
3575
+ } catch {
3576
+ return [];
3577
+ }
3578
+ const out = entries.filter((e) => e.name.endsWith(suffix) && (e.isFile() || e.isSymbolicLink() && _isFile2(path7.join(dir, e.name)))).map((e) => path7.join(dir, e.name));
3579
+ out.sort();
3580
+ return out;
3581
+ }
3582
+ function _iterdirSorted(dir) {
3583
+ let names;
3584
+ try {
3585
+ names = fs7.readdirSync(dir);
3586
+ } catch {
3587
+ return [];
3588
+ }
3589
+ const out = names.map((name) => path7.join(dir, name));
3590
+ out.sort();
3591
+ return out;
3592
+ }
3593
+ function _stem(p) {
3594
+ const base = path7.basename(p);
3595
+ const dot = base.lastIndexOf(".");
3596
+ return dot <= 0 ? base : base.slice(0, dot);
3597
+ }
3598
+ function pyJsonDumps2(value, indent) {
3599
+ return _escapeNonAscii3(_dumpsIndent2(value, indent, 0));
3600
+ }
3601
+ function _dumpsIndent2(value, indent, depth) {
3602
+ const pad = " ".repeat(indent * (depth + 1));
3603
+ const closePad = " ".repeat(indent * depth);
3604
+ if (value === null || value === void 0) {
3605
+ return "null";
3606
+ }
3607
+ if (typeof value === "boolean") {
3608
+ return value ? "true" : "false";
3609
+ }
3610
+ if (typeof value === "number") {
3611
+ return _jsonNum2(value);
3612
+ }
3613
+ if (typeof value === "string") {
3614
+ return _jsonStrAscii2(value);
3615
+ }
3616
+ if (value instanceof PyTimestamp2) {
3617
+ return _jsonStrAscii2(value.pyStr);
3618
+ }
3619
+ if (value instanceof PyFloat3) {
3620
+ return _jsonFloat2(value.value);
3621
+ }
3622
+ if (Array.isArray(value)) {
3623
+ if (value.length === 0) {
3624
+ return "[]";
3625
+ }
3626
+ const items = value.map((v) => pad + _dumpsIndent2(v, indent, depth + 1));
3627
+ return `[
3628
+ ${items.join(",\n")}
3629
+ ${closePad}]`;
3630
+ }
3631
+ if (_isPlainObject4(value)) {
3632
+ const keys = Object.keys(value);
3633
+ if (keys.length === 0) {
3634
+ return "{}";
3635
+ }
3636
+ const items = keys.map((k) => `${pad}${_jsonStrAscii2(k)}: ${_dumpsIndent2(value[k], indent, depth + 1)}`);
3637
+ return `{
3638
+ ${items.join(",\n")}
3639
+ ${closePad}}`;
3640
+ }
3641
+ return _jsonStrAscii2(String(value));
3642
+ }
3643
+ function _jsonNum2(n) {
3644
+ if (!Number.isFinite(n)) {
3645
+ if (Number.isNaN(n)) {
3646
+ return "NaN";
3647
+ }
3648
+ return n > 0 ? "Infinity" : "-Infinity";
3649
+ }
3650
+ return String(n);
3651
+ }
3652
+ function _jsonFloat2(n) {
3653
+ if (!Number.isFinite(n)) {
3654
+ if (Number.isNaN(n)) {
3655
+ return "NaN";
3656
+ }
3657
+ return n > 0 ? "Infinity" : "-Infinity";
3658
+ }
3659
+ return Number.isInteger(n) ? `${n}.0` : String(n);
3660
+ }
3661
+ function _jsonStrAscii2(s) {
3662
+ let out = '"';
3663
+ for (const ch of s) {
3664
+ const code = ch.codePointAt(0) ?? 0;
3665
+ switch (ch) {
3666
+ case '"':
3667
+ out += '\\"';
3668
+ break;
3669
+ case "\\":
3670
+ out += "\\\\";
3671
+ break;
3672
+ case "\n":
3673
+ out += "\\n";
3674
+ break;
3675
+ case "\r":
3676
+ out += "\\r";
3677
+ break;
3678
+ case " ":
3679
+ out += "\\t";
3680
+ break;
3681
+ case "\b":
3682
+ out += "\\b";
3683
+ break;
3684
+ case "\f":
3685
+ out += "\\f";
3686
+ break;
3687
+ default:
3688
+ if (code < 32) {
3689
+ out += `\\u${code.toString(16).padStart(4, "0")}`;
3690
+ } else {
3691
+ out += ch;
3692
+ }
3693
+ }
3694
+ }
3695
+ return `${out}"`;
3696
+ }
3697
+ function _escapeNonAscii3(s) {
3698
+ let out = "";
3699
+ for (const ch of s) {
3700
+ const code = ch.codePointAt(0) ?? 0;
3701
+ if (code > 127) {
3702
+ for (let i = 0; i < ch.length; i += 1) {
3703
+ out += `\\u${ch.charCodeAt(i).toString(16).padStart(4, "0")}`;
3704
+ }
3705
+ } else {
3706
+ out += ch;
3707
+ }
3708
+ }
3709
+ return out;
3710
+ }
3711
+ function _isCliEntry4() {
3712
+ if (process5.argv[1] === void 0) {
3713
+ return false;
3714
+ }
3715
+ const argvUrl = pathToFileURL4(path7.resolve(process5.argv[1])).href;
3716
+ if (import.meta.url === argvUrl) {
3717
+ return true;
3718
+ }
3719
+ try {
3720
+ const here = fs7.realpathSync(fileURLToPath6(import.meta.url));
3721
+ const argv = fs7.realpathSync(path7.resolve(process5.argv[1]));
3722
+ return here === argv;
3723
+ } catch {
3724
+ return false;
3725
+ }
3726
+ }
3727
+ var _isMain3 = _isCliEntry4();
3728
+ if (_isMain3) {
3729
+ process5.exit(main5());
3730
+ }
3731
+
3732
+ // src/scripts/_cli/cmd_memory_get.ts
3733
+ var PROG2 = "agent-config memory:get";
3734
+ var USAGE = `usage: ${PROG2} <id> [<id> ...] [--format text|json]
3735
+ `;
3736
+ var ArgparseExit = class extends Error {
3737
+ constructor(code) {
3738
+ super(`argparse-exit-${code}`);
3739
+ this.code = code;
3740
+ }
3741
+ code;
3742
+ };
3743
+ function _argError3(msg) {
3744
+ process6.stderr.write(USAGE);
3745
+ process6.stderr.write(`${PROG2}: error: ${msg}
3746
+ `);
3747
+ throw new ArgparseExit(2);
3748
+ }
3749
+ function _parse(argv) {
3750
+ const opts = { ids: [], format: "text" };
3751
+ let i = 0;
3752
+ while (i < argv.length) {
3753
+ const a = argv[i];
3754
+ if (a === "-h" || a === "--help") {
3755
+ process6.stdout.write(
3756
+ `${USAGE}
3757
+ Batch-fetch FULL memory entries by id \u2014 the second half of the
3758
+ index-first retrieval workflow (\`memory:lookup --detail index\`
3759
+ first, then \`memory:get\` the ids that justify the fetch).
3760
+
3761
+ options:
3762
+ --format text|json output format (default: text)
3763
+ -h, --help show this help and exit
3764
+
3765
+ exit codes:
3766
+ 0 every requested id was found
3767
+ 1 at least one requested id was not found
3768
+ 2 usage error
3769
+ `
3770
+ );
3771
+ throw new ArgparseExit(0);
3772
+ } else if (a === "--format") {
3773
+ const val = argv[i + 1];
3774
+ if (val === void 0) _argError3("argument --format: expected one argument");
3775
+ if (val !== "text" && val !== "json") {
3776
+ _argError3(`argument --format: invalid choice: '${val}' (choose from 'text', 'json')`);
3777
+ }
3778
+ opts.format = val;
3779
+ i += 2;
3780
+ } else if (a.startsWith("--format=")) {
3781
+ const val = a.slice("--format=".length);
3782
+ if (val !== "text" && val !== "json") {
3783
+ _argError3(`argument --format: invalid choice: '${val}' (choose from 'text', 'json')`);
3784
+ }
3785
+ opts.format = val;
3786
+ i += 1;
3787
+ } else if (a.startsWith("--")) {
3788
+ _argError3(`unrecognized arguments: ${a}`);
3789
+ } else {
3790
+ opts.ids.push(a);
3791
+ i += 1;
3792
+ }
3793
+ }
3794
+ if (opts.ids.length === 0) {
3795
+ _argError3("at least one <id> is required");
3796
+ }
3797
+ return opts;
3798
+ }
3799
+ function isPlainObject(value) {
3800
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3801
+ }
3802
+ function _renderEntryText(entry) {
3803
+ const lines = [];
3804
+ const id = typeof entry["id"] === "string" ? entry["id"] : "(unknown id)";
3805
+ const type = typeof entry["type"] === "string" ? entry["type"] : "(unknown type)";
3806
+ lines.push(`[${id}] ${type}`);
3807
+ const body = entry["body"];
3808
+ if (isPlainObject(body)) {
3809
+ for (const key of Object.keys(body).sort()) {
3810
+ lines.push(` ${key}: ${_renderValue(body[key])}`);
3811
+ }
3812
+ }
3813
+ return lines.join("\n");
3814
+ }
3815
+ function _renderValue(value) {
3816
+ if (typeof value === "string") return value;
3817
+ if (isPlainObject(value) && typeof value["pyStr"] === "string" && Object.keys(value).length === 1) {
3818
+ return value["pyStr"];
3819
+ }
3820
+ return JSON.stringify(value);
3821
+ }
3822
+ function main6(argv = null) {
3823
+ const opts = _parse(argv ?? process6.argv.slice(2));
3824
+ const envelope = memory_get_v1(opts.ids);
3825
+ const entries = Array.isArray(envelope["entries"]) ? envelope["entries"] : [];
3826
+ const idStatus = isPlainObject(envelope["ids"]) ? envelope["ids"] : {};
3827
+ const unknown = opts.ids.filter((id) => idStatus[id] === "unknown");
3828
+ if (opts.format === "json") {
3829
+ process6.stdout.write(`${JSON.stringify(envelope, null, 2)}
3830
+ `);
3831
+ } else {
3832
+ if (entries.length === 0) {
3833
+ process6.stdout.write(" (no entries found)\n");
3834
+ } else {
3835
+ for (const entry of entries) {
3836
+ process6.stdout.write(`${_renderEntryText(entry)}
3837
+ `);
3838
+ }
3839
+ }
3840
+ for (const id of unknown) {
3841
+ process6.stderr.write(`${PROG2}: unknown id: ${id}
3842
+ `);
3843
+ }
3844
+ }
3845
+ return unknown.length > 0 ? 1 : 0;
3846
+ }
3847
+ var _HERE2 = fileURLToPath7(import.meta.url);
3848
+ function _isCliEntry5() {
3849
+ if (process6.argv[1] === void 0) {
3850
+ return false;
3851
+ }
3852
+ const argvUrl = pathToFileURL5(path8.resolve(process6.argv[1])).href;
3853
+ if (import.meta.url === argvUrl) {
3854
+ return true;
3855
+ }
3856
+ try {
3857
+ const here = fs8.realpathSync(fileURLToPath7(import.meta.url));
3858
+ const argv = fs8.realpathSync(path8.resolve(process6.argv[1]));
3859
+ return here === argv;
3860
+ } catch {
3861
+ return false;
3862
+ }
3863
+ }
3864
+ if (_isCliEntry5() || process6.argv[1] === _HERE2) {
3865
+ try {
3866
+ process6.exitCode = main6(process6.argv.slice(2));
3867
+ } catch (exc) {
3868
+ if (exc instanceof ArgparseExit) {
3869
+ process6.exitCode = exc.code;
3870
+ } else {
3871
+ throw exc;
3872
+ }
3873
+ }
3874
+ }
3875
+ export {
3876
+ ArgparseExit,
3877
+ main6 as main
3878
+ };