@elaraai/east-plugin 0.0.0-stage → 1.0.85

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 (133) hide show
  1. package/LICENSE.md +682 -0
  2. package/README.md +104 -2
  3. package/dist/daemon/east-py-lsp.d.ts +2 -0
  4. package/dist/daemon/east-py-lsp.d.ts.map +1 -0
  5. package/dist/daemon/east-py-lsp.js +9 -0
  6. package/dist/daemon/east-py-lsp.js.map +1 -0
  7. package/dist/daemon/lsp.d.ts +2 -0
  8. package/dist/daemon/lsp.d.ts.map +1 -0
  9. package/dist/daemon/lsp.js +7 -0
  10. package/dist/daemon/lsp.js.map +1 -0
  11. package/dist/daemon/server.d.ts +2 -0
  12. package/dist/daemon/server.d.ts.map +1 -0
  13. package/dist/daemon/server.js +71 -0
  14. package/dist/daemon/server.js.map +1 -0
  15. package/dist/hooks/diagnose-bash.d.ts +2 -0
  16. package/dist/hooks/diagnose-bash.d.ts.map +1 -0
  17. package/dist/hooks/diagnose-bash.js +28 -0
  18. package/dist/hooks/diagnose-bash.js.map +1 -0
  19. package/dist/hooks/diagnose.d.ts +2 -0
  20. package/dist/hooks/diagnose.d.ts.map +1 -0
  21. package/dist/hooks/diagnose.js +33 -0
  22. package/dist/hooks/diagnose.js.map +1 -0
  23. package/dist/hooks/pre-read.d.ts +2 -0
  24. package/dist/hooks/pre-read.d.ts.map +1 -0
  25. package/dist/hooks/pre-read.js +11 -0
  26. package/dist/hooks/pre-read.js.map +1 -0
  27. package/dist/hooks/pre-write.d.ts +2 -0
  28. package/dist/hooks/pre-write.d.ts.map +1 -0
  29. package/dist/hooks/pre-write.js +11 -0
  30. package/dist/hooks/pre-write.js.map +1 -0
  31. package/dist/hooks/session-start.d.ts +2 -0
  32. package/dist/hooks/session-start.d.ts.map +1 -0
  33. package/dist/hooks/session-start.js +34 -0
  34. package/dist/hooks/session-start.js.map +1 -0
  35. package/dist/hooks/subagent-start.d.ts +2 -0
  36. package/dist/hooks/subagent-start.d.ts.map +1 -0
  37. package/dist/hooks/subagent-start.js +29 -0
  38. package/dist/hooks/subagent-start.js.map +1 -0
  39. package/dist/lib/bash-writes.d.ts +11 -0
  40. package/dist/lib/bash-writes.d.ts.map +1 -0
  41. package/dist/lib/bash-writes.js +74 -0
  42. package/dist/lib/bash-writes.js.map +1 -0
  43. package/dist/lib/codex-hook.d.ts +62 -0
  44. package/dist/lib/codex-hook.d.ts.map +1 -0
  45. package/dist/lib/codex-hook.js +93 -0
  46. package/dist/lib/codex-hook.js.map +1 -0
  47. package/dist/lib/codex-skills.d.ts +38 -0
  48. package/dist/lib/codex-skills.d.ts.map +1 -0
  49. package/dist/lib/codex-skills.js +80 -0
  50. package/dist/lib/codex-skills.js.map +1 -0
  51. package/dist/lib/codex-tools.d.ts +11 -0
  52. package/dist/lib/codex-tools.d.ts.map +1 -0
  53. package/dist/lib/codex-tools.js +41 -0
  54. package/dist/lib/codex-tools.js.map +1 -0
  55. package/dist/lib/diagnostics-client.d.ts +9 -0
  56. package/dist/lib/diagnostics-client.d.ts.map +1 -0
  57. package/dist/lib/diagnostics-client.js +123 -0
  58. package/dist/lib/diagnostics-client.js.map +1 -0
  59. package/dist/lib/east-index.d.ts +3 -0
  60. package/dist/lib/east-index.d.ts.map +1 -0
  61. package/dist/lib/east-index.js +9 -0
  62. package/dist/lib/east-index.js.map +1 -0
  63. package/dist/lib/east-project.d.ts +49 -0
  64. package/dist/lib/east-project.d.ts.map +1 -0
  65. package/dist/lib/east-project.js +121 -0
  66. package/dist/lib/east-project.js.map +1 -0
  67. package/dist/lib/east-py-lint.d.ts +7 -0
  68. package/dist/lib/east-py-lint.d.ts.map +1 -0
  69. package/dist/lib/east-py-lint.js +22 -0
  70. package/dist/lib/east-py-lint.js.map +1 -0
  71. package/dist/lib/east-rules-context.d.ts +7 -0
  72. package/dist/lib/east-rules-context.d.ts.map +1 -0
  73. package/dist/lib/east-rules-context.js +91 -0
  74. package/dist/lib/east-rules-context.js.map +1 -0
  75. package/dist/lib/hook-io.d.ts +30 -0
  76. package/dist/lib/hook-io.d.ts.map +1 -0
  77. package/dist/lib/hook-io.js +29 -0
  78. package/dist/lib/hook-io.js.map +1 -0
  79. package/dist/lib/host.d.ts +3 -0
  80. package/dist/lib/host.d.ts.map +1 -0
  81. package/dist/lib/host.js +9 -0
  82. package/dist/lib/host.js.map +1 -0
  83. package/dist/lib/lsp-client.d.ts +34 -0
  84. package/dist/lib/lsp-client.d.ts.map +1 -0
  85. package/dist/lib/lsp-client.js +200 -0
  86. package/dist/lib/lsp-client.js.map +1 -0
  87. package/dist/lib/plugin-status.d.ts +9 -0
  88. package/dist/lib/plugin-status.d.ts.map +1 -0
  89. package/dist/lib/plugin-status.js +164 -0
  90. package/dist/lib/plugin-status.js.map +1 -0
  91. package/dist/lib/review.d.ts +16 -0
  92. package/dist/lib/review.d.ts.map +1 -0
  93. package/dist/lib/review.js +74 -0
  94. package/dist/lib/review.js.map +1 -0
  95. package/dist/lib/search-guidance.d.ts +44 -0
  96. package/dist/lib/search-guidance.d.ts.map +1 -0
  97. package/dist/lib/search-guidance.js +73 -0
  98. package/dist/lib/search-guidance.js.map +1 -0
  99. package/dist/lib/search-hooks.d.ts +75 -0
  100. package/dist/lib/search-hooks.d.ts.map +1 -0
  101. package/dist/lib/search-hooks.js +117 -0
  102. package/dist/lib/search-hooks.js.map +1 -0
  103. package/dist/lib/search.d.ts +108 -0
  104. package/dist/lib/search.d.ts.map +1 -0
  105. package/dist/lib/search.js +167 -0
  106. package/dist/lib/search.js.map +1 -0
  107. package/dist/lib/transcript.d.ts +29 -0
  108. package/dist/lib/transcript.d.ts.map +1 -0
  109. package/dist/lib/transcript.js +109 -0
  110. package/dist/lib/transcript.js.map +1 -0
  111. package/dist/mcp/example-tools.d.ts +26 -0
  112. package/dist/mcp/example-tools.d.ts.map +1 -0
  113. package/dist/mcp/example-tools.js +94 -0
  114. package/dist/mcp/example-tools.js.map +1 -0
  115. package/dist/mcp/server.d.ts +2 -0
  116. package/dist/mcp/server.d.ts.map +1 -0
  117. package/dist/mcp/server.js +51 -0
  118. package/dist/mcp/server.js.map +1 -0
  119. package/dist/scripts/examples-file.d.ts +38 -0
  120. package/dist/scripts/examples-file.d.ts.map +1 -0
  121. package/dist/scripts/examples-file.js +224 -0
  122. package/dist/scripts/examples-file.js.map +1 -0
  123. package/dist/scripts/generate-index.d.ts +127 -0
  124. package/dist/scripts/generate-index.d.ts.map +1 -0
  125. package/dist/scripts/generate-index.js +315 -0
  126. package/dist/scripts/generate-index.js.map +1 -0
  127. package/dist/status/report.d.ts +2 -0
  128. package/dist/status/report.d.ts.map +1 -0
  129. package/dist/status/report.js +9 -0
  130. package/dist/status/report.js.map +1 -0
  131. package/index.json +44929 -0
  132. package/package.json +66 -4
  133. package/scripts/render-python.py +67 -0
@@ -0,0 +1,9 @@
1
+ export interface FeatureCheck {
2
+ name: string;
3
+ status: "ok" | "warn" | "fail";
4
+ detail: string;
5
+ }
6
+ /** Inspect the installed plugin + the current project; never throws. */
7
+ export declare function checkPluginStatus(pluginRoot: string, cwd: string): Promise<FeatureCheck[]>;
8
+ export declare function formatStatus(checks: FeatureCheck[]): string;
9
+ //# sourceMappingURL=plugin-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-status.d.ts","sourceRoot":"","sources":["../../lib/plugin-status.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB;AA2CD,wEAAwE;AACxE,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAgHhG;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,CAM3D"}
@@ -0,0 +1,164 @@
1
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { buildSearchIndex } from "./search.js";
5
+ import { getEastProjectInfo } from "./east-project.js";
6
+ import { execFile } from "node:child_process";
7
+ import { findEastPy } from "@elaraai/east-diagnostics";
8
+ const ICON = { ok: "✓", warn: "⚠", fail: "✗" };
9
+ function check(name, fn) {
10
+ return Promise.resolve()
11
+ .then(fn)
12
+ .catch((e) => ({ name, status: "fail", detail: `check errored: ${String(e).slice(0, 100)}` }));
13
+ }
14
+ function resolves(fromDir, spec) {
15
+ try {
16
+ // Absolute base so module resolution works even when given a relative cwd.
17
+ return createRequire(resolve(fromDir, "_.js")).resolve(spec);
18
+ }
19
+ catch {
20
+ return undefined;
21
+ }
22
+ }
23
+ function nearestTsconfig(fromDir) {
24
+ let dir = resolve(fromDir);
25
+ for (;;) {
26
+ const candidate = join(dir, "tsconfig.json");
27
+ if (existsSync(candidate))
28
+ return candidate;
29
+ const parent = dirname(dir);
30
+ if (parent === dir)
31
+ return undefined;
32
+ dir = parent;
33
+ }
34
+ }
35
+ const BUNDLED = [
36
+ ".build/hooks/session-start.js",
37
+ ".build/hooks/subagent-start.js",
38
+ ".build/hooks/pre-write.js",
39
+ ".build/hooks/pre-read.js",
40
+ ".build/hooks/diagnose.js",
41
+ ".build/hooks/diagnose-bash.js",
42
+ ".build/daemon/server.js",
43
+ ".build/daemon/lsp.js",
44
+ ".build/daemon/east-py-lsp.js",
45
+ ".build/mcp/server.js",
46
+ ];
47
+ /** Inspect the installed plugin + the current project; never throws. */
48
+ export async function checkPluginStatus(pluginRoot, cwd) {
49
+ const checks = [];
50
+ checks.push(await check("Plugin", () => {
51
+ const pkg = JSON.parse(readFileSync(join(pluginRoot, existsSync(join(pluginRoot, ".codex-plugin")) ? ".codex-plugin" : ".claude-plugin", "plugin.json"), "utf8"));
52
+ return { name: "Plugin", status: "ok", detail: `version ${pkg.version ?? "?"} (${pluginRoot})` };
53
+ }));
54
+ checks.push(await check("Bundled artifacts", () => {
55
+ const artifacts = existsSync(join(pluginRoot, ".codex-plugin")) ? [...BUNDLED, ".build/hooks/codex-tools.js"] : BUNDLED;
56
+ const missing = artifacts.filter((a) => !existsSync(join(pluginRoot, a)));
57
+ return missing.length === 0
58
+ ? { name: "Bundled artifacts", status: "ok", detail: `all ${artifacts.length} hook/daemon/MCP bundles present` }
59
+ : { name: "Bundled artifacts", status: "fail", detail: `missing ${missing.length}: ${missing.join(", ")}` };
60
+ }));
61
+ checks.push(await check("Hook configuration", () => {
62
+ const json = JSON.parse(readFileSync(join(pluginRoot, "hooks", "hooks.json"), "utf8"));
63
+ const events = Object.keys(json.hooks ?? {});
64
+ const diagnoseWired = /diagnose\.js|codex-tools\.js/.test(JSON.stringify(json.hooks ?? {}));
65
+ return {
66
+ name: "Hook configuration",
67
+ status: diagnoseWired ? "ok" : "warn",
68
+ detail: diagnoseWired
69
+ ? `${events.length} events: ${events.join(", ")}`
70
+ : `${events.join(", ")} — PostToolUse is NOT wired to diagnose.js (stale install?)`,
71
+ };
72
+ }));
73
+ checks.push(await check("Example search", async () => {
74
+ const indexPath = join(pluginRoot, "index.json");
75
+ const data = JSON.parse(readFileSync(indexPath, "utf8"));
76
+ const entries = data.entries ?? [];
77
+ const programs = entries.filter((e) => e.ir !== undefined);
78
+ const python = programs.filter((e) => typeof e.python === "string").length;
79
+ const index = await buildSearchIndex(indexPath);
80
+ const hits = index.search("array map", { limit: 3 });
81
+ const ok = entries.length > 0 && hits.length > 0 && programs.length > 0 && python === programs.length;
82
+ return {
83
+ name: "Example search (index + MCP)",
84
+ status: ok ? "ok" : "warn",
85
+ detail: `${entries.length} examples indexed, ${programs.length} as IR (${python} with a python rendering); sample query → ${hits.length} hits`,
86
+ };
87
+ }));
88
+ checks.push(await check("Skills", () => {
89
+ const dirs = readdirSync(join(pluginRoot, "skills"), { withFileTypes: true })
90
+ .filter((d) => d.isDirectory() && existsSync(join(pluginRoot, "skills", d.name, "SKILL.md")))
91
+ .map((d) => d.name);
92
+ return { name: "Skills", status: dirs.length > 0 ? "ok" : "warn", detail: `${dirs.length}: ${dirs.join(", ")}` };
93
+ }));
94
+ checks.push(await check("East project (cwd)", async () => {
95
+ const { isEast, skills } = await getEastProjectInfo(cwd);
96
+ return {
97
+ name: "East project (cwd)",
98
+ status: isEast ? "ok" : "warn",
99
+ detail: isEast ? `detected: ${skills.join(", ")}` : `${cwd} is not an East project — hooks stay idle here (expected outside East projects)`,
100
+ };
101
+ }));
102
+ checks.push(await check("Diagnostics (python / east-py)", async () => {
103
+ // Without this check a missing east-py is INVISIBLE: runEastPyLint returns
104
+ // null, every python file silently gets no review, and the rest of this
105
+ // report stays green. Two questions: do the rules answer, and can the warm
106
+ // server (`east-py lsp`, the build tier's home) start.
107
+ const command = findEastPy(cwd);
108
+ const run = (args) => new Promise((done) => {
109
+ execFile(command, args, { timeout: 8000, encoding: "utf-8" }, (error, stdout, stderr) => {
110
+ done({ ok: error === null, stdout: String(stdout), stderr: String(stderr) });
111
+ });
112
+ });
113
+ const listed = await run(["lint", "--list-rules"]);
114
+ if (!listed.ok) {
115
+ return {
116
+ name: "Diagnostics (python / east-py)",
117
+ status: "warn",
118
+ detail: `\`${command}\` did not answer — python East files get NO review until east-py resolves (a project .venv above the file, east-py on PATH, or EAST_PY_LINT)`,
119
+ };
120
+ }
121
+ const rules = listed.stdout.split("\n").filter((l) => l.startsWith("EAS")).length;
122
+ const probe = await run(["lsp", "--probe"]);
123
+ return probe.ok
124
+ ? { name: "Diagnostics (python / east-py)", status: "ok", detail: `${command} — ${rules} rules; ${probe.stdout.trim()} (warm server + build tier)` }
125
+ : {
126
+ name: "Diagnostics (python / east-py)",
127
+ status: "warn",
128
+ detail: `${command} — ${rules} rules, but \`east-py lsp\` cannot start (${probe.stderr.trim().split("\n")[0] ?? "no reason given"}): the rules still run per change, the build tier is off`,
129
+ };
130
+ }));
131
+ checks.push(await check("Diagnostics (PostToolUse daemon)", () => {
132
+ const parts = [];
133
+ let status = "ok";
134
+ const tsOk = resolves(cwd, "typescript") !== undefined;
135
+ parts.push(tsOk ? "typescript resolvable" : "typescript NOT resolvable");
136
+ if (!tsOk)
137
+ status = "warn";
138
+ const tsconfig = nearestTsconfig(cwd) !== undefined;
139
+ parts.push(tsconfig ? "tsconfig found" : "no tsconfig near cwd");
140
+ if (!tsconfig)
141
+ status = "warn";
142
+ const eastOk = resolves(cwd, "@elaraai/east") !== undefined;
143
+ parts.push(eastOk ? "@elaraai/east resolvable (built)" : "@elaraai/east not resolvable/built");
144
+ const daemonOk = existsSync(join(pluginRoot, ".build/daemon/server.js"));
145
+ if (!daemonOk) {
146
+ parts.push("daemon bundle MISSING");
147
+ status = "fail";
148
+ }
149
+ return {
150
+ name: "Diagnostics (PostToolUse daemon)",
151
+ status,
152
+ detail: `${parts.join("; ")} — ${status === "ok" ? "ready" : status === "fail" ? "broken" : "limited (prereqs missing)"}`,
153
+ };
154
+ }));
155
+ return checks;
156
+ }
157
+ export function formatStatus(checks) {
158
+ const anyFail = checks.some((c) => c.status === "fail");
159
+ const anyWarn = checks.some((c) => c.status === "warn");
160
+ const header = anyFail ? "East plugin status — ISSUES" : anyWarn ? "East plugin status — OK (with notes)" : "East plugin status — OK";
161
+ const lines = checks.map((c) => `- ${ICON[c.status]} **${c.name}**: ${c.detail}`);
162
+ return [`## ${header}`, "", ...lines].join("\n");
163
+ }
164
+ //# sourceMappingURL=plugin-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-status.js","sourceRoot":"","sources":["../../lib/plugin-status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /** Whether the plugin reviews this path at all, by extension and location. */
2
+ export declare function reviewable(filePath: string): boolean;
3
+ /**
4
+ * The `<east-code-review>` text for one file.
5
+ *
6
+ * `""` when the file is clean, `null` when it is not East source, not
7
+ * reviewable, or no reviewer answered within budget — a transient miss must
8
+ * stay silent AND stay un-deduped so a retry can succeed.
9
+ *
10
+ * Reviews each distinct file CONTENT once per session: the marker is keyed by
11
+ * session + path + content hash, so re-reads of unchanged content skip while a
12
+ * real edit re-fires. That is also what keeps a file written by Bash and then
13
+ * read from being reviewed twice.
14
+ */
15
+ export declare function reviewFile(sessionId: string, filePath: string): Promise<string | null>;
16
+ //# sourceMappingURL=review.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../lib/review.ts"],"names":[],"mappings":"AAcA,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGpD;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwC5F"}
@@ -0,0 +1,74 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { existsSync, writeFileSync } from "node:fs";
3
+ import { createHash } from "node:crypto";
4
+ import { tmpdir } from "node:os";
5
+ import { join, dirname, resolve } from "node:path";
6
+ import { getEastProjectInfo } from "./east-project.js";
7
+ import { getDiagnosticsText } from "./diagnostics-client.js";
8
+ import { getPythonDiagnosticsText } from "./east-py-lint.js";
9
+ import { PYTHON_EAST_IMPORT } from "@elaraai/east-diagnostics";
10
+ const EAST_IMPORT_PATTERN = /@elaraai\/east/;
11
+ // Vendored / built / generated trees: never review code the agent doesn't own.
12
+ const SKIP_PATH = /[/\\](node_modules|dist|build|\.venv|\.git)[/\\]/;
13
+ /** Whether the plugin reviews this path at all, by extension and location. */
14
+ export function reviewable(filePath) {
15
+ if (SKIP_PATH.test(filePath))
16
+ return false;
17
+ return /\.(py|ts|tsx|js)$/.test(filePath);
18
+ }
19
+ /**
20
+ * The `<east-code-review>` text for one file.
21
+ *
22
+ * `""` when the file is clean, `null` when it is not East source, not
23
+ * reviewable, or no reviewer answered within budget — a transient miss must
24
+ * stay silent AND stay un-deduped so a retry can succeed.
25
+ *
26
+ * Reviews each distinct file CONTENT once per session: the marker is keyed by
27
+ * session + path + content hash, so re-reads of unchanged content skip while a
28
+ * real edit re-fires. That is also what keeps a file written by Bash and then
29
+ * read from being reviewed twice.
30
+ */
31
+ export async function reviewFile(sessionId, filePath) {
32
+ if (!reviewable(filePath))
33
+ return null;
34
+ const python = filePath.endsWith(".py");
35
+ let content;
36
+ try {
37
+ content = await readFile(filePath, "utf-8");
38
+ }
39
+ catch {
40
+ return null;
41
+ }
42
+ if (!(python ? PYTHON_EAST_IMPORT : EAST_IMPORT_PATTERN).test(content))
43
+ return null;
44
+ // Resolve the East project from the FILE's location, NOT the session cwd:
45
+ // Claude is frequently launched from elsewhere while editing a project under
46
+ // a sibling directory. A python file's project is wherever its `.venv` is.
47
+ const projectDir = dirname(resolve(filePath));
48
+ if (!python) {
49
+ const { isEast } = await getEastProjectInfo(projectDir);
50
+ if (!isEast)
51
+ return null;
52
+ }
53
+ const key = createHash("sha1")
54
+ .update(`${sessionId}\0${filePath}\0`)
55
+ .update(content)
56
+ .digest("hex")
57
+ .slice(0, 20);
58
+ const marker = join(tmpdir(), `east-diag-seen-${key}`);
59
+ if (existsSync(marker))
60
+ return null;
61
+ const text = python
62
+ ? await getPythonDiagnosticsText(filePath)
63
+ : await getDiagnosticsText(projectDir, filePath);
64
+ if (text === null)
65
+ return null; // transient — don't mark, allow a retry
66
+ try {
67
+ writeFileSync(marker, "");
68
+ }
69
+ catch {
70
+ /* best-effort dedupe */
71
+ }
72
+ return text;
73
+ }
74
+ //# sourceMappingURL=review.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../../lib/review.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,mBAAmB,GAAG,gBAAgB,CAAC;AAC7C,+EAA+E;AAC/E,MAAM,SAAS,GAAG,kDAAkD,CAAC;AAErE,8EAA8E;AAC9E,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,IAAI,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3C,OAAO,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,SAAiB,EAAE,QAAgB;IAClE,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAExC,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAEpF,0EAA0E;IAC1E,6EAA6E;IAC7E,2EAA2E;IAC3E,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC9C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,kBAAkB,CAAC,UAAU,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;IAC3B,CAAC;IAED,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC;SAC3B,MAAM,CAAC,GAAG,SAAS,KAAK,QAAQ,IAAI,CAAC;SACrC,MAAM,CAAC,OAAO,CAAC;SACf,MAAM,CAAC,KAAK,CAAC;SACb,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAChB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,kBAAkB,GAAG,EAAE,CAAC,CAAC;IACvD,IAAI,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAEpC,MAAM,IAAI,GAAG,MAAM;QACjB,CAAC,CAAC,MAAM,wBAAwB,CAAC,QAAQ,CAAC;QAC1C,CAAC,CAAC,MAAM,kBAAkB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACnD,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC,CAAC,wCAAwC;IAExE,IAAI,CAAC;QACH,aAAa,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,wBAAwB;IAC1B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,44 @@
1
+ /** The names the search-first guidance speaks in. */
2
+ export interface SearchGuidance {
3
+ /** The search tool as the agent calls it, e.g. `mcp__plugin_east_east__search_east_examples`. */
4
+ searchTool: string;
5
+ /** The fetch tool as the agent calls it, e.g. `mcp__plugin_east_east__get_east_example`. */
6
+ getTool: string;
7
+ /** What the code, its packages and its skills are called, e.g. `East`. */
8
+ corpus: string;
9
+ /** The npm scope whose installed packages the read reminder watches, e.g. `@elaraai`. */
10
+ scope: string;
11
+ }
12
+ /** East's names. */
13
+ export declare const EAST_GUIDANCE: SearchGuidance;
14
+ /**
15
+ * The instruction a write of corpus code meets when no search is on record.
16
+ *
17
+ * @param g - The names it speaks in
18
+ * @returns The gate's text
19
+ */
20
+ export declare function gateText(g: SearchGuidance): string;
21
+ /**
22
+ * The reminder a read of the installed packages or the examples gets.
23
+ *
24
+ * @param g - The names it speaks in
25
+ * @returns The reminder's text
26
+ */
27
+ export declare function readText(g: SearchGuidance): string;
28
+ /**
29
+ * Whether a Read, Grep or Glob reaches for the example corpus: the installed
30
+ * packages of `scope` or the `*.examples.ts` files. A project's own grep for
31
+ * its imports of the scope is not a sweep of the packages, and a read of one
32
+ * project file is never one.
33
+ *
34
+ * @param scope - The npm scope whose installed packages count, e.g. `@elaraai`
35
+ * @returns The check, over a tool's name and input
36
+ */
37
+ export declare function corpusReadFor(scope: string): (tool: string, input: Record<string, unknown>) => boolean;
38
+ /** East's gate. */
39
+ export declare const GATE_TEXT: string;
40
+ /** East's read reminder. */
41
+ export declare const READ_TEXT: string;
42
+ /** Whether a Read, Grep or Glob reaches for East's corpus. */
43
+ export declare const isExampleCorpusRead: (tool: string, input: Record<string, unknown>) => boolean;
44
+ //# sourceMappingURL=search-guidance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-guidance.d.ts","sourceRoot":"","sources":["../../lib/search-guidance.ts"],"names":[],"mappings":"AAKA,qDAAqD;AACrD,MAAM,WAAW,cAAc;IAC7B,iGAAiG;IACjG,UAAU,EAAE,MAAM,CAAC;IACnB,4FAA4F;IAC5F,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;IACf,yFAAyF;IACzF,KAAK,EAAE,MAAM,CAAC;CACf;AAED,oBAAoB;AACpB,eAAO,MAAM,aAAa,EAAE,cAK3B,CAAC;AAQF;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAQlD;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAKlD;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAatG;AAED,mBAAmB;AACnB,eAAO,MAAM,SAAS,EAAE,MAAgC,CAAC;AAEzD,4BAA4B;AAC5B,eAAO,MAAM,SAAS,EAAE,MAAgC,CAAC;AAEzD,8DAA8D;AAC9D,eAAO,MAAM,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAA4C,CAAC"}
@@ -0,0 +1,73 @@
1
+ // The search-first guidance (#654): the gate a write of corpus code meets when
2
+ // no example search is on record, and the reminder a read of the installed
3
+ // packages or the examples gets. Spoken in a plugin's own names, so a plugin
4
+ // that builds on East's gives the same guidance over its own corpus.
5
+ /** East's names. */
6
+ export const EAST_GUIDANCE = {
7
+ searchTool: "mcp__plugin_east_east__search_east_examples",
8
+ getTool: "mcp__plugin_east_east__get_east_example",
9
+ corpus: "East",
10
+ scope: "@elaraai",
11
+ };
12
+ /** A tool's own name, without the `mcp__<server>__` prefix the agent calls it by. */
13
+ function bare(tool) {
14
+ const at = tool.lastIndexOf("__");
15
+ return at < 0 ? tool : tool.slice(at + 2);
16
+ }
17
+ /**
18
+ * The instruction a write of corpus code meets when no search is on record.
19
+ *
20
+ * @param g - The names it speaks in
21
+ * @returns The gate's text
22
+ */
23
+ export function gateText(g) {
24
+ return [
25
+ `STOP: no ${g.corpus} example search on record in this session, and this is ${g.corpus} code.`,
26
+ `Before writing or changing ${g.corpus} code, search the tested example index — it is the API reference:`,
27
+ `1. \`${g.searchTool}\` with what you are about to do (language: "python" for east-py, "typescript" otherwise); summaries come back — id, signature, inputs and result.`,
28
+ `2. \`${g.getTool}\` for the one or two that match, and pattern your code on them.`,
29
+ `Do not read node_modules/${g.scope}/** or *.examples.ts files instead: the index is the same corpus, exact and far cheaper. Every ${g.corpus} skill requires this step.`,
30
+ ].join("\n");
31
+ }
32
+ /**
33
+ * The reminder a read of the installed packages or the examples gets.
34
+ *
35
+ * @param g - The names it speaks in
36
+ * @returns The reminder's text
37
+ */
38
+ export function readText(g) {
39
+ return [
40
+ `Note: the ${g.corpus} example index is the API reference — \`${g.searchTool}\` (then \`${bare(g.getTool)}\`) returns the same tested programs as the ${g.corpus} packages' examples and type declarations, exact, printed in TypeScript or python, at a fraction of the tokens.`,
41
+ `Reading \`.d.ts\` signatures or sweeping \`*.examples.ts\` files reliably produces broken ${g.corpus} code that still type-checks: the signatures omit the runtime rules. Search instead, and read a specific file only when the search pointed you at it.`,
42
+ ].join("\n");
43
+ }
44
+ /**
45
+ * Whether a Read, Grep or Glob reaches for the example corpus: the installed
46
+ * packages of `scope` or the `*.examples.ts` files. A project's own grep for
47
+ * its imports of the scope is not a sweep of the packages, and a read of one
48
+ * project file is never one.
49
+ *
50
+ * @param scope - The npm scope whose installed packages count, e.g. `@elaraai`
51
+ * @returns The check, over a tool's name and input
52
+ */
53
+ export function corpusReadFor(scope) {
54
+ const escaped = scope.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
55
+ const packagePath = new RegExp(`[/\\\\]node_modules[/\\\\]${escaped}[/\\\\]`);
56
+ // a pattern that sweeps the packages themselves (`node_modules/@elaraai/**`), as opposed to a project's own grep for its `@elaraai/east` imports
57
+ const packagePattern = new RegExp(`(^|[/\\\\])node_modules[/\\\\]${escaped}([/\\\\]|$)`);
58
+ const examplesFile = /\.examples\.tsx?$/;
59
+ return (tool, input) => {
60
+ const file = typeof input['file_path'] === 'string' ? input['file_path'] : '';
61
+ const dir = typeof input['path'] === 'string' ? input['path'] : '';
62
+ const pattern = typeof input['pattern'] === 'string' ? input['pattern'] : '';
63
+ return tool === 'Read' ? packagePath.test(file) || examplesFile.test(file)
64
+ : packagePath.test(dir) || packagePattern.test(pattern) || /\.examples\.tsx?/.test(pattern);
65
+ };
66
+ }
67
+ /** East's gate. */
68
+ export const GATE_TEXT = gateText(EAST_GUIDANCE);
69
+ /** East's read reminder. */
70
+ export const READ_TEXT = readText(EAST_GUIDANCE);
71
+ /** Whether a Read, Grep or Glob reaches for East's corpus. */
72
+ export const isExampleCorpusRead = corpusReadFor(EAST_GUIDANCE.scope);
73
+ //# sourceMappingURL=search-guidance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-guidance.js","sourceRoot":"","sources":["../../lib/search-guidance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ import { type HookInput } from "./hook-io.js";
2
+ /** What a search-first hook answers: context given to the agent, or the call refused with its reason. */
3
+ export interface SearchHookReply {
4
+ decision: "context" | "deny";
5
+ text: string;
6
+ }
7
+ /** The pre-write gate's configuration. */
8
+ export interface SearchGateOptions {
9
+ /** The tools whose call counts as a search: the plugin's own and, with the East plugin beside it, East's. */
10
+ searchTools: readonly string[];
11
+ /** The instruction the gate gives (`gateText(...)`). */
12
+ gateText: string;
13
+ /** Whether a file's code is the corpus's: it imports the corpus's packages. */
14
+ isCorpusCode(code: string, python: boolean): boolean;
15
+ /** Whether the project at `cwd` is one the gate applies in; every project when absent. */
16
+ isProject?(cwd: string, python: boolean): Promise<boolean>;
17
+ /** The prefix of the per-session marker in the temporary directory that remembers a search seen. */
18
+ markerPrefix: string;
19
+ /** The environment variable that makes the gate refuse the write when it is `deny`. */
20
+ requireSearchEnv: string;
21
+ }
22
+ /** East's gate. */
23
+ export declare const EAST_SEARCH_GATE: SearchGateOptions;
24
+ /**
25
+ * The pre-write gate's answer to an Edit or Write: the instruction to search
26
+ * first when it writes corpus code and no search is on record, else nothing. A
27
+ * reminder, or a refusal when the options' environment variable is `deny`.
28
+ * Once a search is seen, a per-session marker keeps every later write silent,
29
+ * so a long transcript is read at most once.
30
+ *
31
+ * @param event - The PreToolUse event
32
+ * @param options - The gate's configuration
33
+ * @returns The reply, or null when the write passes
34
+ */
35
+ export declare function preWriteGate(event: HookInput, options: SearchGateOptions): Promise<SearchHookReply | null>;
36
+ /** The read reminder's configuration. */
37
+ export interface ReadReminderOptions {
38
+ /** The reminder (`readText(...)`). */
39
+ readText: string;
40
+ /** Whether a Read, Grep or Glob reaches for the corpus (`corpusReadFor(scope)`). */
41
+ isCorpusRead(tool: string, input: Record<string, unknown>): boolean;
42
+ }
43
+ /** East's reminder. */
44
+ export declare const EAST_READ_REMINDER: ReadReminderOptions;
45
+ /**
46
+ * The read reminder's answer to a Read, Grep or Glob: the reminder when it
47
+ * reaches for the corpus, else nothing. Never a refusal.
48
+ *
49
+ * @param event - The PreToolUse event
50
+ * @param options - The reminder's configuration
51
+ * @returns The reply, or null
52
+ */
53
+ export declare function preReadReminder(event: HookInput, options: ReadReminderOptions): SearchHookReply | null;
54
+ /**
55
+ * Writes a hook's reply to stdout, as the host reads it.
56
+ *
57
+ * @param hookEventName - The event the hook answers, e.g. `PreToolUse`
58
+ * @param reply - The reply
59
+ */
60
+ export declare function writeSearchHookReply(hookEventName: string, reply: SearchHookReply): void;
61
+ /**
62
+ * Runs the pre-write gate as a hook: reads the event from stdin, writes the
63
+ * reply, if any, to stdout.
64
+ *
65
+ * @param options - The gate's configuration
66
+ */
67
+ export declare function runPreWriteGate(options: SearchGateOptions): Promise<void>;
68
+ /**
69
+ * Runs the read reminder as a hook: reads the event from stdin, writes the
70
+ * reply, if any, to stdout.
71
+ *
72
+ * @param options - The reminder's configuration
73
+ */
74
+ export declare function runPreReadReminder(options: ReadReminderOptions): Promise<void>;
75
+ //# sourceMappingURL=search-hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-hooks.d.ts","sourceRoot":"","sources":["../../lib/search-hooks.ts"],"names":[],"mappings":"AAKA,OAAO,EAAqD,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAWjG,yGAAyG;AACzG,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,SAAS,GAAG,MAAM,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,0CAA0C;AAC1C,MAAM,WAAW,iBAAiB;IAChC,6GAA6G;IAC7G,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;IACrD,0FAA0F;IAC1F,SAAS,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D,oGAAoG;IACpG,YAAY,EAAE,MAAM,CAAC;IACrB,uFAAuF;IACvF,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,mBAAmB;AACnB,eAAO,MAAM,gBAAgB,EAAE,iBAO9B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAmChH;AAED,yCAAyC;AACzC,MAAM,WAAW,mBAAmB;IAClC,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;CACrE;AAED,uBAAuB;AACvB,eAAO,MAAM,kBAAkB,EAAE,mBAAgF,CAAC;AAElH;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,mBAAmB,GAAG,eAAe,GAAG,IAAI,CAEtG;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAGxF;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAG/E;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAGpF"}
@@ -0,0 +1,117 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { existsSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { PYTHON_EAST_IMPORT } from "@elaraai/east-diagnostics";
6
+ import { readHookInput, writeHookDecision, writeHookOutput } from "./hook-io.js";
7
+ import { getEastProjectInfo } from "./east-project.js";
8
+ import { SEARCH_TOOLS, searchedInTranscript } from "./transcript.js";
9
+ import { GATE_TEXT, READ_TEXT, isExampleCorpusRead } from "./search-guidance.js";
10
+ /** East's gate. */
11
+ export const EAST_SEARCH_GATE = {
12
+ searchTools: SEARCH_TOOLS,
13
+ gateText: GATE_TEXT,
14
+ isCorpusCode: (code, python) => (python ? PYTHON_EAST_IMPORT : /@elaraai\/east/).test(code),
15
+ isProject: async (cwd, python) => python || (await getEastProjectInfo(cwd)).isEast,
16
+ markerPrefix: "east-search-seen",
17
+ requireSearchEnv: "EAST_REQUIRE_SEARCH",
18
+ };
19
+ /**
20
+ * The pre-write gate's answer to an Edit or Write: the instruction to search
21
+ * first when it writes corpus code and no search is on record, else nothing. A
22
+ * reminder, or a refusal when the options' environment variable is `deny`.
23
+ * Once a search is seen, a per-session marker keeps every later write silent,
24
+ * so a long transcript is read at most once.
25
+ *
26
+ * @param event - The PreToolUse event
27
+ * @param options - The gate's configuration
28
+ * @returns The reply, or null when the write passes
29
+ */
30
+ export async function preWriteGate(event, options) {
31
+ const cwd = event.cwd || process.cwd();
32
+ const filePath = event.tool_input?.file_path;
33
+ if (!filePath)
34
+ return null;
35
+ const python = filePath.endsWith(".py");
36
+ if (!python && !filePath.endsWith(".ts") && !filePath.endsWith(".tsx") && !filePath.endsWith(".js"))
37
+ return null;
38
+ // Is this the corpus's code? A Write carries its content; an Edit only its
39
+ // diff, so the file on disk (or the new text) says what it is.
40
+ let code = "";
41
+ if (event.tool_name === "Write") {
42
+ code = event.tool_input?.content ?? "";
43
+ }
44
+ else if (event.tool_name === "Edit") {
45
+ try {
46
+ code = await readFile(filePath, "utf-8");
47
+ }
48
+ catch {
49
+ code = event.tool_input?.new_string ?? "";
50
+ }
51
+ }
52
+ if (!options.isCorpusCode(code, python))
53
+ return null;
54
+ if (options.isProject !== undefined && !(await options.isProject(cwd, python)))
55
+ return null;
56
+ const marker = join(tmpdir(), `${options.markerPrefix}-${event.session_id}`);
57
+ if (existsSync(marker))
58
+ return null;
59
+ if (event.transcript_path && (await searchedInTranscript(event.transcript_path, options.searchTools))) {
60
+ try {
61
+ writeFileSync(marker, options.searchTools.join("\n"));
62
+ }
63
+ catch {
64
+ /* best-effort */
65
+ }
66
+ return null;
67
+ }
68
+ return { decision: process.env[options.requireSearchEnv] === "deny" ? "deny" : "context", text: options.gateText };
69
+ }
70
+ /** East's reminder. */
71
+ export const EAST_READ_REMINDER = { readText: READ_TEXT, isCorpusRead: isExampleCorpusRead };
72
+ /**
73
+ * The read reminder's answer to a Read, Grep or Glob: the reminder when it
74
+ * reaches for the corpus, else nothing. Never a refusal.
75
+ *
76
+ * @param event - The PreToolUse event
77
+ * @param options - The reminder's configuration
78
+ * @returns The reply, or null
79
+ */
80
+ export function preReadReminder(event, options) {
81
+ return options.isCorpusRead(event.tool_name ?? "", event.tool_input ?? {}) ? { decision: "context", text: options.readText } : null;
82
+ }
83
+ /**
84
+ * Writes a hook's reply to stdout, as the host reads it.
85
+ *
86
+ * @param hookEventName - The event the hook answers, e.g. `PreToolUse`
87
+ * @param reply - The reply
88
+ */
89
+ export function writeSearchHookReply(hookEventName, reply) {
90
+ if (reply.decision === "deny")
91
+ writeHookDecision(hookEventName, "deny", reply.text);
92
+ else
93
+ writeHookOutput(hookEventName, reply.text);
94
+ }
95
+ /**
96
+ * Runs the pre-write gate as a hook: reads the event from stdin, writes the
97
+ * reply, if any, to stdout.
98
+ *
99
+ * @param options - The gate's configuration
100
+ */
101
+ export async function runPreWriteGate(options) {
102
+ const reply = await preWriteGate(await readHookInput(), options);
103
+ if (reply !== null)
104
+ writeSearchHookReply("PreToolUse", reply);
105
+ }
106
+ /**
107
+ * Runs the read reminder as a hook: reads the event from stdin, writes the
108
+ * reply, if any, to stdout.
109
+ *
110
+ * @param options - The reminder's configuration
111
+ */
112
+ export async function runPreReadReminder(options) {
113
+ const reply = preReadReminder(await readHookInput(), options);
114
+ if (reply !== null)
115
+ writeSearchHookReply("PreToolUse", reply);
116
+ }
117
+ //# sourceMappingURL=search-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-hooks.js","sourceRoot":"","sources":["../../lib/search-hooks.ts"],"names":[],"mappings":"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"}