* 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 from637c908dropped 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 from637c908dropped 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>
87 lines
2.2 KiB
TypeScript
87 lines
2.2 KiB
TypeScript
import { readdirSync, readFileSync } from 'fs'
|
|
import { join, basename } from 'path'
|
|
import { parseFrontmatter } from '../utils/frontmatterParser.js'
|
|
import type { FrontmatterData } from '../utils/frontmatterParser.js'
|
|
import { getClaudeConfigHomeDir } from '../utils/envUtils.js'
|
|
import {
|
|
getProjectDirsUpToHome,
|
|
extractDescriptionFromMarkdown,
|
|
type ClaudeConfigDirectory,
|
|
} from '../utils/markdownConfigLoader.js'
|
|
|
|
export interface TemplateInfo {
|
|
name: string
|
|
description: string
|
|
filePath: string
|
|
frontmatter: FrontmatterData
|
|
content: string
|
|
}
|
|
|
|
/**
|
|
* Discover .claude/templates directories from CWD up to git root,
|
|
* plus the user-level ~/.claude/templates.
|
|
*/
|
|
function getTemplatesDirs(): string[] {
|
|
const projectDirs = getProjectDirsUpToHome(
|
|
'templates' as ClaudeConfigDirectory,
|
|
process.cwd(),
|
|
)
|
|
|
|
// User-level dir (getProjectDirsUpToHome stops before home)
|
|
const userDir = join(getClaudeConfigHomeDir(), 'templates')
|
|
try {
|
|
readdirSync(userDir)
|
|
return [...projectDirs, userDir]
|
|
} catch {
|
|
return projectDirs
|
|
}
|
|
}
|
|
|
|
/**
|
|
* List all available templates.
|
|
*/
|
|
export function listTemplates(): TemplateInfo[] {
|
|
const templates: TemplateInfo[] = []
|
|
const seenNames = new Set<string>()
|
|
|
|
for (const dir of getTemplatesDirs()) {
|
|
let files: string[]
|
|
try {
|
|
files = readdirSync(dir)
|
|
} catch {
|
|
continue
|
|
}
|
|
|
|
for (const file of files) {
|
|
if (!file.endsWith('.md')) continue
|
|
const name = basename(file, '.md')
|
|
if (seenNames.has(name)) continue
|
|
seenNames.add(name)
|
|
|
|
const filePath = join(dir, file)
|
|
try {
|
|
const raw = readFileSync(filePath, 'utf-8')
|
|
const { frontmatter, content } = parseFrontmatter(raw, filePath)
|
|
const description =
|
|
(typeof frontmatter.description === 'string'
|
|
? frontmatter.description
|
|
: '') || extractDescriptionFromMarkdown(content, 'No description')
|
|
|
|
templates.push({ name, description, filePath, frontmatter, content })
|
|
} catch {
|
|
// Skip unreadable files
|
|
}
|
|
}
|
|
}
|
|
|
|
return templates
|
|
}
|
|
|
|
/**
|
|
* Load a specific template by name.
|
|
*/
|
|
export function loadTemplate(name: string): TemplateInfo | null {
|
|
const all = listTemplates()
|
|
return all.find(t => t.name === name) ?? null
|
|
}
|