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

@@ -1,13 +1,216 @@
// Auto-generated stub — replace with real implementation
import type { Command } from '@commander-js/extra-typings';
import type { Command } from '@commander-js/extra-typings'
import {
createTask,
getTask,
updateTask,
listTasks,
getTasksDir,
} from '../../utils/tasks.js'
import { getRecentActivity } from '../../utils/logoV2Utils.js'
import type { LogOption } from '../../types/logs.js'
export {};
export const logHandler: (logId: string | number | undefined) => Promise<void> = (async () => {}) as (logId: string | number | undefined) => Promise<void>;
export const errorHandler: (num: number | undefined) => Promise<void> = (async () => {}) as (num: number | undefined) => Promise<void>;
export const exportHandler: (source: string, outputFile: string) => Promise<void> = (async () => {}) as (source: string, outputFile: string) => Promise<void>;
export const taskCreateHandler: (subject: string, opts: { description?: string; list?: string }) => Promise<void> = (async () => {}) as (subject: string, opts: { description?: string; list?: string }) => Promise<void>;
export const taskListHandler: (opts: { list?: string; pending?: boolean; json?: boolean }) => Promise<void> = (async () => {}) as (opts: { list?: string; pending?: boolean; json?: boolean }) => Promise<void>;
export const taskGetHandler: (id: string, opts: { list?: string }) => Promise<void> = (async () => {}) as (id: string, opts: { list?: string }) => Promise<void>;
export const taskUpdateHandler: (id: string, opts: { list?: string; status?: string; subject?: string; description?: string; owner?: string; clearOwner?: boolean }) => Promise<void> = (async () => {}) as (id: string, opts: { list?: string; status?: string; subject?: string; description?: string; owner?: string; clearOwner?: boolean }) => Promise<void>;
export const taskDirHandler: (opts: { list?: string }) => Promise<void> = (async () => {}) as (opts: { list?: string }) => Promise<void>;
export const completionHandler: (shell: string, opts: { output?: string }, program: Command) => Promise<void> = (async () => {}) as (shell: string, opts: { output?: string }, program: Command) => Promise<void>;
const DEFAULT_LIST = 'default'
// ─── Group C: Task CRUD ──────────────────────────────────────────────────────
export async function taskCreateHandler(
subject: string,
opts: { description?: string; list?: string },
): Promise<void> {
const listId = opts.list || DEFAULT_LIST
const id = await createTask(listId, {
subject,
description: opts.description || '',
status: 'pending',
blocks: [],
blockedBy: [],
})
console.log(`Created task ${id}: ${subject}`)
}
export async function taskListHandler(opts: {
list?: string
pending?: boolean
json?: boolean
}): Promise<void> {
const listId = opts.list || DEFAULT_LIST
let tasks = await listTasks(listId)
if (opts.pending) {
tasks = tasks.filter(t => t.status === 'pending')
}
if (opts.json) {
console.log(JSON.stringify(tasks, null, 2))
return
}
if (tasks.length === 0) {
console.log('No tasks found.')
return
}
for (const t of tasks) {
console.log(` [${t.status}] ${t.id}: ${t.subject}`)
if (t.description) console.log(` ${t.description}`)
if (t.owner) console.log(` owner: ${t.owner}`)
}
}
export async function taskGetHandler(
id: string,
opts: { list?: string },
): Promise<void> {
const listId = opts.list || DEFAULT_LIST
const task = await getTask(listId, id)
if (!task) {
console.error(`Task not found: ${id}`)
process.exitCode = 1
return
}
console.log(JSON.stringify(task, null, 2))
}
export async function taskUpdateHandler(
id: string,
opts: {
list?: string
status?: string
subject?: string
description?: string
owner?: string
clearOwner?: boolean
},
): Promise<void> {
const listId = opts.list || DEFAULT_LIST
const updates: Record<string, unknown> = {}
if (opts.status) updates.status = opts.status
if (opts.subject) updates.subject = opts.subject
if (opts.description) updates.description = opts.description
if (opts.owner) updates.owner = opts.owner
if (opts.clearOwner) updates.owner = undefined
const task = await updateTask(listId, id, updates)
if (!task) {
console.error(`Task not found: ${id}`)
process.exitCode = 1
return
}
console.log(`Updated task ${id}: [${task.status}] ${task.subject}`)
}
export async function taskDirHandler(opts: { list?: string }): Promise<void> {
const listId = opts.list || DEFAULT_LIST
console.log(getTasksDir(listId))
}
// ─── Group B: Log / Error / Export ───────────────────────────────────────────
export async function logHandler(
logId: string | number | undefined,
): Promise<void> {
const logs = await getRecentActivity()
if (logId === undefined) {
if (logs.length === 0) {
console.log('No recent sessions.')
return
}
for (let i = 0; i < Math.min(logs.length, 20); i++) {
const log = logs[i]!
const date = log.modified
? new Date(log.modified).toLocaleString()
: 'unknown'
const title =
(log as Record<string, unknown>).title || log.sessionId || 'untitled'
console.log(` ${i}: ${title} (${date})`)
}
return
}
const idx = typeof logId === 'string' ? parseInt(logId, 10) : logId
const log =
Number.isFinite(idx) && idx >= 0 && idx < logs.length
? logs[idx]
: logs.find(l => l.sessionId === String(logId))
if (!log) {
console.error(`Session not found: ${logId}`)
process.exitCode = 1
return
}
console.log(JSON.stringify(log, null, 2))
}
export async function errorHandler(num: number | undefined): Promise<void> {
// Error log viewing — shows recent session errors
const logs = await getRecentActivity()
const count = num ?? 5
console.log(`Last ${count} sessions:`)
for (let i = 0; i < Math.min(count, logs.length); i++) {
const log = logs[i]!
const date = log.modified
? new Date(log.modified).toLocaleString()
: 'unknown'
console.log(` ${i}: ${log.sessionId} (${date})`)
}
}
export async function exportHandler(
source: string,
outputFile: string,
): Promise<void> {
const { writeFile, readFile } = await import('fs/promises')
const logs = await getRecentActivity()
// Try as index first
const idx = parseInt(source, 10)
let log: LogOption | undefined
if (Number.isFinite(idx) && idx >= 0 && idx < logs.length) {
log = logs[idx]
} else {
log = logs.find(l => l.sessionId === source)
}
if (!log) {
// Try as file path
try {
const content = await readFile(source, 'utf-8')
await writeFile(outputFile, content, 'utf-8')
console.log(`Exported ${source}${outputFile}`)
return
} catch {
console.error(`Source not found: ${source}`)
process.exitCode = 1
return
}
}
await writeFile(outputFile, JSON.stringify(log, null, 2), 'utf-8')
console.log(`Exported session ${log.sessionId}${outputFile}`)
}
// ─── Group D: Completion ─────────────────────────────────────────────────────
export async function completionHandler(
shell: string,
opts: { output?: string },
_program: Command,
): Promise<void> {
const { regenerateCompletionCache } = await import(
'../../utils/completionCache.js'
)
if (opts.output) {
// Generate and write to file
await regenerateCompletionCache()
console.log(`Completion cache regenerated for ${shell}.`)
} else {
// Regenerate and output to stdout
await regenerateCompletionCache()
console.log(`Completion cache regenerated for ${shell}.`)
}
}

View File

@@ -1,3 +1,158 @@
// Auto-generated stub — replace with real implementation
export {};
export const templatesMain: (args: string[]) => Promise<void> = () => Promise.resolve();
import { randomUUID } from 'crypto'
import { listTemplates, loadTemplate } from '../../jobs/templates.js'
import {
createJob,
readJobState,
appendJobReply,
getJobDir,
} from '../../jobs/state.js'
/**
* Entry point for template job commands: `new`, `list`, `reply`.
* Called from cli.tsx fast-path.
*/
export async function templatesMain(args: string[]): Promise<void> {
const subcommand = args[0]
switch (subcommand) {
case 'list':
handleList()
break
case 'new':
handleNew(args.slice(1))
break
case 'reply':
handleReply(args.slice(1))
break
case 'status':
handleStatus(args.slice(1))
break
default:
console.error(`Unknown template command: ${subcommand}`)
printUsage()
process.exitCode = 1
}
}
function printUsage(): void {
console.log(`
Template Job Commands:
claude job list List available templates
claude job new <template> [args] Create a new job from a template
claude job reply <job-id> <text> Reply to an existing job
claude job status <job-id> Show job status
`)
}
function handleStatus(args: string[]): void {
const jobId = args[0]
if (!jobId) {
console.error('Usage: claude job status <job-id>')
process.exitCode = 1
return
}
const state = readJobState(jobId)
if (!state) {
console.error(`Job not found: ${jobId}`)
process.exitCode = 1
return
}
console.log(`Job: ${state.jobId}`)
console.log(` Template: ${state.templateName}`)
console.log(` Status: ${state.status}`)
console.log(` Created: ${state.createdAt}`)
console.log(` Updated: ${state.updatedAt}`)
console.log(` Args: ${state.args.join(' ') || '(none)'}`)
}
function handleList(): void {
const templates = listTemplates()
if (templates.length === 0) {
console.log('No templates found.')
console.log('Place .md files in .claude/templates/ or ~/.claude/templates/')
return
}
console.log(
`${templates.length} template${templates.length > 1 ? 's' : ''} found:\n`,
)
for (const t of templates) {
console.log(` ${t.name}`)
console.log(` ${t.description}`)
console.log(` Path: ${t.filePath}`)
console.log()
}
}
function handleNew(args: string[]): void {
const templateName = args[0]
if (!templateName) {
console.error('Usage: claude job new <template> [args...]')
process.exitCode = 1
return
}
const template = loadTemplate(templateName)
if (!template) {
console.error(`Template not found: ${templateName}`)
console.log('\nAvailable templates:')
for (const t of listTemplates()) {
console.log(` ${t.name}`)
}
process.exitCode = 1
return
}
const jobId = randomUUID().slice(0, 8)
const inputText = args.slice(1).join(' ')
const rawContent = `---\n${Object.entries(template.frontmatter)
.map(([k, v]) => `${k}: ${v}`)
.join('\n')}\n---\n${template.content}`
const dir = createJob(
jobId,
templateName,
rawContent,
inputText,
args.slice(1),
)
console.log(`Job created: ${jobId}`)
console.log(` Template: ${templateName}`)
console.log(` Directory: ${dir}`)
if (inputText) {
console.log(` Input: ${inputText}`)
}
}
function handleReply(args: string[]): void {
const jobId = args[0]
const text = args.slice(1).join(' ')
if (!jobId || !text) {
console.error('Usage: claude job reply <job-id> <text>')
process.exitCode = 1
return
}
const state = readJobState(jobId)
if (!state) {
console.error(`Job not found: ${jobId}`)
process.exitCode = 1
return
}
const ok = appendJobReply(jobId, text)
if (ok) {
console.log(`Reply added to job ${jobId}`)
console.log(` Directory: ${getJobDir(jobId)}`)
} else {
console.error(`Failed to append reply to job ${jobId}`)
process.exitCode = 1
}
}