Feat/integrate lint preview (#285)

* feat: 适配 zed acp 协议

* docs: 完善 acp 文档

* feat: integrate feature branches + daemon/job 命令层级化 + 跨平台后台引擎

Cherry-picked from origin/lint/preview (637c908), excluding lint-only changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: correct detectMimeFromBase64 to decode raw bytes from base64

Cherry-picked from origin/lint/preview (ee36954).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: daemon 子进程 spawn 跨平台修复 + CliLaunchSpec 集中化重构

Cherry-picked from origin/lint/preview (c5f52cd), excluding lint-only formatting changes.

- 新建 src/utils/cliLaunch.ts: 集中化 CLI 子进程启动层
- 修复 --daemon-worker=kind 等号格式解析
- 修复 daemon/bg fast path 缺少 setShellIfWindows()
- 修复 checkPathExists 用 existsSync 替代 execSync('dir')
- 7 个 spawn 站点迁移到 CliLaunchSpec

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: merge tsconfig.base.json into tsconfig.json with full compiler options

The cherry-pick from 637c908 dropped jsx/strict/etc settings when removing
tsconfig.base.json. This commit restores them in a single tsconfig.json.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: merge tsconfig.base.json into tsconfig.json with full compiler options

The cherry-pick from 637c908 dropped jsx/strict/etc settings when removing
tsconfig.base.json. This commit restores them in a single tsconfig.json.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
claude-code-best
2026-04-16 20:59:29 +08:00
committed by GitHub
parent a02dc0bded
commit c8d08d235b
137 changed files with 13267 additions and 837 deletions

View File

@@ -0,0 +1,117 @@
import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test'
import { existsSync } from 'node:fs'
import { join } from 'node:path'
import { cleanupTempDir, createTempDir } from '../../../tests/mocks/file-system'
// Mock the lockfile module so tests don't need real file locks
mock.module('../lockfile.js', () => ({
lock: async (_file: string, _options?: unknown) => {
return async () => {}
},
}))
let tempDir = ''
beforeEach(async () => {
tempDir = await createTempDir('autonomy-persistence-')
})
afterEach(async () => {
if (tempDir) {
await cleanupTempDir(tempDir)
}
})
describe('withAutonomyPersistenceLock', () => {
test('runs fn and returns its result', async () => {
const { withAutonomyPersistenceLock } = await import(
'../autonomyPersistence'
)
const result = await withAutonomyPersistenceLock(tempDir, async () => {
return 42
})
expect(result).toBe(42)
})
test('creates the autonomy directory and lock file', async () => {
const { withAutonomyPersistenceLock } = await import(
'../autonomyPersistence'
)
await withAutonomyPersistenceLock(tempDir, async () => 'ok')
const autonomyDir = join(tempDir, '.claude', 'autonomy')
expect(existsSync(autonomyDir)).toBe(true)
})
test('propagates errors from the inner function', async () => {
const { withAutonomyPersistenceLock } = await import(
'../autonomyPersistence'
)
await expect(
withAutonomyPersistenceLock(tempDir, async () => {
throw new Error('inner failure')
}),
).rejects.toThrow('inner failure')
})
test('serializes concurrent calls on the same rootDir', async () => {
const { withAutonomyPersistenceLock } = await import(
'../autonomyPersistence'
)
const order: number[] = []
const first = withAutonomyPersistenceLock(tempDir, async () => {
order.push(1)
// Simulate async work
await new Promise(resolve => setTimeout(resolve, 20))
order.push(2)
return 'first'
})
const second = withAutonomyPersistenceLock(tempDir, async () => {
order.push(3)
return 'second'
})
const [r1, r2] = await Promise.all([first, second])
expect(r1).toBe('first')
expect(r2).toBe('second')
// The second call must wait for the first to finish
expect(order).toEqual([1, 2, 3])
})
test('allows parallel calls on different rootDirs', async () => {
const { withAutonomyPersistenceLock } = await import(
'../autonomyPersistence'
)
const tempDir2 = await createTempDir('autonomy-persistence-2-')
try {
const order: string[] = []
const first = withAutonomyPersistenceLock(tempDir, async () => {
order.push('a-start')
await new Promise(resolve => setTimeout(resolve, 10))
order.push('a-end')
return 'a'
})
const second = withAutonomyPersistenceLock(tempDir2, async () => {
order.push('b-start')
await new Promise(resolve => setTimeout(resolve, 10))
order.push('b-end')
return 'b'
})
const [r1, r2] = await Promise.all([first, second])
expect(r1).toBe('a')
expect(r2).toBe('b')
// Both should start before either ends (parallel)
expect(order.indexOf('a-start')).toBeLessThan(order.indexOf('a-end'))
expect(order.indexOf('b-start')).toBeLessThan(order.indexOf('b-end'))
} finally {
await cleanupTempDir(tempDir2)
}
})
})