Memory: parse quoted qmd command

This commit is contained in:
Benjamin Jesuiter
2026-02-02 18:57:16 +01:00
committed by Vignesh
parent 11a968f5c3
commit 3e82cbd55b
3 changed files with 81 additions and 1 deletions

View File

@@ -30,6 +30,20 @@ describe("resolveMemoryBackendConfig", () => {
expect(resolved.qmd?.update.intervalMs).toBeGreaterThan(0);
});
it("parses quoted qmd command paths", () => {
const cfg = {
agents: { defaults: { workspace: "/tmp/memory-test" } },
memory: {
backend: "qmd",
qmd: {
command: '"/Applications/QMD Tools/qmd" --flag',
},
},
} as MoltbotConfig;
const resolved = resolveMemoryBackendConfig({ cfg, agentId: "main" });
expect(resolved.qmd?.command).toBe("/Applications/QMD Tools/qmd");
});
it("resolves custom paths relative to workspace", () => {
const cfg = {
agents: {

View File

@@ -11,6 +11,7 @@ import type {
} from "../config/types.memory.js";
import type { SessionSendPolicyConfig } from "../config/types.base.js";
import { resolveUserPath } from "../utils.js";
import { splitShellArgs } from "../utils/shell-argv.js";
export type ResolvedMemoryBackendConfig = {
backend: MemoryBackend;
@@ -232,8 +233,11 @@ export function resolveMemoryBackendConfig(params: {
...resolveCustomPaths(qmdCfg?.paths, workspaceDir, nameSet),
];
const rawCommand = qmdCfg?.command?.trim() || "qmd";
const parsedCommand = splitShellArgs(rawCommand);
const command = parsedCommand?.[0] || rawCommand.split(/\s+/)[0] || "qmd";
const resolved: ResolvedQmdConfig = {
command: (qmdCfg?.command?.trim() || "qmd").split(/\s+/)[0] || "qmd",
command,
collections,
includeDefaultMemory,
sessions: resolveSessionConfig(qmdCfg?.sessions, workspaceDir),

62
src/utils/shell-argv.ts Normal file
View File

@@ -0,0 +1,62 @@
export function splitShellArgs(raw: string): string[] | null {
const tokens: string[] = [];
let buf = "";
let inSingle = false;
let inDouble = false;
let escaped = false;
const pushToken = () => {
if (buf.length > 0) {
tokens.push(buf);
buf = "";
}
};
for (let i = 0; i < raw.length; i += 1) {
const ch = raw[i];
if (escaped) {
buf += ch;
escaped = false;
continue;
}
if (!inSingle && !inDouble && ch === "\\") {
escaped = true;
continue;
}
if (inSingle) {
if (ch === "'") {
inSingle = false;
} else {
buf += ch;
}
continue;
}
if (inDouble) {
if (ch === '"') {
inDouble = false;
} else {
buf += ch;
}
continue;
}
if (ch === "'") {
inSingle = true;
continue;
}
if (ch === '"') {
inDouble = true;
continue;
}
if (/\s/.test(ch)) {
pushToken();
continue;
}
buf += ch;
}
if (escaped || inSingle || inDouble) {
return null;
}
pushToken();
return tokens;
}