fushiguro-mcp 0.1.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 (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +278 -0
  3. package/catalog/agents/backend-api.md +71 -0
  4. package/catalog/agents/content-editor.md +59 -0
  5. package/catalog/agents/customer-support.md +60 -0
  6. package/catalog/agents/data-analyst.md +66 -0
  7. package/catalog/agents/database.md +65 -0
  8. package/catalog/agents/docs.md +52 -0
  9. package/catalog/agents/process-automation.md +61 -0
  10. package/catalog/agents/research-analyst.md +57 -0
  11. package/catalog/agents/security.md +68 -0
  12. package/catalog/agents/testing.md +63 -0
  13. package/catalog/agents/ux-ui-specialist.md +87 -0
  14. package/catalog/connectors/crm.md +24 -0
  15. package/catalog/connectors/data-warehouse.md +26 -0
  16. package/catalog/connectors/document-store.md +23 -0
  17. package/catalog/connectors/helpdesk.md +24 -0
  18. package/catalog/knowledge/platform/using-this-catalog.md +50 -0
  19. package/catalog/runbooks/_TEMPLATE.md +52 -0
  20. package/catalog/runbooks/ai-use-case-intake.md +89 -0
  21. package/catalog/runbooks/change-release.md +54 -0
  22. package/catalog/runbooks/customer-escalation.md +67 -0
  23. package/catalog/skills/cite-sources.md +18 -0
  24. package/catalog/skills/clarify-scope.md +18 -0
  25. package/catalog/skills/data-quality-check.md +22 -0
  26. package/catalog/skills/risk-and-compliance-check.md +21 -0
  27. package/catalog/skills/stakeholder-summary.md +21 -0
  28. package/catalog/tools/knowledge-search.md +14 -0
  29. package/catalog/tools/shell.md +15 -0
  30. package/catalog/tools/web-fetch.md +15 -0
  31. package/catalog/tools/web-search.md +15 -0
  32. package/catalog/topics/ai-adoption.md +25 -0
  33. package/catalog/topics/customer-operations.md +21 -0
  34. package/catalog/topics/data-and-reporting.md +20 -0
  35. package/catalog/topics/marketing-content.md +19 -0
  36. package/catalog/topics/product-engineering.md +20 -0
  37. package/dist/catalog.d.ts +34 -0
  38. package/dist/catalog.js +412 -0
  39. package/dist/catalog.js.map +1 -0
  40. package/dist/config.d.ts +17 -0
  41. package/dist/config.js +48 -0
  42. package/dist/config.js.map +1 -0
  43. package/dist/index.d.ts +10 -0
  44. package/dist/index.js +46 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/init.d.ts +5 -0
  47. package/dist/init.js +159 -0
  48. package/dist/init.js.map +1 -0
  49. package/dist/knowledge.d.ts +31 -0
  50. package/dist/knowledge.js +126 -0
  51. package/dist/knowledge.js.map +1 -0
  52. package/dist/main.d.ts +1 -0
  53. package/dist/main.js +22 -0
  54. package/dist/main.js.map +1 -0
  55. package/dist/memory.d.ts +85 -0
  56. package/dist/memory.js +374 -0
  57. package/dist/memory.js.map +1 -0
  58. package/dist/quiet.d.ts +1 -0
  59. package/dist/quiet.js +21 -0
  60. package/dist/quiet.js.map +1 -0
  61. package/dist/registry.d.ts +15 -0
  62. package/dist/registry.js +128 -0
  63. package/dist/registry.js.map +1 -0
  64. package/dist/router.d.ts +55 -0
  65. package/dist/router.js +358 -0
  66. package/dist/router.js.map +1 -0
  67. package/dist/server.d.ts +6 -0
  68. package/dist/server.js +529 -0
  69. package/dist/server.js.map +1 -0
  70. package/dist/text.d.ts +20 -0
  71. package/dist/text.js +72 -0
  72. package/dist/text.js.map +1 -0
  73. package/dist/types.d.ts +195 -0
  74. package/dist/types.js +3 -0
  75. package/dist/types.js.map +1 -0
  76. package/package.json +57 -0
package/dist/init.js ADDED
@@ -0,0 +1,159 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, join, relative, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
5
+ const DIRS = [
6
+ "agents",
7
+ "skills",
8
+ "knowledge",
9
+ "runbooks",
10
+ "topics",
11
+ "tools",
12
+ "connectors",
13
+ ];
14
+ const CATALOG_README = `# <Your business>'s Fushiguro catalog
15
+
16
+ This directory is your organisation's AI operating layer. It layers over the
17
+ catalog that ships with the server: an entry here with the same \`name\` as a
18
+ packaged one **merges into it** — your scalar fields win, list fields union, and
19
+ your body is appended. Nothing is forked, so you keep receiving improvements to
20
+ the base prompts underneath your own additions.
21
+
22
+ Commit this directory. It is a shared business asset, and it is the part that
23
+ makes answers specific to you rather than generic.
24
+
25
+ ## Where to start
26
+
27
+ 1. **\`knowledge/\`** — highest leverage by a wide margin. Drop your real policy
28
+ and product documents into a folder per collection (\`knowledge/policies/\`,
29
+ \`knowledge/product/\`). Plain markdown; frontmatter is optional here.
30
+ 2. **\`connectors/\`** — describe the systems you actually use, and delete the
31
+ packaged ones you do not. Record env var **names**, never secret values.
32
+ 3. **\`runbooks/\`** — the packaged runbooks are templates. Copy
33
+ \`runbook-template.md\` and fill in one real process. Escalation is usually
34
+ the right first one.
35
+ 4. **\`agents/\`** — extend a packaged agent with your house rules, or add a role
36
+ the base set does not cover.
37
+
38
+ Run the \`status\` tool at any time to see what is loaded and what is still an
39
+ unfilled template.
40
+
41
+ ## Extending a packaged agent
42
+
43
+ Create \`agents/customer-support.md\` with only what you are adding:
44
+
45
+ \`\`\`markdown
46
+ ---
47
+ name: customer-support
48
+ model: opus
49
+ keywords: [overage, seat, enterprise contract]
50
+ connectors: [our-helpdesk]
51
+ ---
52
+
53
+ ## How we do this here
54
+
55
+ Our plans are Starter, Pro, and Enterprise, and entitlement differs sharply
56
+ between them. Always confirm the plan before quoting any policy window.
57
+ \`\`\`
58
+
59
+ Add \`override: true\` to the frontmatter to replace the packaged body outright
60
+ instead of appending to it.
61
+
62
+ ## What does NOT go here
63
+
64
+ Personal working preferences — how one person likes their code formatted, or
65
+ their tone. Those are captured per-user through the \`remember\` tool and stored
66
+ in that person's own memory database. This catalog is for rules that apply to
67
+ everyone.
68
+ `;
69
+ const KNOWLEDGE_PLACEHOLDER = `---
70
+ name: example-policy
71
+ title: Example policy — replace this
72
+ collection: policies
73
+ reviewed: REPLACE-WITH-DATE
74
+ ---
75
+
76
+ # Replace this file with a real policy document
77
+
78
+ Everything in this directory is indexed and retrieved for agents to cite. This
79
+ placeholder exists so retrieval has something to return before you have added
80
+ anything real — delete it once you have.
81
+
82
+ Knowledge files are ordinary markdown. Headings matter: they become the citation
83
+ trail an agent shows when it uses a passage, so write headings that identify the
84
+ rule rather than generic ones like "Overview".
85
+
86
+ Good documents to add first, in rough order of value:
87
+
88
+ - Refund, cancellation, and entitlement policy, per plan
89
+ - Escalation and approval thresholds — who can authorise what
90
+ - Product behaviour that support is repeatedly asked to explain
91
+ - Brand voice and writing guidance
92
+ - Metric definitions, so one metric keeps one meaning
93
+
94
+ Aim for the document a new hire would need on their first week. If it is not
95
+ written down anywhere, an agent cannot be told to follow it.
96
+ `;
97
+ /**
98
+ * Scaffolds a business's own catalog. Never overwrites an existing file, so it
99
+ * is safe to re-run after the server has been upgraded.
100
+ */
101
+ export function init(targetArg) {
102
+ const catalogDir = resolve(targetArg ?? join(process.cwd(), ".fushiguro", "catalog"));
103
+ const results = [];
104
+ const write = (path, contents) => {
105
+ if (existsSync(path)) {
106
+ results.push({ path, status: "skipped" });
107
+ return;
108
+ }
109
+ mkdirSync(dirname(path), { recursive: true });
110
+ writeFileSync(path, contents);
111
+ results.push({ path, status: "created" });
112
+ };
113
+ for (const dir of DIRS)
114
+ mkdirSync(join(catalogDir, dir), { recursive: true });
115
+ mkdirSync(join(catalogDir, "knowledge", "policies"), { recursive: true });
116
+ write(join(catalogDir, "README.md"), CATALOG_README);
117
+ write(join(catalogDir, "knowledge", "policies", "example-policy.md"), KNOWLEDGE_PLACEHOLDER);
118
+ // Give them the runbook template locally, so filling one in needs no lookup.
119
+ // The copy gets its own name — sharing one with a packaged entry would merge
120
+ // the two rather than starting a new runbook.
121
+ const templateSource = join(packageRoot, "catalog", "runbooks", "_TEMPLATE.md");
122
+ if (existsSync(templateSource)) {
123
+ const template = readFileSync(templateSource, "utf8")
124
+ .replace(/^name: .*$/m, "name: my-first-runbook")
125
+ .replace(/^title: .*$/m, "title: My First Runbook — rename this")
126
+ .replace(/^description: .*$/m, "description: Replace this with what the process does. Rename the file and the name field to match.");
127
+ write(join(catalogDir, "runbooks", "my-first-runbook.md"), template);
128
+ }
129
+ const created = results.filter((r) => r.status === "created");
130
+ const skipped = results.filter((r) => r.status === "skipped");
131
+ const show = (p) => relative(process.cwd(), p) || p;
132
+ process.stdout.write(`\nCatalog ready at ${show(catalogDir)}\n\n`);
133
+ for (const r of created)
134
+ process.stdout.write(` created ${show(r.path)}\n`);
135
+ for (const r of skipped)
136
+ process.stdout.write(` kept ${show(r.path)} (already existed)\n`);
137
+ process.stdout.write([
138
+ "",
139
+ "Next:",
140
+ "",
141
+ " 1. Register the server so your whole team gets it from the repo:",
142
+ "",
143
+ " claude mcp add fushiguro --scope project -- npx -y fushiguro-mcp",
144
+ "",
145
+ " That writes .mcp.json. Commit it, and teammates are prompted to",
146
+ " enable the server the next time they open the repo.",
147
+ "",
148
+ ` 2. Put your real policy and product documents in ${show(join(catalogDir, "knowledge"))}/`,
149
+ " That is the highest-leverage thing in the catalog by a wide margin.",
150
+ "",
151
+ " 3. Fill in one runbook, then set `template: false` in its frontmatter.",
152
+ "",
153
+ " 4. Commit the catalog. It is a shared business asset.",
154
+ "",
155
+ "Run the `status` tool once connected to see what loaded.",
156
+ "",
157
+ ].join("\n"));
158
+ }
159
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { KnowledgeChunk } from "./types.js";
2
+ export interface ChunkHit extends KnowledgeChunk {
3
+ /** Relevance, higher is better. Comparable within one search only. */
4
+ score: number;
5
+ }
6
+ /**
7
+ * Full-text index over the knowledge corpus. The whole index is rebuilt when the
8
+ * corpus hash changes — the corpus is small enough that incremental updates would
9
+ * cost more in complexity than they save in time.
10
+ */
11
+ export declare class KnowledgeIndex {
12
+ #private;
13
+ constructor(dbPath: string);
14
+ close(): void;
15
+ /** Rebuilds the index if the corpus changed. Returns true when it rebuilt. */
16
+ sync(chunks: KnowledgeChunk[], signature: string): boolean;
17
+ /**
18
+ * Ranked chunk retrieval. `collections` narrows the corpus to what an agent is
19
+ * allowed to see; a heading hit counts for more than a body hit, since headings
20
+ * are the terms the author chose deliberately.
21
+ */
22
+ search(query: string, options?: {
23
+ collections?: string[];
24
+ docs?: string[];
25
+ limit?: number;
26
+ }): ChunkHit[];
27
+ stats(): {
28
+ chunks: number;
29
+ collections: Record<string, number>;
30
+ };
31
+ }
@@ -0,0 +1,126 @@
1
+ import { mkdirSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ import { DatabaseSync } from "node:sqlite";
4
+ import { ftsQuery } from "./text.js";
5
+ /**
6
+ * Full-text index over the knowledge corpus. The whole index is rebuilt when the
7
+ * corpus hash changes — the corpus is small enough that incremental updates would
8
+ * cost more in complexity than they save in time.
9
+ */
10
+ export class KnowledgeIndex {
11
+ #db;
12
+ constructor(dbPath) {
13
+ if (dbPath !== ":memory:")
14
+ mkdirSync(dirname(dbPath), { recursive: true });
15
+ this.#db = new DatabaseSync(dbPath);
16
+ this.#db.exec("PRAGMA journal_mode = WAL");
17
+ this.#db.exec(`
18
+ CREATE TABLE IF NOT EXISTS knowledge_meta (
19
+ key TEXT PRIMARY KEY,
20
+ value TEXT NOT NULL
21
+ );
22
+
23
+ CREATE TABLE IF NOT EXISTS knowledge_chunks (
24
+ rowid_key INTEGER PRIMARY KEY,
25
+ id TEXT NOT NULL UNIQUE,
26
+ doc_name TEXT NOT NULL,
27
+ collection TEXT NOT NULL,
28
+ heading TEXT NOT NULL DEFAULT '',
29
+ text TEXT NOT NULL,
30
+ idx INTEGER NOT NULL
31
+ );
32
+
33
+ CREATE INDEX IF NOT EXISTS idx_chunks_collection ON knowledge_chunks(collection);
34
+
35
+ CREATE VIRTUAL TABLE IF NOT EXISTS knowledge_fts
36
+ USING fts5(text, heading, doc_name, tokenize = 'porter unicode61');
37
+ `);
38
+ }
39
+ close() {
40
+ this.#db.close();
41
+ }
42
+ #signature() {
43
+ const row = this.#db
44
+ .prepare("SELECT value FROM knowledge_meta WHERE key = 'signature'")
45
+ .get();
46
+ return row?.value ?? "";
47
+ }
48
+ /** Rebuilds the index if the corpus changed. Returns true when it rebuilt. */
49
+ sync(chunks, signature) {
50
+ if (signature === this.#signature())
51
+ return false;
52
+ this.#db.exec("BEGIN");
53
+ try {
54
+ this.#db.exec("DELETE FROM knowledge_chunks");
55
+ this.#db.exec("DELETE FROM knowledge_fts");
56
+ const insertChunk = this.#db.prepare(`INSERT INTO knowledge_chunks (id, doc_name, collection, heading, text, idx)
57
+ VALUES (?, ?, ?, ?, ?, ?)`);
58
+ const insertFts = this.#db.prepare("INSERT INTO knowledge_fts (rowid, text, heading, doc_name) VALUES (?, ?, ?, ?)");
59
+ for (const chunk of chunks) {
60
+ const result = insertChunk.run(chunk.id, chunk.docName, chunk.collection, chunk.heading, chunk.text, chunk.index);
61
+ insertFts.run(Number(result.lastInsertRowid), chunk.text, chunk.heading, chunk.docName);
62
+ }
63
+ this.#db
64
+ .prepare(`INSERT INTO knowledge_meta (key, value) VALUES ('signature', ?)
65
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`)
66
+ .run(signature);
67
+ this.#db.exec("COMMIT");
68
+ }
69
+ catch (err) {
70
+ this.#db.exec("ROLLBACK");
71
+ throw err;
72
+ }
73
+ return true;
74
+ }
75
+ /**
76
+ * Ranked chunk retrieval. `collections` narrows the corpus to what an agent is
77
+ * allowed to see; a heading hit counts for more than a body hit, since headings
78
+ * are the terms the author chose deliberately.
79
+ */
80
+ search(query, options = {}) {
81
+ const match = ftsQuery(query);
82
+ if (!match)
83
+ return [];
84
+ const limit = Math.min(50, Math.max(1, options.limit ?? 6));
85
+ const clauses = ["knowledge_fts MATCH ?"];
86
+ const params = [match];
87
+ if (options.collections?.length) {
88
+ clauses.push(`c.collection IN (${options.collections.map(() => "?").join(", ")})`);
89
+ params.push(...options.collections);
90
+ }
91
+ if (options.docs?.length) {
92
+ clauses.push(`c.doc_name IN (${options.docs.map(() => "?").join(", ")})`);
93
+ params.push(...options.docs);
94
+ }
95
+ params.push(limit);
96
+ const rows = this.#db
97
+ .prepare(`SELECT c.id, c.doc_name, c.collection, c.heading, c.text, c.idx,
98
+ -bm25(knowledge_fts, 1.0, 2.0, 1.0) AS score
99
+ FROM knowledge_fts f
100
+ JOIN knowledge_chunks c ON c.rowid_key = f.rowid
101
+ WHERE ${clauses.join(" AND ")}
102
+ ORDER BY score DESC
103
+ LIMIT ?`)
104
+ .all(...params);
105
+ return rows.map((row) => ({
106
+ id: row.id,
107
+ docName: row.doc_name,
108
+ collection: row.collection,
109
+ heading: row.heading,
110
+ text: row.text,
111
+ index: row.idx,
112
+ score: Number(row.score.toFixed(3)),
113
+ }));
114
+ }
115
+ stats() {
116
+ const total = Number(this.#db.prepare("SELECT COUNT(*) AS n FROM knowledge_chunks").get().n);
117
+ const collections = {};
118
+ for (const row of this.#db
119
+ .prepare("SELECT collection, COUNT(*) AS n FROM knowledge_chunks GROUP BY collection")
120
+ .all()) {
121
+ collections[row.collection] = Number(row.n);
122
+ }
123
+ return { chunks: total, collections };
124
+ }
125
+ }
126
+ //# sourceMappingURL=knowledge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knowledge.js","sourceRoot":"","sources":["../src/knowledge.ts"],"names":[],"mappings":"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"}
package/dist/main.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function main(): Promise<void>;
package/dist/main.js ADDED
@@ -0,0 +1,22 @@
1
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2
+ import { loadConfig } from "./config.js";
3
+ import { createServer } from "./server.js";
4
+ export async function main() {
5
+ const config = loadConfig();
6
+ const { server, close } = createServer(config);
7
+ const shutdown = async () => {
8
+ try {
9
+ await server.close();
10
+ }
11
+ finally {
12
+ close();
13
+ }
14
+ process.exit(0);
15
+ };
16
+ process.on("SIGINT", shutdown);
17
+ process.on("SIGTERM", shutdown);
18
+ await server.connect(new StdioServerTransport());
19
+ // stdout carries the protocol; anything human-readable must go to stderr.
20
+ process.stderr.write(`fushiguro-mcp ready — base: ${config.baseCatalogDir}, custom: ${config.customCatalogDir}, memory: ${config.dbPath}\n`);
21
+ }
22
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAE/C,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;gBAAS,CAAC;YACT,KAAK,EAAE,CAAC;QACV,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAEhC,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;IACjD,0EAA0E;IAC1E,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,+BAA+B,MAAM,CAAC,cAAc,aAAa,MAAM,CAAC,gBAAgB,aAAa,MAAM,CAAC,MAAM,IAAI,CACvH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,85 @@
1
+ import type { Memory, MemoryKind, Outcome } from "./types.js";
2
+ export interface RememberInput {
3
+ text: string;
4
+ kind: MemoryKind;
5
+ scope: string;
6
+ agent: string | null;
7
+ why?: string | null;
8
+ confidence?: number;
9
+ source?: string | null;
10
+ }
11
+ export interface RememberResult {
12
+ id: number;
13
+ action: "created" | "reinforced" | "superseded";
14
+ /** Set when this write retired an older memory. */
15
+ supersededId?: number;
16
+ memory: Memory;
17
+ }
18
+ export interface RecallOptions {
19
+ query?: string;
20
+ /** Include memories for this specialist plus the agent-agnostic ones. */
21
+ agent?: string | null;
22
+ /** Scopes to search. Later entries do not outrank earlier ones. */
23
+ scopes?: string[];
24
+ limit?: number;
25
+ }
26
+ export declare class MemoryStore {
27
+ #private;
28
+ constructor(dbPath: string);
29
+ close(): void;
30
+ /**
31
+ * Writes a preference, folding it into an existing one when it says the same
32
+ * thing. Restating a preference reinforces it; a constraint or correction that
33
+ * restates a softer memory retires that memory instead of duplicating it.
34
+ */
35
+ remember(input: RememberInput): RememberResult;
36
+ /**
37
+ * Ranked recall. Relevance comes from BM25 when there is a query, then gets
38
+ * scaled by kind weight and confidence so a hard constraint beats a passing
39
+ * remark even when the remark matches the words better.
40
+ *
41
+ * Constraints and corrections are also pulled in unconditionally, whether or
42
+ * not they match the query. A rule like "never use purple" that only surfaces
43
+ * when the user happens to say "purple" is not a rule — it has to be in front
44
+ * of the agent on the task where it would otherwise be broken.
45
+ */
46
+ recall(options?: RecallOptions): Memory[];
47
+ /** Marks memories as used, so `memory_stats` can show what actually earns its keep. */
48
+ markUsed(ids: number[]): void;
49
+ get(id: number): Memory | null;
50
+ forget(id: number): boolean;
51
+ list(options?: {
52
+ agent?: string | null;
53
+ scope?: string;
54
+ limit?: number;
55
+ }): Memory[];
56
+ /**
57
+ * Records how a routing decision landed and nudges the term→agent affinity
58
+ * that the router consults next time. This is the loop that makes routing
59
+ * improve with use rather than staying frozen at whatever the keywords say.
60
+ */
61
+ recordOutcome(input: {
62
+ task: string;
63
+ agent: string;
64
+ outcome: Outcome;
65
+ notes?: string | null;
66
+ correctAgent?: string | null;
67
+ }): {
68
+ decisionId: number;
69
+ adjusted: number;
70
+ };
71
+ /** Learned per-agent weight for the given terms, summed. */
72
+ affinity(terms: string[]): Map<string, number>;
73
+ stats(): {
74
+ total: number;
75
+ byKind: Record<string, number>;
76
+ byAgent: Record<string, number>;
77
+ superseded: number;
78
+ decisions: number;
79
+ topUsed: {
80
+ id: number;
81
+ text: string;
82
+ hits: number;
83
+ }[];
84
+ };
85
+ }