@polderlabs/bizar 4.0.0 → 4.2.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.
- package/README.md +11 -14
- package/bizar-dash/CHANGELOG.md +1 -1
- package/bizar-dash/src/server/api.mjs +2 -2
- package/bizar-dash/src/server/artifacts-store.mjs +4 -4
- package/bizar-dash/src/server/memory-git.mjs +80 -0
- package/bizar-dash/src/server/memory-lightrag.mjs +767 -0
- package/bizar-dash/src/server/memory-store.mjs +47 -0
- package/bizar-dash/src/server/mod-security.mjs +2 -2
- package/bizar-dash/src/server/routes/memory.mjs +174 -15
- package/bizar-dash/src/server/server.mjs +1 -1
- package/bizar-dash/src/server/state.mjs +2 -2
- package/bizar-dash/src/web/views/Config.tsx +461 -1
- package/bizar-dash/tests/memory-cli.test.mjs +542 -0
- package/bizar-dash/tests/memory-config.test.mjs +422 -0
- package/bizar-dash/tests/memory-git.test.mjs +109 -1
- package/bizar-dash/tests/memory-lightrag.test.mjs +153 -0
- package/bizar-dash/tests/memory-protocol-drift.test.mjs +45 -0
- package/cli/banner.mjs +1 -1
- package/cli/bin.mjs +4 -4
- package/cli/bootstrap.mjs +1 -1
- package/cli/copy.mjs +22 -16
- package/cli/doctor.mjs +4 -4
- package/cli/doctor.test.mjs +2 -2
- package/cli/init.mjs +2 -2
- package/cli/install.mjs +21 -16
- package/cli/memory.mjs +710 -31
- package/cli/utils.mjs +6 -3
- package/config/AGENTS.md +7 -7
- package/config/agents/_shared/AGENT_BASELINE.md +59 -61
- package/config/opencode.json +13 -38
- package/config/skills/memory-protocol/SKILL.md +105 -0
- package/config/skills/obsidian/SKILL.md +58 -1
- package/install.sh +11 -1
- package/package.json +2 -2
- package/plugins/bizar/index.ts +7 -0
- package/plugins/bizar/src/commands.ts +42 -1
- package/plugins/bizar/src/tools/open-kb.ts +191 -0
- package/plugins/bizar/tests/commands.test.ts +36 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polderlabs/bizar",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"description": "Norse-pantheon multi-agent system for opencode — 13 agents across 4 cost tiers with cost-aware routing, plans, and a configurable agent harness. v4 ships as a single npm package bundling the dashboard server, opencode plugin, and typed SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"build:sdk": "tsc -p packages/sdk/tsconfig.json",
|
|
22
22
|
"test:sdk": "npm test --prefix packages/sdk",
|
|
23
23
|
"test:sdk:watch": "npm run test:watch --prefix packages/sdk",
|
|
24
|
-
"test": "npm run typecheck && npm run test:sdk && bun test plugins/bizar/tests/loop.test.ts plugins/bizar/tests/block.test.ts plugins/bizar/tests/stall-think.test.ts plugins/bizar/tests/tools/bg-get-comments.test.ts plugins/bizar/tests/tools/bg-spawn-delegation.test.ts plugins/bizar/tests/tools/opencode-runner.test.ts plugins/bizar/tests/settings.test.ts plugins/bizar/tests/commands.test.ts plugins/bizar/tests/commands-impl.test.ts plugins/bizar/tests/tools/plan-action.test.ts plugins/bizar/tests/tools/wait-for-feedback.test.ts plugins/bizar/tests/tools/read-glyph-feedback.test.ts plugins/bizar/tests/reasoning-clean.test.ts plugins/bizar/tests/key-rotation.test.ts && node bizar-dash/tests/smoke-v2.mjs && node --test bizar-dash/tests/path-safe.test.mjs bizar-dash/tests/tmux-wrap.test.mjs bizar-dash/tests/opencode-runner.test.mjs bizar-dash/tests/mod-instructions.node.test.mjs bizar-dash/tests/mod-upgrade.node.test.mjs bizar-dash/tests/graphify-mod-spawn.node.test.mjs bizar-dash/tests/no-agent-browser.node.test.mjs bizar-dash/tests/providers-store-backup-keys.node.test.mjs bizar-dash/tests/dashboard-ports.test.mjs bizar-dash/tests/submit-feedback.test.mjs bizar-dash/tests/yaml.test.mjs bizar-dash/tests/memory-store.test.mjs bizar-dash/tests/memory-schema.test.mjs bizar-dash/tests/memory-secrets.test.mjs bizar-dash/tests/memory-git.test.mjs bizar-dash/tests/memory-sync.test.mjs bizar-dash/tests/obsidian-back-compat.test.mjs",
|
|
24
|
+
"test": "npm run typecheck && npm run test:sdk && bun test plugins/bizar/tests/loop.test.ts plugins/bizar/tests/block.test.ts plugins/bizar/tests/stall-think.test.ts plugins/bizar/tests/tools/bg-get-comments.test.ts plugins/bizar/tests/tools/bg-spawn-delegation.test.ts plugins/bizar/tests/tools/opencode-runner.test.ts plugins/bizar/tests/settings.test.ts plugins/bizar/tests/commands.test.ts plugins/bizar/tests/commands-impl.test.ts plugins/bizar/tests/tools/plan-action.test.ts plugins/bizar/tests/tools/wait-for-feedback.test.ts plugins/bizar/tests/tools/read-glyph-feedback.test.ts plugins/bizar/tests/reasoning-clean.test.ts plugins/bizar/tests/key-rotation.test.ts && node bizar-dash/tests/smoke-v2.mjs && node --test bizar-dash/tests/path-safe.test.mjs bizar-dash/tests/tmux-wrap.test.mjs bizar-dash/tests/opencode-runner.test.mjs bizar-dash/tests/mod-instructions.node.test.mjs bizar-dash/tests/mod-upgrade.node.test.mjs bizar-dash/tests/graphify-mod-spawn.node.test.mjs bizar-dash/tests/no-agent-browser.node.test.mjs bizar-dash/tests/providers-store-backup-keys.node.test.mjs bizar-dash/tests/dashboard-ports.test.mjs bizar-dash/tests/submit-feedback.test.mjs bizar-dash/tests/yaml.test.mjs bizar-dash/tests/memory-store.test.mjs bizar-dash/tests/memory-schema.test.mjs bizar-dash/tests/memory-secrets.test.mjs bizar-dash/tests/memory-git.test.mjs bizar-dash/tests/memory-sync.test.mjs bizar-dash/tests/obsidian-back-compat.test.mjs bizar-dash/tests/memory-lightrag.test.mjs bizar-dash/tests/memory-config.test.mjs bizar-dash/tests/memory-cli.test.mjs",
|
|
25
25
|
"build": "npm run build:sdk"
|
|
26
26
|
},
|
|
27
27
|
"keywords": [
|
package/plugins/bizar/index.ts
CHANGED
|
@@ -120,6 +120,7 @@ import { createBgStatusTool } from "./src/tools/bg-status.js";
|
|
|
120
120
|
import { createBgCollectTool } from "./src/tools/bg-collect.js";
|
|
121
121
|
import { createBgKillTool } from "./src/tools/bg-kill.js";
|
|
122
122
|
import { createBgGetCommentsTool } from "./src/tools/bg-get-comments.js";
|
|
123
|
+
import { createOpenKbTool } from "./src/tools/open-kb.js";
|
|
123
124
|
|
|
124
125
|
// v0.4.0 — visual plan flow: settings, slash commands, plan tools
|
|
125
126
|
import { SettingsStore } from "./src/settings.js";
|
|
@@ -941,6 +942,12 @@ function buildHooks(ctx: RuntimeContext, bg: BgDeps): Hooks {
|
|
|
941
942
|
worktree: ctx.worktree,
|
|
942
943
|
logger: ctx.logger,
|
|
943
944
|
}),
|
|
945
|
+
// v4.0.0 — open the Bizar Memory vault in Obsidian. Available to
|
|
946
|
+
// all agents; no serve child required.
|
|
947
|
+
bizar_open_kb: createOpenKbTool({
|
|
948
|
+
worktree: ctx.worktree,
|
|
949
|
+
logger: ctx.logger,
|
|
950
|
+
}),
|
|
944
951
|
};
|
|
945
952
|
const tools = bg.instanceManager
|
|
946
953
|
? {
|
|
@@ -265,6 +265,7 @@ function parseFlags(tokens: string[]): ParsedFlags {
|
|
|
265
265
|
* - `/plan comments <slug> [id]` — tool_invocation: bizar_get_plan_comments
|
|
266
266
|
* - `/plan wait <slug> [--timeout N]`
|
|
267
267
|
* — deferred (returns response; no tool call)
|
|
268
|
+
* - `/kb` — open the Bizar Memory vault in Obsidian
|
|
268
269
|
* - `/help` / `/commands` — help text
|
|
269
270
|
*/
|
|
270
271
|
export function parseSlashCommand(
|
|
@@ -293,6 +294,8 @@ export function parseSlashCommand(
|
|
|
293
294
|
return handlePlan(rest, ctx);
|
|
294
295
|
case "bizar":
|
|
295
296
|
return handleBizar(rest, ctx);
|
|
297
|
+
case "kb":
|
|
298
|
+
return handleKb(rest, ctx);
|
|
296
299
|
case "help":
|
|
297
300
|
case "commands":
|
|
298
301
|
return helpResult();
|
|
@@ -1090,12 +1093,49 @@ function handleBizar(arg: string, ctx: ParseContext): SlashCommandResult {
|
|
|
1090
1093
|
};
|
|
1091
1094
|
}
|
|
1092
1095
|
|
|
1096
|
+
// --- /kb -----------------------------------------------------------------
|
|
1097
|
+
|
|
1098
|
+
/**
|
|
1099
|
+
* v4.0.0 — `/kb` opens the Bizar Memory vault in Obsidian.
|
|
1100
|
+
*
|
|
1101
|
+
* Behavior:
|
|
1102
|
+
* - Routes through the `bizar_open_kb` tool via tool_invocation.
|
|
1103
|
+
* - The tool resolves the vault path from .bizar/memory.json and
|
|
1104
|
+
* spawns Obsidian (or xdg-open as fallback).
|
|
1105
|
+
* - If memory isn't initialized, the tool returns a helpful message.
|
|
1106
|
+
*
|
|
1107
|
+
* No subcommands. With args, returns a help pointer.
|
|
1108
|
+
*/
|
|
1109
|
+
function handleKb(arg: string, _ctx: ParseContext): SlashCommandResult {
|
|
1110
|
+
const trimmed = arg.trim();
|
|
1111
|
+
|
|
1112
|
+
if (trimmed !== "") {
|
|
1113
|
+
return {
|
|
1114
|
+
handled: true,
|
|
1115
|
+
response:
|
|
1116
|
+
`Unknown argument: \`${trimmed}\`. \`/kb\` takes no arguments.\n\n` +
|
|
1117
|
+
`Use \`/kb\` to open the Bizar Memory vault in Obsidian.\n` +
|
|
1118
|
+
`Use \`bizar memory status\` to see vault details.`,
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
return {
|
|
1123
|
+
handled: true,
|
|
1124
|
+
response: "Opening Bizar Memory vault in Obsidian…",
|
|
1125
|
+
sideEffect: {
|
|
1126
|
+
kind: "tool_invocation",
|
|
1127
|
+
toolName: "bizar_open_kb",
|
|
1128
|
+
args: {},
|
|
1129
|
+
},
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1093
1133
|
// --- /help ----------------------------------------------------------------
|
|
1094
1134
|
|
|
1095
1135
|
function helpResult(): SlashCommandResult {
|
|
1096
1136
|
return {
|
|
1097
1137
|
handled: true,
|
|
1098
|
-
response: "Available commands: /visual-plan [on|off|status], /plan new <slug> [template], /plan list, /plan open <slug>, /plan get <slug>, /plan add <slug>, /plan update <slug> <id>, /plan delete <slug> <id>, /plan comment <slug> [id] \"text\", /plan comments <slug> [id], /plan status <slug> <status>, /plan wait <slug> [--timeout N], /bizar, /bizar <args>, /help. See the dialog for full descriptions.",
|
|
1138
|
+
response: "Available commands: /visual-plan [on|off|status], /plan new <slug> [template], /plan list, /plan open <slug>, /plan get <slug>, /plan add <slug>, /plan update <slug> <id>, /plan delete <slug> <id>, /plan comment <slug> [id] \"text\", /plan comments <slug> [id], /plan status <slug> <status>, /plan wait <slug> [--timeout N], /kb, /bizar, /bizar <args>, /help. See the dialog for full descriptions.",
|
|
1099
1139
|
dialog: {
|
|
1100
1140
|
id: generateId(),
|
|
1101
1141
|
title: "Bizar Commands",
|
|
@@ -1117,6 +1157,7 @@ function helpResult(): SlashCommandResult {
|
|
|
1117
1157
|
{ cmd: "/plan wait <slug> [--timeout N]", desc: "Wait for feedback (deferred)" },
|
|
1118
1158
|
{ cmd: "/bizar", desc: "Launch the Bizar dashboard" },
|
|
1119
1159
|
{ cmd: "/bizar <args>", desc: "Route a request via the menu" },
|
|
1160
|
+
{ cmd: "/kb", desc: "Open the Bizar Memory vault in Obsidian" },
|
|
1120
1161
|
{ cmd: "/help | /commands", desc: "Show this help" },
|
|
1121
1162
|
],
|
|
1122
1163
|
templates: [...KNOWN_TEMPLATES],
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* open-kb.ts
|
|
3
|
+
*
|
|
4
|
+
* v4.0.0 — `bizar_open_kb` tool.
|
|
5
|
+
*
|
|
6
|
+
* Resolves the project's Bizar Memory vault from `.bizar/memory.json`
|
|
7
|
+
* and opens it in Obsidian. Falls back to printing the vault path if
|
|
8
|
+
* Obsidian isn't installed or running headlessly.
|
|
9
|
+
*
|
|
10
|
+
* Behavior:
|
|
11
|
+
* - Reads .bizar/memory.json from worktree
|
|
12
|
+
* - Calls resolveVault() to determine vault path
|
|
13
|
+
* - Spawns `obsidian <vaultPath>` as a detached child
|
|
14
|
+
* - On ENOENT, tries `xdg-open "obsidian://open?path=<vaultPath>"`
|
|
15
|
+
* - On no display server, prints the path so the user can `cd` to it
|
|
16
|
+
*
|
|
17
|
+
* No args. Returns a JSON string with { ok, vaultPath, opened, method }.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { tool } from "@opencode-ai/plugin";
|
|
21
|
+
import { z } from "zod";
|
|
22
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
import { homedir } from "node:os";
|
|
25
|
+
import { spawn } from "node:child_process";
|
|
26
|
+
|
|
27
|
+
import type { Logger } from "../logger.js";
|
|
28
|
+
|
|
29
|
+
export interface OpenKbDeps {
|
|
30
|
+
worktree: string;
|
|
31
|
+
logger: Logger;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface MemoryJson {
|
|
35
|
+
version?: number;
|
|
36
|
+
backend?: string;
|
|
37
|
+
projectId?: string;
|
|
38
|
+
memoryRepo?: {
|
|
39
|
+
mode?: string;
|
|
40
|
+
path?: string;
|
|
41
|
+
remote?: string | null;
|
|
42
|
+
branch?: string;
|
|
43
|
+
namespace?: string | null;
|
|
44
|
+
};
|
|
45
|
+
namespaces?: {
|
|
46
|
+
project?: string;
|
|
47
|
+
global?: string;
|
|
48
|
+
user?: string;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function loadMemoryJson(worktree: string): MemoryJson | null {
|
|
53
|
+
const p = join(worktree, ".bizar", "memory.json");
|
|
54
|
+
if (!existsSync(p)) return null;
|
|
55
|
+
try {
|
|
56
|
+
return JSON.parse(readFileSync(p, "utf8"));
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function resolveVaultPath(worktree: string, cfg: MemoryJson | null): string | null {
|
|
63
|
+
if (!cfg) return null;
|
|
64
|
+
const mr = cfg.memoryRepo || {};
|
|
65
|
+
const mode = mr.mode || "local-only";
|
|
66
|
+
const projectId = cfg.projectId || worktree.split("/").pop() || "unknown";
|
|
67
|
+
|
|
68
|
+
if (mode === "local-only") {
|
|
69
|
+
return join(worktree, ".obsidian");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// managed or linked
|
|
73
|
+
const rawPath = mr.path || "";
|
|
74
|
+
if (!rawPath) return null;
|
|
75
|
+
const expanded = rawPath.startsWith("~")
|
|
76
|
+
? join(homedir(), rawPath.slice(1))
|
|
77
|
+
: rawPath;
|
|
78
|
+
return join(expanded, "projects", projectId);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function createOpenKbTool(deps: OpenKbDeps) {
|
|
82
|
+
return tool({
|
|
83
|
+
description:
|
|
84
|
+
"Open the Bizar Memory vault in Obsidian. Resolves the vault path " +
|
|
85
|
+
"from .bizar/memory.json and spawns Obsidian with the path. " +
|
|
86
|
+
"If Obsidian isn't installed, prints the vault path so the user " +
|
|
87
|
+
"can open it manually. Available to all agents.",
|
|
88
|
+
args: {},
|
|
89
|
+
execute: async () => {
|
|
90
|
+
const cfg = loadMemoryJson(deps.worktree);
|
|
91
|
+
|
|
92
|
+
if (!cfg) {
|
|
93
|
+
return {
|
|
94
|
+
output: JSON.stringify({
|
|
95
|
+
ok: false,
|
|
96
|
+
error: "memory_not_initialized",
|
|
97
|
+
message:
|
|
98
|
+
"Run `bizar memory init` first to set up the Bizar Memory vault.",
|
|
99
|
+
}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const vaultPath = resolveVaultPath(deps.worktree, cfg);
|
|
104
|
+
|
|
105
|
+
if (!vaultPath) {
|
|
106
|
+
return {
|
|
107
|
+
output: JSON.stringify({
|
|
108
|
+
ok: false,
|
|
109
|
+
error: "vault_path_unresolved",
|
|
110
|
+
message: "Could not resolve vault path from .bizar/memory.json",
|
|
111
|
+
config: cfg,
|
|
112
|
+
}),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!existsSync(vaultPath)) {
|
|
117
|
+
return {
|
|
118
|
+
output: JSON.stringify({
|
|
119
|
+
ok: false,
|
|
120
|
+
error: "vault_not_found",
|
|
121
|
+
vaultPath,
|
|
122
|
+
message: `Vault directory does not exist at ${vaultPath}. Run \`bizar memory init\` to create it.`,
|
|
123
|
+
}),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Try `obsidian <path>` first
|
|
128
|
+
try {
|
|
129
|
+
const child = spawn("obsidian", [vaultPath], {
|
|
130
|
+
detached: true,
|
|
131
|
+
stdio: "ignore",
|
|
132
|
+
});
|
|
133
|
+
child.on("error", () => {
|
|
134
|
+
// Fall through to xdg-open attempt
|
|
135
|
+
});
|
|
136
|
+
child.unref();
|
|
137
|
+
|
|
138
|
+
// Give it a beat — if spawn succeeded, assume success
|
|
139
|
+
return {
|
|
140
|
+
output: JSON.stringify({
|
|
141
|
+
ok: true,
|
|
142
|
+
vaultPath,
|
|
143
|
+
opened: true,
|
|
144
|
+
method: "obsidian",
|
|
145
|
+
message: `Opened vault in Obsidian at ${vaultPath}`,
|
|
146
|
+
}),
|
|
147
|
+
};
|
|
148
|
+
} catch (err) {
|
|
149
|
+
deps.logger.warn(
|
|
150
|
+
`bizar: open-kb: obsidian spawn failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
151
|
+
);
|
|
152
|
+
// fall through to xdg-open
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Fallback: xdg-open with obsidian:// URI
|
|
156
|
+
try {
|
|
157
|
+
const child = spawn(
|
|
158
|
+
"xdg-open",
|
|
159
|
+
[`obsidian://open?path=${encodeURIComponent(vaultPath)}`],
|
|
160
|
+
{ detached: true, stdio: "ignore" },
|
|
161
|
+
);
|
|
162
|
+
child.on("error", (err) => {
|
|
163
|
+
deps.logger.warn(`bizar: open-kb: xdg-open failed: ${err.message}`);
|
|
164
|
+
});
|
|
165
|
+
child.unref();
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
output: JSON.stringify({
|
|
169
|
+
ok: true,
|
|
170
|
+
vaultPath,
|
|
171
|
+
opened: true,
|
|
172
|
+
method: "xdg-open",
|
|
173
|
+
message: `Opened vault via xdg-open at ${vaultPath}`,
|
|
174
|
+
}),
|
|
175
|
+
};
|
|
176
|
+
} catch (err) {
|
|
177
|
+
// Last resort: just print the path
|
|
178
|
+
return {
|
|
179
|
+
output: JSON.stringify({
|
|
180
|
+
ok: false,
|
|
181
|
+
error: "no_obsidian",
|
|
182
|
+
vaultPath,
|
|
183
|
+
message:
|
|
184
|
+
`Could not launch Obsidian. Vault path: ${vaultPath}\n` +
|
|
185
|
+
`Install Obsidian (https://obsidian.md) and try again, or open the vault manually with \`obsidian ${vaultPath}\`.`,
|
|
186
|
+
}),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
}
|
|
@@ -545,4 +545,40 @@ describe("parseSlashCommand — /help includes new subcommands", () => {
|
|
|
545
545
|
expect(text).toContain(cmd);
|
|
546
546
|
}
|
|
547
547
|
});
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
// ===========================================================================
|
|
551
|
+
// Group 11 — v4.0.0 /kb
|
|
552
|
+
// ===========================================================================
|
|
553
|
+
|
|
554
|
+
describe("parseSlashCommand — /kb", () => {
|
|
555
|
+
test("parses '/kb' slash command", () => {
|
|
556
|
+
const result = parseSlashCommand("/kb", baseCtx);
|
|
557
|
+
expect(result).not.toBeNull();
|
|
558
|
+
expect(result!.handled).toBe(true);
|
|
559
|
+
expect(result!.sideEffect).toEqual({
|
|
560
|
+
kind: "tool_invocation",
|
|
561
|
+
toolName: "bizar_open_kb",
|
|
562
|
+
args: {},
|
|
563
|
+
});
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
test("'/kb' with args returns help", () => {
|
|
567
|
+
const result = parseSlashCommand("/kb foo", baseCtx);
|
|
568
|
+
expect(result).not.toBeNull();
|
|
569
|
+
expect(result!.handled).toBe(true);
|
|
570
|
+
expect(result!.sideEffect).toBeUndefined();
|
|
571
|
+
expect(result!.response).toContain("Unknown argument");
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
test("'/kb' case-insensitive", () => {
|
|
575
|
+
const r = parseSlashCommand("/KB", baseCtx);
|
|
576
|
+
expect(r).not.toBeNull();
|
|
577
|
+
expect(r!.handled).toBe(true);
|
|
578
|
+
expect(r!.sideEffect).toEqual({
|
|
579
|
+
kind: "tool_invocation",
|
|
580
|
+
toolName: "bizar_open_kb",
|
|
581
|
+
args: {},
|
|
582
|
+
});
|
|
583
|
+
});
|
|
548
584
|
});
|