Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
* (or in a different subdirectory), the wrong workspace path is sent to
* the Tower API, causing the open to fail.
*
* Fix: Derive workspace root from the FILE's directory (via findWorkspaceRoot)
* instead of CWD. The file itself determines which workspace it belongs to.
* Fix (updated by bugfix #535): Prefer CWD-based workspace detection (for
* cross-workspace opens), but fall back to file-based detection when CWD
* isn't in a recognizable workspace.
*/
import { describe, it, expect } from 'vitest';
import { resolve } from 'node:path';
Expand All @@ -19,10 +20,11 @@ describe('Bugfix #500: af open works from any directory', () => {
'utf-8',
);

it('should use findWorkspaceRoot from file directory, not getConfig', () => {
// The fix: workspace root is derived from the file's location
it('should fall back to findWorkspaceRoot from file directory when CWD is not in a workspace', () => {
// Bugfix #535 changed to CWD-first with file-based fallback.
// The file-based fallback must still exist for when CWD is outside any workspace.
expect(openSrc).toContain('findWorkspaceRoot(dirname(filePath))');
// Should NOT use getConfig() — that uses CWD which is the wrong approach
// Should NOT use getConfig() — that doesn't support file-based fallback
expect(openSrc).not.toContain('getConfig()');
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Regression test for bugfix #535: af open does not reliably work on files
* outside the current directory
*
* Root cause: `open.ts` derived the workspace from the FILE's location
* (via findWorkspaceRoot(dirname(filePath))). When running `af open` from
* workspace A but targeting a file in workspace B, the file opened in B's
* annotation viewer instead of A's.
*
* Fix: Prefer CWD-based workspace detection (findWorkspaceRoot(process.cwd()))
* so the file opens in the user's current workspace. Fall back to file-based
* detection only when CWD isn't in a recognizable workspace.
*/
import { describe, it, expect } from 'vitest';
import { resolve } from 'node:path';
import { readFileSync } from 'node:fs';

describe('Bugfix #535: af open cross-workspace file resolution', () => {
const openSrc = readFileSync(
resolve(import.meta.dirname, '../commands/open.ts'),
'utf-8',
);

it('should prefer CWD-based workspace detection over file-based', () => {
// The fix: determine workspace from CWD first
expect(openSrc).toContain('findWorkspaceRoot(process.cwd())');
});

it('should check whether CWD resolves to a real workspace', () => {
// Must verify CWD is in a workspace (has codev/ or .git) before using it
expect(openSrc).toContain('cwdIsWorkspace');
expect(openSrc).toMatch(/existsSync.*codev/);
expect(openSrc).toMatch(/existsSync.*\.git/);
});

it('should fall back to file-based detection when CWD is not a workspace', () => {
// When CWD is outside any workspace (e.g., /tmp), fall back to file location
expect(openSrc).toContain('findWorkspaceRoot(dirname(filePath))');
});

it('should still support worktree fallback to main repo', () => {
// Bugfix #427's worktree fallback must be preserved
expect(openSrc).toContain('getMainRepoFromWorktree(workspacePath)');
});

it('should not use getConfig for workspace detection', () => {
// getConfig() uses CWD internally but doesn't support the fallback pattern
expect(openSrc).not.toContain('getConfig()');
});
});
16 changes: 12 additions & 4 deletions packages/codev/src/agent-farm/commands/open.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,18 @@ export async function open(options: OpenOptions): Promise<void> {
fatal(`File not found: ${filePath}`);
}

// Find workspace root from the FILE's location, not CWD.
// This allows `af open` to work from any directory — the file itself
// determines which workspace it belongs to.
let workspacePath = findWorkspaceRoot(dirname(filePath));
// Determine workspace from CWD first (the user's current workspace context).
// This ensures cross-workspace file opens show in the user's active workspace,
// not in the workspace where the file physically resides.
// Fall back to file-based detection when CWD isn't in a recognizable workspace
// (e.g., running from /tmp).
const cwdWorkspace = findWorkspaceRoot(process.cwd());
const cwdIsWorkspace = existsSync(resolve(cwdWorkspace, 'codev')) ||
existsSync(resolve(cwdWorkspace, '.git'));

let workspacePath = cwdIsWorkspace
? cwdWorkspace
: findWorkspaceRoot(dirname(filePath));

// When running from a worktree, Tower only knows the main repo workspace.
// Fall back to main repo path so the API call targets a registered workspace.
Expand Down