diff --git a/apps/x/apps/main/src/ipc.ts b/apps/x/apps/main/src/ipc.ts index a2230eda..74388f65 100644 --- a/apps/x/apps/main/src/ipc.ts +++ b/apps/x/apps/main/src/ipc.ts @@ -110,6 +110,18 @@ function markdownToHtml(markdown: string, title: string): string { ${html}` } +function resolveShellPath(filePath: string): string { + if (filePath.startsWith('~')) { + return path.join(os.homedir(), filePath.slice(1)); + } + + if (path.isAbsolute(filePath)) { + return filePath; + } + + return workspace.resolveWorkspacePath(filePath); +} + type InvokeChannels = ipc.InvokeChannels; type IPCChannels = ipc.IPCChannels; @@ -271,7 +283,7 @@ function handleWorkspaceChange(event: z.infer { - let filePath = args.path; - if (filePath.startsWith('~')) { - filePath = path.join(os.homedir(), filePath.slice(1)); - } else if (!path.isAbsolute(filePath)) { - // Workspace-relative path — resolve against ~/.rowboat/ - filePath = path.join(os.homedir(), '.rowboat', filePath); - } + const filePath = resolveShellPath(args.path); const error = await shell.openPath(filePath); return { error: error || undefined }; }, 'shell:readFileBase64': async (_event, args) => { - let filePath = args.path; - if (filePath.startsWith('~')) { - filePath = path.join(os.homedir(), filePath.slice(1)); - } else if (!path.isAbsolute(filePath)) { - // Workspace-relative path — resolve against ~/.rowboat/ - filePath = path.join(os.homedir(), '.rowboat', filePath); - } + const filePath = resolveShellPath(args.path); const stat = await fs.stat(filePath); if (stat.size > 10 * 1024 * 1024) { throw new Error('File too large (>10MB)'); diff --git a/apps/x/apps/main/src/test-agent.ts b/apps/x/apps/main/src/test-agent.ts index 836deea7..738d861a 100644 --- a/apps/x/apps/main/src/test-agent.ts +++ b/apps/x/apps/main/src/test-agent.ts @@ -3,7 +3,7 @@ import { bus } from '@x/core/dist/runs/bus.js'; async function main() { const { id } = await runsCore.createRun({ - // this will expect an agent file to exist at ~/.rowboat/agents/test-agent.md + // this expects an agent file to exist at WorkDir/agents/test-agent.md agentId: 'test-agent', }); console.log(`created run: ${id}`); @@ -16,4 +16,4 @@ async function main() { console.log(`created message: ${msgId}`); } -main(); \ No newline at end of file +main(); diff --git a/apps/x/packages/core/src/application/assistant/instructions.ts b/apps/x/packages/core/src/application/assistant/instructions.ts index 76472c90..022b21e4 100644 --- a/apps/x/packages/core/src/application/assistant/instructions.ts +++ b/apps/x/packages/core/src/application/assistant/instructions.ts @@ -112,10 +112,10 @@ Users can interact with the knowledge graph through you, open it directly in Obs ## How to Access the Knowledge Graph **CRITICAL PATH REQUIREMENT:** -- The workspace root is \`~/.rowboat/\` +- The workspace root is the configured workdir - The knowledge base is in the \`knowledge/\` subfolder - When using workspace tools, ALWAYS include \`knowledge/\` in the path -- **WRONG:** \`workspace-grep({ pattern: "John", path: "" })\` or \`path: "."\` or \`path: "~/.rowboat"\` +- **WRONG:** \`workspace-grep({ pattern: "John", path: "" })\` or \`path: "."\` or any absolute path to the workspace root - **CORRECT:** \`workspace-grep({ pattern: "John", path: "knowledge/" })\` Use the builtin workspace tools to search and read the knowledge base: @@ -212,16 +212,16 @@ For third-party services (GitHub, Gmail, Slack, etc.), load the \`composio-integ ${runtimeContextPrompt} ## Workspace Access & Scope -- **Inside \`~/.rowboat/\`:** Use builtin workspace tools (\`workspace-readFile\`, \`workspace-writeFile\`, etc.). These don't require security approval. -- **Outside \`~/.rowboat/\` (Desktop, Downloads, Documents, etc.):** Use \`executeCommand\` to run shell commands. -- **IMPORTANT:** Do NOT access files outside \`~/.rowboat/\` unless the user explicitly asks you to (e.g., "organize my Desktop", "find a file in Downloads"). +- **Inside the workspace root:** Use builtin workspace tools (\`workspace-readFile\`, \`workspace-writeFile\`, etc.). These don't require security approval. +- **Outside the workspace root (Desktop, Downloads, Documents, etc.):** Use \`executeCommand\` to run shell commands. +- **IMPORTANT:** Do NOT access files outside the workspace root unless the user explicitly asks you to (e.g., "organize my Desktop", "find a file in Downloads"). -**CRITICAL - When the user asks you to work with files outside ~/.rowboat:** +**CRITICAL - When the user asks you to work with files outside the workspace root:** - Follow the detected runtime platform above for shell syntax and filesystem path style. - On macOS/Linux, use POSIX-style commands and paths (e.g., \`~/Desktop\`, \`~/Downloads\`, \`open\` on macOS). - On Windows, use cmd-compatible commands and Windows paths (e.g., \`C:\\Users\\\\Desktop\`). - You CAN access the user's full filesystem via \`executeCommand\` - there is no sandbox restriction on paths. -- NEVER say "I can only run commands inside ~/.rowboat" or "I don't have access to your Desktop" - just use \`executeCommand\`. +- NEVER say "I can only run commands inside the workspace root" or "I don't have access to your Desktop" - just use \`executeCommand\`. - NEVER offer commands for the user to run manually - run them yourself with \`executeCommand\`. - NEVER say "I'll run shell commands equivalent to..." - just describe what you'll do in plain language (e.g., "I'll move 12 screenshots to a new Screenshots folder"). - NEVER ask what OS the user is on if runtime platform is already available. @@ -244,14 +244,14 @@ ${runtimeContextPrompt} - \`save-to-memory\` - Save observations about the user to the agent memory system. Use this proactively during conversations. - \`composio-list-toolkits\`, \`composio-search-tools\`, \`composio-execute-tool\`, \`composio-connect-toolkit\` — Composio integration tools. Load the \`composio-integration\` skill for usage guidance. -**Prefer these tools whenever possible** — they work instantly with zero friction. For file operations inside \`~/.rowboat/\`, always use these instead of \`executeCommand\`. +**Prefer these tools whenever possible** — they work instantly with zero friction. For file operations inside the workspace root, always use these instead of \`executeCommand\`. **Shell commands via \`executeCommand\`:** -- You can run ANY shell command via \`executeCommand\`. Some commands are pre-approved in \`~/.rowboat/config/security.json\` and run immediately. +- You can run ANY shell command via \`executeCommand\`. Some commands are pre-approved in \`config/security.json\` within the workspace root and run immediately. - Commands not on the pre-approved list will trigger a one-time approval prompt for the user — this is fine and expected, just a minor friction. Do NOT let this stop you from running commands you need. - **Never say "I can't run this command"** or ask the user to run something manually. Just call \`executeCommand\` and let the approval flow handle it. - When calling \`executeCommand\`, do NOT provide the \`cwd\` parameter unless absolutely necessary. The default working directory is already set to the workspace root. -- Always confirm with the user before executing commands that modify files outside \`~/.rowboat/\` (e.g., "I'll move 12 screenshots to ~/Desktop/Screenshots. Proceed?"). +- Always confirm with the user before executing commands that modify files outside the workspace root (e.g., "I'll move 12 screenshots to ~/Desktop/Screenshots. Proceed?"). **CRITICAL: MCP Server Configuration** - ALWAYS use the \`addMcpServer\` builtin tool to add or update MCP servers—it validates the configuration before saving diff --git a/apps/x/packages/core/src/application/assistant/skills/background-agents/skill.ts b/apps/x/packages/core/src/application/assistant/skills/background-agents/skill.ts index 7ac1b89e..b8e481b6 100644 --- a/apps/x/packages/core/src/application/assistant/skills/background-agents/skill.ts +++ b/apps/x/packages/core/src/application/assistant/skills/background-agents/skill.ts @@ -11,7 +11,7 @@ Load this skill whenever a user wants to inspect, create, edit, or schedule back - Agents configure a model, tools (in frontmatter), and instructions (in the body) - Tools can be: builtin (like ` + "`executeCommand`" + `), MCP integrations, or **other agents** - **"Workflows" are just agents that orchestrate other agents** by having them as tools -- **Background agents run on schedules** defined in ` + "`~/.rowboat/config/agent-schedule.json`" + ` +- **Background agents run on schedules** defined in ` + "`config/agent-schedule.json`" + ` within the workspace root ## How multi-agent workflows work @@ -22,7 +22,7 @@ Load this skill whenever a user wants to inspect, create, edit, or schedule back ## Scheduling Background Agents -Background agents run automatically based on schedules defined in ` + "`~/.rowboat/config/agent-schedule.json`" + `. +Background agents run automatically based on schedules defined in ` + "`config/agent-schedule.json`" + ` in the workspace root. ### Schedule Configuration File @@ -150,7 +150,7 @@ You can add a ` + "`description`" + ` field to describe what the agent does. Thi **IMPORTANT: Do NOT modify ` + "`agent-schedule-state.json`" + `** - it is managed automatically by the background runner. -The runner automatically tracks execution state in ` + "`~/.rowboat/config/agent-schedule-state.json`" + `: +The runner automatically tracks execution state in ` + "`config/agent-schedule-state.json`" + ` in the workspace root: - ` + "`status`" + `: scheduled, running, finished, failed, triggered (for once-schedules) - ` + "`lastRunAt`" + `: When the agent last ran - ` + "`nextRunAt`" + `: When the agent will run next @@ -410,7 +410,7 @@ Create a morning briefing: Execute these steps in sequence. Don't ask for human input. ` + "```" + ` -**4. Schedule the workflow** in ` + "`~/.rowboat/config/agent-schedule.json`" + `: +**4. Schedule the workflow** in ` + "`config/agent-schedule.json`" + `: ` + "```json" + ` { "agents": { @@ -548,7 +548,7 @@ Use the search tool to find information on the web. 5. When creating multi-agent workflows, create an orchestrator agent 6. Add other agents as tools with ` + "`type: agent`" + ` for chaining 7. Use ` + "`listMcpServers`" + ` and ` + "`listMcpTools`" + ` when adding MCP integrations -8. Configure schedules in ` + "`~/.rowboat/config/agent-schedule.json`" + ` (ONLY edit this file, NOT the state file) +8. Configure schedules in ` + "`config/agent-schedule.json`" + ` (ONLY edit this file, NOT the state file) 9. Confirm work done and outline next steps once changes are complete `; diff --git a/apps/x/packages/core/src/application/assistant/skills/builtin-tools/skill.ts b/apps/x/packages/core/src/application/assistant/skills/builtin-tools/skill.ts index 0113a726..4187584e 100644 --- a/apps/x/packages/core/src/application/assistant/skills/builtin-tools/skill.ts +++ b/apps/x/packages/core/src/application/assistant/skills/builtin-tools/skill.ts @@ -10,7 +10,7 @@ Agents can use builtin tools by declaring them in the YAML frontmatter \`tools\` ### executeCommand **The most powerful and versatile builtin tool** - Execute any bash/shell command and get the output. -**Security note:** Commands are filtered through \`.rowboat/config/security.json\`. Populate this file with allowed command names (array or dictionary entries). Any command not present is blocked and returns exit code 126 so the agent knows it violated the policy. +**Security note:** Commands are filtered through \`config/security.json\` in the workspace root. Populate this file with allowed command names (array or dictionary entries). Any command not present is blocked and returns exit code 126 so the agent knows it violated the policy. **Agent tool declaration (YAML frontmatter):** \`\`\`yaml diff --git a/apps/x/packages/core/src/application/assistant/skills/doc-collab/skill.ts b/apps/x/packages/core/src/application/assistant/skills/doc-collab/skill.ts index 567d43ac..f5f63c17 100644 --- a/apps/x/packages/core/src/application/assistant/skills/doc-collab/skill.ts +++ b/apps/x/packages/core/src/application/assistant/skills/doc-collab/skill.ts @@ -166,7 +166,7 @@ workspace-readFile("knowledge/Projects/[Project].md") ## Document Locations -Documents are stored in \`~/.rowboat/knowledge/\` with subfolders: +Documents are stored in \`knowledge/\` within the workspace root, with subfolders: - \`People/\` - Notes about individuals - \`Organizations/\` - Notes about companies, teams - \`Projects/\` - Project documentation diff --git a/apps/x/packages/core/src/application/assistant/skills/draft-emails/skill.ts b/apps/x/packages/core/src/application/assistant/skills/draft-emails/skill.ts index 4e4322af..208e9560 100644 --- a/apps/x/packages/core/src/application/assistant/skills/draft-emails/skill.ts +++ b/apps/x/packages/core/src/application/assistant/skills/draft-emails/skill.ts @@ -7,7 +7,7 @@ You are helping the user draft email responses. Use their calendar and knowledge **BEFORE drafting any email, you MUST look up the person/organization in the knowledge base.** -**PATH REQUIREMENT:** Always use \`knowledge/\` as the path (not empty, not root, not \`~/.rowboat\`). +**PATH REQUIREMENT:** Always use \`knowledge/\` as the path (not empty, not the workspace root, not an absolute path). - **WRONG:** \`path: ""\` or \`path: "."\` - **CORRECT:** \`path: "knowledge/"\` diff --git a/apps/x/packages/core/src/application/assistant/skills/meeting-prep/skill.ts b/apps/x/packages/core/src/application/assistant/skills/meeting-prep/skill.ts index 3a38e715..44453637 100644 --- a/apps/x/packages/core/src/application/assistant/skills/meeting-prep/skill.ts +++ b/apps/x/packages/core/src/application/assistant/skills/meeting-prep/skill.ts @@ -7,7 +7,7 @@ You are helping the user prepare for meetings by gathering context from their kn **BEFORE creating any meeting brief, you MUST look up the attendees in the knowledge base.** -**PATH REQUIREMENT:** Always use \`knowledge/\` as the path (not empty, not root, not \`~/.rowboat\`). +**PATH REQUIREMENT:** Always use \`knowledge/\` as the path (not empty, not the workspace root, not an absolute path). - **WRONG:** \`path: ""\` or \`path: "."\` - **CORRECT:** \`path: "knowledge/"\` diff --git a/apps/x/packages/core/src/application/assistant/skills/slack/skill.ts b/apps/x/packages/core/src/application/assistant/skills/slack/skill.ts index 66df837d..81bb2562 100644 --- a/apps/x/packages/core/src/application/assistant/skills/slack/skill.ts +++ b/apps/x/packages/core/src/application/assistant/skills/slack/skill.ts @@ -7,7 +7,7 @@ You interact with Slack by running **agent-slack** commands through \`executeCom ## 1. Check Connection -Before any Slack operation, read \`~/.rowboat/config/slack.json\`. If \`enabled\` is \`false\` or the \`workspaces\` array is empty, simply tell the user: "Slack is not enabled. You can enable it in the Connectors settings." Do not attempt any agent-slack commands. +Before any Slack operation, read \`config/slack.json\` from the workspace root. If \`enabled\` is \`false\` or the \`workspaces\` array is empty, simply tell the user: "Slack is not enabled. You can enable it in the Connectors settings." Do not attempt any agent-slack commands. If enabled, use the workspace URLs from the config for all commands. @@ -75,7 +75,7 @@ agent-slack canvas get F01234567 --workspace https://team.slack.com ## 3. Multi-Workspace -**Important:** The user has chosen which workspaces to use. Before your first Slack operation, read \`~/.rowboat/config/slack.json\` to see the selected workspaces. Only interact with workspaces listed in that config — ignore any other authenticated workspaces. +**Important:** The user has chosen which workspaces to use. Before your first Slack operation, read \`config/slack.json\` from the workspace root to see the selected workspaces. Only interact with workspaces listed in that config — ignore any other authenticated workspaces. If the selected workspace list contains multiple entries, use \`--workspace \` to disambiguate: diff --git a/apps/x/packages/core/src/application/lib/builtin-tools.ts b/apps/x/packages/core/src/application/lib/builtin-tools.ts index 404db713..064df87b 100644 --- a/apps/x/packages/core/src/application/lib/builtin-tools.ts +++ b/apps/x/packages/core/src/application/lib/builtin-tools.ts @@ -1092,14 +1092,14 @@ export const BuiltinTools: z.infer = { } catch { return { success: false, - error: 'Exa Search API key not configured. Create ~/.rowboat/config/exa-search.json with { "apiKey": "" }', + error: `Exa Search API key not configured. Create ${exaConfigPath} with { "apiKey": "" }`, }; } if (!apiKey) { return { success: false, - error: 'Exa Search API key is empty. Set "apiKey" in ~/.rowboat/config/exa-search.json', + error: `Exa Search API key is empty. Set "apiKey" in ${exaConfigPath}`, }; } diff --git a/apps/x/packages/core/src/knowledge/README.md b/apps/x/packages/core/src/knowledge/README.md index d8442c80..055a8bf1 100644 --- a/apps/x/packages/core/src/knowledge/README.md +++ b/apps/x/packages/core/src/knowledge/README.md @@ -13,7 +13,7 @@ Main orchestrator that: ### `graph_state.ts` State management module that tracks which files have been processed: - Uses hybrid mtime + hash approach for change detection -- Stores state in `~/.rowboat/knowledge_graph_state.json` +- Stores state in `WorkDir/knowledge_graph_state.json` - Provides modular functions for state operations ### `sync_gmail.ts` & `sync_fireflies.ts` @@ -39,7 +39,7 @@ This is efficient (only hashes potentially changed files) and reliable (confirms ### State File Structure -`~/.rowboat/knowledge_graph_state.json`: +`WorkDir/knowledge_graph_state.json`: ```json { "processedFiles": { @@ -69,7 +69,7 @@ This is efficient (only hashes potentially changed files) and reliable (confirms 3. **Agent processes batch** - Extracts entities (people, orgs, projects, topics) - - Creates/updates notes in `~/.rowboat/knowledge/` + - Creates/updates notes in `WorkDir/knowledge/` - Merges information for entities appearing in multiple files ## Replacing the Change Detection Logic @@ -135,7 +135,7 @@ import { resetGraphState } from './build_graph.js'; resetGraphState(); // Clears the state file ``` -Or manually delete: `~/.rowboat/knowledge_graph_state.json` +Or manually delete: `WorkDir/knowledge_graph_state.json` ## Note Creation Strictness @@ -143,7 +143,7 @@ The system supports three strictness levels that control how aggressively notes ### Configuration -Strictness is configured in `~/.rowboat/config/note_creation.json`: +Strictness is configured in `WorkDir/config/note_creation.json`: ```json { @@ -218,7 +218,7 @@ Each strictness level has its own agent prompt: Change `BATCH_SIZE` in `build_graph.ts` (currently 25 files per batch) ### State File Location -Change `STATE_FILE` in `graph_state.ts` (currently `~/.rowboat/knowledge_graph_state.json`) +Change `STATE_FILE` in `graph_state.ts` (currently `WorkDir/knowledge_graph_state.json`) ### Hash Algorithm Change `crypto.createHash('sha256')` in `graph_state.ts` to use a different algorithm (md5, sha1, etc.) diff --git a/apps/x/packages/core/src/knowledge/note_system.ts b/apps/x/packages/core/src/knowledge/note_system.ts index d167e97c..a62a4e37 100644 --- a/apps/x/packages/core/src/knowledge/note_system.ts +++ b/apps/x/packages/core/src/knowledge/note_system.ts @@ -9,7 +9,7 @@ export interface NoteTypeDefinition { extractionGuide: string; } -// ── Default definitions (used to seed ~/.rowboat/config/notes.json) ────────── +// ── Default definitions (used to seed WorkDir/config/notes.json) ───────────── const DEFAULT_NOTE_TYPE_DEFINITIONS: NoteTypeDefinition[] = [ { diff --git a/apps/x/packages/core/src/knowledge/tag_system.ts b/apps/x/packages/core/src/knowledge/tag_system.ts index 7b46ef4d..e525655a 100644 --- a/apps/x/packages/core/src/knowledge/tag_system.ts +++ b/apps/x/packages/core/src/knowledge/tag_system.ts @@ -26,7 +26,7 @@ export interface TagDefinition { noteEffect?: NoteEffect; } -// ── Default definitions (used to seed ~/.rowboat/config/tags.json) ────────── +// ── Default definitions (used to seed WorkDir/config/tags.json) ───────────── const DEFAULT_TAG_DEFINITIONS: TagDefinition[] = [ // ── Relationship — who is this from/about (all create) ──────────────── diff --git a/apps/x/packages/core/src/voice/voice.ts b/apps/x/packages/core/src/voice/voice.ts index 895c81b9..1cfba03b 100644 --- a/apps/x/packages/core/src/voice/voice.ts +++ b/apps/x/packages/core/src/voice/voice.ts @@ -2,10 +2,9 @@ import * as fs from 'fs/promises'; import * as path from 'path'; import { isSignedIn } from '../account/account.js'; import { getAccessToken } from '../auth/tokens.js'; +import { WorkDir } from '../config/config.js'; import { API_URL } from '../config/env.js'; -const homedir = process.env.HOME || process.env.USERPROFILE || ''; - export interface VoiceConfig { deepgram: { apiKey: string } | null; elevenlabs: { apiKey: string; voiceId?: string } | null; @@ -13,7 +12,7 @@ export interface VoiceConfig { async function readJsonConfig(filename: string): Promise | null> { try { - const configPath = path.join(homedir, '.rowboat', 'config', filename); + const configPath = path.join(WorkDir, 'config', filename); const raw = await fs.readFile(configPath, 'utf8'); return JSON.parse(raw); } catch { @@ -51,7 +50,7 @@ export async function synthesizeSpeech(text: string): Promise<{ audioBase64: str console.log('[voice] synthesizing speech via Rowboat proxy, text length:', text.length, 'voiceId:', voiceId); } else { if (!config.elevenlabs) { - throw new Error('ElevenLabs not configured. Create ~/.rowboat/config/elevenlabs.json with { "apiKey": "" }'); + throw new Error(`ElevenLabs not configured. Create ${path.join(WorkDir, 'config', 'elevenlabs.json')} with { "apiKey": "" }`); } const voiceId = config.elevenlabs.voiceId || 'UgBBYS2sOqTuMpoF3BR0'; url = `https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`; diff --git a/apps/x/packages/core/src/workspace/watcher.ts b/apps/x/packages/core/src/workspace/watcher.ts index 7d59331d..3460f014 100644 --- a/apps/x/packages/core/src/workspace/watcher.ts +++ b/apps/x/packages/core/src/workspace/watcher.ts @@ -10,7 +10,7 @@ export type WorkspaceChangeCallback = (event: z.infer