48 lines
1.9 KiB
TypeScript
48 lines
1.9 KiB
TypeScript
import { createHash } from 'node:crypto'
|
||
import * as os from 'node:os'
|
||
import * as path from 'node:path'
|
||
|
||
export const DEFAULT_WORKTREE_TEMPLATE = '~/Sources/worktree/$project_name/$feature_name'
|
||
|
||
/**
|
||
* 设计文档 / 计划按 ECC 布局存放:`.claude/specs/<stem>.md`、`.claude/plans/<stem>.md`,
|
||
* 文件名的 stem 就是 slug,两者共用同一个 stem。少数布局把文件名固定成
|
||
* `plan.md` / `spec.md`,这时 slug 退回父目录名;再兜底路径 hash,保证不为空。
|
||
*/
|
||
export function deriveSlug(planFile: string): string {
|
||
const stem = path.basename(planFile).replace(/\.md$/, '')
|
||
if (stem && stem !== 'plan' && stem !== 'spec')
|
||
return stem
|
||
const parent = path.basename(path.dirname(planFile))
|
||
if (parent && parent !== '.' && parent !== '/')
|
||
return parent
|
||
return createHash('sha256').update(planFile).digest('hex').slice(0, 8)
|
||
}
|
||
|
||
/** 实施分支固定形如 `feature/<slug>`;接管方只有分支名,从这里反推 worktree 目录名。 */
|
||
export function slugFromBranch(branch: string): string {
|
||
return branch.startsWith('feature/') ? branch.slice('feature/'.length) : branch
|
||
}
|
||
|
||
/** 本机没有工单的 worktree 时,审查它的 PR 建的 worktree 用这个目录名:按工单号,和实施用的 `feature/<slug>` 目录分开。 */
|
||
export function reviewWorktreeSlug(issueNumber: number): string {
|
||
return `review-${issueNumber}`
|
||
}
|
||
|
||
/**
|
||
* 把 worktreeDirectory 模板展开成绝对路径。`~` 在这里展开:git/execFile 不经 shell,
|
||
* 不会自己做 tilde 展开。
|
||
*/
|
||
export function resolveWorktreeDir(template: string, workspaceRoot: string, slug: string): string {
|
||
let p = template
|
||
.split('$project_name')
|
||
.join(path.basename(workspaceRoot))
|
||
.split('$project_root')
|
||
.join(workspaceRoot)
|
||
.split('$feature_name')
|
||
.join(slug)
|
||
if (p.startsWith('~'))
|
||
p = path.join(os.homedir(), p.slice(1))
|
||
return path.resolve(p)
|
||
}
|