feat(cursor): multitask slash, autoReview mode, native worktree/add-dir (#1014)

* feat(cursor): multitask slash, autoReview mode, native worktree/add-dir

Close the highest-value Cursor Agent gaps for remote HAPI: expand ACP-safe
slash pass-through (/multitask, worktree, add-dir, …), add autoReview
permission mode (--auto-review spawn + mid-session slash), and route Cursor
New Session worktrees through agent --worktree instead of HAPI sibling trees.

Fixes #1013

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): accept --mode autoReview for hapi cursor

Align --mode parsing with CURSOR_PERMISSION_MODES so documented
`hapi cursor --mode autoReview` enables Smart Auto instead of silently
falling back to default.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
SSU-WEI HUANG
2026-07-12 18:41:52 +08:00
committed by GitHub
co-authored by Cursor
parent b104c38f3a
commit 73584e925a
24 changed files with 630 additions and 143 deletions
+29 -1
View File
@@ -93,7 +93,7 @@ describe('buildCliArgs', () => {
})
it('validates all known permission modes', () => {
for (const mode of ['default', 'acceptEdits', 'auto', 'bypassPermissions', 'plan', 'ask', 'read-only', 'safe-yolo', 'yolo']) {
for (const mode of ['default', 'acceptEdits', 'auto', 'bypassPermissions', 'plan', 'ask', 'debug', 'autoReview', 'read-only', 'safe-yolo', 'yolo']) {
const args = buildCliArgs('claude', {
directory: '/tmp',
permissionMode: mode,
@@ -103,6 +103,34 @@ describe('buildCliArgs', () => {
}
})
it('passes --cursor-worktree for cursor worktree sessions', () => {
const args = buildCliArgs('cursor', {
directory: '/tmp/repo',
sessionType: 'worktree',
worktreeName: 'feature-x',
})
expect(args).toContain('--cursor-worktree')
expect(args).toContain('feature-x')
})
it('passes bare --cursor-worktree when name is omitted', () => {
const args = buildCliArgs('cursor', {
directory: '/tmp/repo',
sessionType: 'worktree',
})
expect(args).toContain('--cursor-worktree')
expect(args[args.length - 1]).toBe('--cursor-worktree')
})
it('does not pass --cursor-worktree for non-cursor worktree sessions', () => {
const args = buildCliArgs('claude', {
directory: '/tmp/repo',
sessionType: 'worktree',
worktreeName: 'feature-x',
})
expect(args).not.toContain('--cursor-worktree')
})
it('uses --session-id for pi resume (not --resume)', () => {
const args = buildCliArgs('pi', {
directory: '/tmp',
+27 -13
View File
@@ -336,20 +336,27 @@ export async function startRunner(options: { workspaceRoots?: string[] } = {}):
}
if (sessionType === 'worktree') {
const worktreeResult = await createWorktree({
basePath: directory,
nameHint: worktreeName
});
if (!worktreeResult.ok) {
logger.debug(`[RUNNER RUN] Worktree creation failed: ${worktreeResult.error}`);
return {
type: 'error',
errorMessage: worktreeResult.error
};
// Cursor Agent has native `--worktree` under ~/.cursor/worktrees/. Prefer that
// over HAPI's sibling-directory worktree so Cursor sandbox/skills see the same layout.
if (agent === 'cursor') {
spawnDirectory = directory;
logger.debug(`[RUNNER RUN] Cursor-native worktree requested (nameHint=${worktreeName ?? '(auto)'})`);
} else {
const worktreeResult = await createWorktree({
basePath: directory,
nameHint: worktreeName
});
if (!worktreeResult.ok) {
logger.debug(`[RUNNER RUN] Worktree creation failed: ${worktreeResult.error}`);
return {
type: 'error',
errorMessage: worktreeResult.error
};
}
worktreeInfo = worktreeResult.info;
spawnDirectory = worktreeInfo.worktreePath;
logger.debug(`[RUNNER RUN] Created worktree ${worktreeInfo.worktreePath} (branch ${worktreeInfo.branch})`);
}
worktreeInfo = worktreeResult.info;
spawnDirectory = worktreeInfo.worktreePath;
logger.debug(`[RUNNER RUN] Created worktree ${worktreeInfo.worktreePath} (branch ${worktreeInfo.branch})`);
}
const cleanupWorktree = async () => {
@@ -1125,5 +1132,12 @@ export function buildCliArgs(
args.push('--yolo');
}
}
if (agent === 'cursor' && options.sessionType === 'worktree') {
args.push('--cursor-worktree');
const name = options.worktreeName?.trim();
if (name) {
args.push(name);
}
}
return args;
}