fix workdir everywhere

This commit is contained in:
Arjun 2026-04-08 21:52:21 +05:30
parent 44fd94d5e6
commit ef7bc579a1
15 changed files with 52 additions and 54 deletions

View file

@ -110,6 +110,18 @@ function markdownToHtml(markdown: string, title: string): string {
</style></head><body>${html}</body></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<typeof workspaceShared.WorkspaceCh
/**
* Start workspace watcher
* Watches ~/.rowboat recursively and emits change events to renderer
* Watches the configured workspace root recursively and emits change events to renderer
*
* This should be called once when the app starts (from main.ts).
* The watcher runs as a main-process service and catches ALL filesystem changes
@ -607,24 +619,12 @@ export function setupIpcHandlers() {
},
// Shell integration handlers
'shell:openPath': 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 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)');

View file

@ -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();
main();

View file

@ -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\\<name>\\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

View file

@ -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
`;

View file

@ -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

View file

@ -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

View file

@ -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/"\`

View file

@ -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/"\`

View file

@ -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 <url>\` to disambiguate:

View file

@ -1092,14 +1092,14 @@ export const BuiltinTools: z.infer<typeof BuiltinToolsSchema> = {
} catch {
return {
success: false,
error: 'Exa Search API key not configured. Create ~/.rowboat/config/exa-search.json with { "apiKey": "<your-key>" }',
error: `Exa Search API key not configured. Create ${exaConfigPath} with { "apiKey": "<your-key>" }`,
};
}
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}`,
};
}

View file

@ -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.)

View file

@ -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[] = [
{

View file

@ -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) ────────────────

View file

@ -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<Record<string, unknown> | 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": "<your-key>" }');
throw new Error(`ElevenLabs not configured. Create ${path.join(WorkDir, 'config', 'elevenlabs.json')} with { "apiKey": "<your-key>" }`);
}
const voiceId = config.elevenlabs.voiceId || 'UgBBYS2sOqTuMpoF3BR0';
url = `https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`;

View file

@ -10,7 +10,7 @@ export type WorkspaceChangeCallback = (event: z.infer<typeof WorkspaceChangeEven
/**
* Create a workspace watcher
* Watches ~/.rowboat recursively and emits change events via callback
* Watches the configured workspace root recursively and emits change events via callback
*
* Returns a watcher instance that can be closed.
* The watcher emits events immediately without debouncing.
@ -74,4 +74,3 @@ export async function createWorkspaceWatcher(
return watcher;
}