A module keeping a checkout of a repository of decisions, designs and issues from the git seat, current on every announced merge and on a timer, answering records_search / records_read / records_list / records_status / records_sync at the commit it read (novox/hq ADR 0025, ADR 0153). The repository is a setting; it names no mesh.
63 lines
3.0 KiB
TypeScript
63 lines
3.0 KiB
TypeScript
// records' tools — how the record is asked (novox/hq ADR 0025, ADR 0153).
|
|
//
|
|
// Five questions, each answered from the checkout at the commit it names: where does a phrase
|
|
// appear, what does one document say, what does a folder hold, where does the checkout stand, and
|
|
// bring it up to date now. The reasoning stays in the documents; the tools only find them.
|
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
|
import { recordsFromEnv, type Records } from "../records.js";
|
|
|
|
export function getRecordsTools(records: Records): ToolDefinition[] {
|
|
return [
|
|
{
|
|
name: "records_search",
|
|
description:
|
|
"Where a phrase appears in the decisions, designs and issues, as written: document, line, nearest heading. " +
|
|
"Search the literal words of a symptom or a term before forming a hypothesis; the answer names the commit it was read at.",
|
|
input: {
|
|
query: { type: "string", description: "the phrase, matched case-insensitively as written" },
|
|
limit: { type: "number", description: "at most this many places (default 50)" },
|
|
},
|
|
run: async (args) => records.search(String(args.query ?? ""), args.limit ? Number(args.limit) : undefined),
|
|
},
|
|
{
|
|
name: "records_read",
|
|
description: "One document, whole, by its path in the repository — a decision record, a design document, an issue report.",
|
|
input: { path: { type: "string", description: "the document's path, e.g. 02-DECISIONS/0025-....md" } },
|
|
run: async (args) => records.read(String(args.path ?? "")),
|
|
},
|
|
{
|
|
name: "records_list",
|
|
description: "What a folder of the repository holds: its sub-folders and its documents. The root when no folder is named.",
|
|
input: { folder: { type: "string", description: "a folder inside the repository (optional)" } },
|
|
run: async (args) => records.list(args.folder ? String(args.folder) : ""),
|
|
},
|
|
{
|
|
name: "records_status",
|
|
description: "Where the checkout stands: the repository, the forge it is read from, the commit and its date, when it was last brought up to date.",
|
|
input: {},
|
|
run: async () => records.standing(),
|
|
},
|
|
{
|
|
name: "records_sync",
|
|
description: "Bring the checkout up to date now, and say where it stands.",
|
|
input: {},
|
|
run: async () => {
|
|
await records.sync();
|
|
return records.standing();
|
|
},
|
|
},
|
|
];
|
|
}
|
|
|
|
// The reader is made once, at load, from the environment the runtime resolves; the contributor is
|
|
// synchronous and is called at every collection. Without a repository to read there is nothing to
|
|
// answer, and the module exposes no tools rather than five that fail — the sdk's contract: a
|
|
// contributor returning [] is normal.
|
|
let reader: Records | null = null;
|
|
try {
|
|
reader = await recordsFromEnv();
|
|
} catch (err) {
|
|
console.log(`[records] no tools — ${err instanceof Error ? err.message : String(err)}`);
|
|
}
|
|
registerModuleTools("records", () => (reader ? getRecordsTools(reader) : []));
|