feat: add per-group workDir for agent working directory override
Agents can now run in a specified project directory instead of the group folder. The group's CLAUDE.md is still loaded via additionalDirectories.
This commit is contained in:
@@ -227,14 +227,11 @@ export class DiscordChannel implements Channel {
|
||||
|
||||
// Convert @username mentions to Discord mention format
|
||||
const mentionMap: Record<string, string> = {
|
||||
'눈쟁이': '216851709744513024',
|
||||
눈쟁이: '216851709744513024',
|
||||
};
|
||||
let resolved = text;
|
||||
for (const [name, id] of Object.entries(mentionMap)) {
|
||||
resolved = resolved.replace(
|
||||
new RegExp(`@${name}`, 'g'),
|
||||
`<@${id}>`,
|
||||
);
|
||||
resolved = resolved.replace(new RegExp(`@${name}`, 'g'), `<@${id}>`);
|
||||
}
|
||||
|
||||
// Discord has a 2000 character limit per message — split if needed
|
||||
|
||||
@@ -155,6 +155,8 @@ function prepareGroupEnvironment(
|
||||
NANOCLAW_IPC_DIR: groupIpcDir,
|
||||
NANOCLAW_GLOBAL_DIR: globalDir,
|
||||
NANOCLAW_EXTRA_DIR: extraDirs.length > 0 ? extraDirs[0] : '',
|
||||
// Working directory override (agent uses this as cwd instead of group dir)
|
||||
...(group.workDir ? { NANOCLAW_WORK_DIR: group.workDir } : {}),
|
||||
// MCP server context
|
||||
NANOCLAW_CHAT_JID: group.folder,
|
||||
NANOCLAW_GROUP_FOLDER: group.folder,
|
||||
|
||||
18
src/db.ts
18
src/db.ts
@@ -128,6 +128,15 @@ function createSchema(database: Database.Database): void {
|
||||
/* column already exists */
|
||||
}
|
||||
|
||||
// Add work_dir column if it doesn't exist (migration for per-group working directory)
|
||||
try {
|
||||
database.exec(
|
||||
`ALTER TABLE registered_groups ADD COLUMN work_dir TEXT`,
|
||||
);
|
||||
} catch {
|
||||
/* column already exists */
|
||||
}
|
||||
|
||||
// Add channel and is_group columns if they don't exist (migration for existing DBs)
|
||||
try {
|
||||
database.exec(`ALTER TABLE chats ADD COLUMN channel TEXT`);
|
||||
@@ -576,6 +585,7 @@ export function getRegisteredGroup(
|
||||
requires_trigger: number | null;
|
||||
is_main: number | null;
|
||||
agent_type: string | null;
|
||||
work_dir: string | null;
|
||||
}
|
||||
| undefined;
|
||||
if (!row) return undefined;
|
||||
@@ -599,6 +609,7 @@ export function getRegisteredGroup(
|
||||
row.requires_trigger === null ? undefined : row.requires_trigger === 1,
|
||||
isMain: row.is_main === 1 ? true : undefined,
|
||||
agentType: (row.agent_type as RegisteredGroup['agentType']) || undefined,
|
||||
workDir: row.work_dir || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -607,8 +618,8 @@ export function setRegisteredGroup(jid: string, group: RegisteredGroup): void {
|
||||
throw new Error(`Invalid group folder "${group.folder}" for JID ${jid}`);
|
||||
}
|
||||
db.prepare(
|
||||
`INSERT OR REPLACE INTO registered_groups (jid, name, folder, trigger_pattern, added_at, container_config, requires_trigger, is_main, agent_type)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
`INSERT OR REPLACE INTO registered_groups (jid, name, folder, trigger_pattern, added_at, container_config, requires_trigger, is_main, agent_type, work_dir)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
).run(
|
||||
jid,
|
||||
group.name,
|
||||
@@ -619,6 +630,7 @@ export function setRegisteredGroup(jid: string, group: RegisteredGroup): void {
|
||||
group.requiresTrigger === undefined ? 1 : group.requiresTrigger ? 1 : 0,
|
||||
group.isMain ? 1 : 0,
|
||||
group.agentType || 'claude-code',
|
||||
group.workDir || null,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -633,6 +645,7 @@ export function getAllRegisteredGroups(): Record<string, RegisteredGroup> {
|
||||
requires_trigger: number | null;
|
||||
is_main: number | null;
|
||||
agent_type: string | null;
|
||||
work_dir: string | null;
|
||||
}>;
|
||||
const result: Record<string, RegisteredGroup> = {};
|
||||
for (const row of rows) {
|
||||
@@ -655,6 +668,7 @@ export function getAllRegisteredGroups(): Record<string, RegisteredGroup> {
|
||||
row.requires_trigger === null ? undefined : row.requires_trigger === 1,
|
||||
isMain: row.is_main === 1 ? true : undefined,
|
||||
agentType: (row.agent_type as RegisteredGroup['agentType']) || undefined,
|
||||
workDir: row.work_dir || undefined,
|
||||
};
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -20,6 +20,7 @@ export interface RegisteredGroup {
|
||||
requiresTrigger?: boolean; // Default: true for groups, false for solo chats
|
||||
isMain?: boolean; // True for the main control group (no trigger, elevated privileges)
|
||||
agentType?: AgentType;
|
||||
workDir?: string; // Working directory for the agent (defaults to group folder)
|
||||
}
|
||||
|
||||
export interface NewMessage {
|
||||
|
||||
Reference in New Issue
Block a user