♻️ refactor: 需求与计划改走 ECC 工作流,PRD/计划落到 .claude 目录
头脑风暴、续聊、实施三套 prompt(vscode / jetbrains / tui 同步)从 superpowers 的 brainstorming 与 /goal 换成 /ecc:plan-prd、/ecc:plan、/ecc:tdd-workflow。spec/plan 路径固定为 .claude/prds/<slug>.prd.md 与 .claude/plans/<slug>.plan.md,slug 派生去掉 .prd.md / .plan.md 后缀;worktreePath 测试挪到 test/ 目录。 Claude-Session: https://claude.ai/code/session_01QWxLb6X8Vd2G5vHnp6R7qm
This commit is contained in:
1 parent
028195ab2b
commit
1fee0f2630
19 files changed
+136
-90
No files matched your search
@@ -1,32 +0,0 @@
|
||||
import * as os from 'node:os'
|
||||
import * as path from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { DEFAULT_WORKTREE_TEMPLATE, deriveSlug, resolveWorktreeDir, slugFromBranch } from './worktreePath'
|
||||
|
||||
describe('deriveSlug', () => {
|
||||
it('取 plans/ 后一段', () => {
|
||||
expect(deriveSlug('docs/superpowers/plans/foo-bar/plan.md')).toBe('foo-bar')
|
||||
})
|
||||
it('没有 plans/specs 锚点时退回父目录名', () => {
|
||||
expect(deriveSlug('docs/x/baz/plan.md')).toBe('baz')
|
||||
})
|
||||
})
|
||||
|
||||
describe('slugFromBranch', () => {
|
||||
it('去掉 feature/ 前缀', () => {
|
||||
expect(slugFromBranch('feature/foo-bar')).toBe('foo-bar')
|
||||
})
|
||||
it('无前缀原样返回', () => {
|
||||
expect(slugFromBranch('hotfix-1')).toBe('hotfix-1')
|
||||
})
|
||||
})
|
||||
|
||||
describe('resolveWorktreeDir', () => {
|
||||
it('展开三个占位符与 ~', () => {
|
||||
const out = resolveWorktreeDir(DEFAULT_WORKTREE_TEMPLATE, '/w/proj', 'slug')
|
||||
expect(out).toBe(path.join(os.homedir(), 'Sources', 'worktree', 'proj', 'slug'))
|
||||
})
|
||||
it('$project_root 展开为工作区绝对路径', () => {
|
||||
expect(resolveWorktreeDir('$project_root/.wt/$feature_name', '/w/proj', 's')).toBe('/w/proj/.wt/s')
|
||||
})
|
||||
})
|
||||
@@ -5,14 +5,19 @@ import * as path from 'node:path'
|
||||
export const DEFAULT_WORKTREE_TEMPLATE = '~/Sources/worktree/$project_name/$feature_name'
|
||||
|
||||
/**
|
||||
* spec/plan 以目录形式存放(docs/superpowers/{specs,plans}/<slug>/…),slug 取
|
||||
* `plans`/`specs` 后一段。兜底父目录名,再兜底路径 hash,保证目录名不为空。
|
||||
* PRD / 计划按 ECC 布局存放:`.claude/prds/<slug>.prd.md`、`.claude/plans/<slug>.plan.md`。
|
||||
* slug 取 `plans` / `prds` / `specs` 后一段并去掉 `.plan.md` / `.prd.md` / `.md` 后缀。
|
||||
* 兜底父目录名,再兜底路径 hash,保证目录名不为空。
|
||||
*/
|
||||
export function deriveSlug(planFile: string): string {
|
||||
const segments = planFile.split('/').filter(Boolean)
|
||||
const anchor = segments.findIndex(s => s === 'plans' || s === 'specs')
|
||||
if (anchor >= 0 && segments[anchor + 1])
|
||||
return segments[anchor + 1]
|
||||
const anchor = segments.findIndex(s => s === 'plans' || s === 'prds' || s === 'specs')
|
||||
const next = anchor >= 0 ? segments[anchor + 1] : undefined
|
||||
if (next) {
|
||||
const slug = next.replace(/\.(?:plan|prd)\.md$|\.md$/, '')
|
||||
if (slug)
|
||||
return slug
|
||||
}
|
||||
const parent = path.basename(path.dirname(planFile))
|
||||
if (parent && parent !== '.' && parent !== '/')
|
||||
return parent
|
||||
|
||||
@@ -48,13 +48,13 @@ export interface Issue {
|
||||
/**
|
||||
* Optional workspace-relative path to the spec file for this issue, as
|
||||
* surfaced from the Claude session transcript. Lives under
|
||||
* `docs/superpowers/specs/*.md`.
|
||||
* `.claude/prds/<slug>.prd.md`.
|
||||
*/
|
||||
specFile?: string
|
||||
/**
|
||||
* Optional workspace-relative path to the plan file for this issue, as
|
||||
* surfaced from the Claude session transcript. Lives under
|
||||
* `docs/superpowers/plans/*.md`.
|
||||
* `.claude/plans/<slug>.plan.md`.
|
||||
*/
|
||||
planFile?: string
|
||||
/** Optional workspace-relative path to the generated PR diff summary. */
|
||||
|
||||
@@ -791,7 +791,7 @@ export class KanbanWebviewPanel {
|
||||
* 3. Confirm the always-on webhook coordinator is bound to the current
|
||||
* port, then register a gitea webhook scoped to the feature branch
|
||||
* and hand the bookkeeping to the coordinator.
|
||||
* 4. Spawn `claude` in an editor-tab terminal with a /goal prompt that
|
||||
* 4. Spawn `claude` in an editor-tab terminal with the implement prompt that
|
||||
* tells cc to create the worktree, implement the plan, and emit
|
||||
* `<request_review>$pr_no</request_review>` when done.
|
||||
*
|
||||
|
||||
@@ -632,7 +632,7 @@ async function triggerAutoReviewTabUnlocked(panel: KanbanWebviewPanel, opts: {
|
||||
* 3. Confirm the always-on webhook coordinator is bound to the current
|
||||
* port, then register a gitea webhook scoped to the feature branch
|
||||
* and hand the bookkeeping to the coordinator.
|
||||
* 4. Spawn `claude` in an editor-tab terminal with a /goal prompt that
|
||||
* 4. Spawn `claude` in an editor-tab terminal with the implement prompt that
|
||||
* tells cc to create the worktree, implement the plan, and emit
|
||||
* `<request_review>$pr_no</request_review>` when done.
|
||||
*
|
||||
|
||||
@@ -24,11 +24,11 @@ import { normalizeReleasePrefix } from '../git/release'
|
||||
* users can tweak them without rebuilding. These short fallbacks just keep
|
||||
* the extension usable if the file is missing/corrupt.
|
||||
*/
|
||||
const FALLBACK_BRAINSTORM_PROMPT = `/goal 我现在有这样一个需求 {userRequest},你用 spx 命令创建 gitea 工单。工单 body 末尾必须包含 <!-- spx:nonce={nonce} -->。创建完工单立即停下汇报,不要擅自实施。`
|
||||
const FALLBACK_BRAINSTORM_PROMPT = `我现在有这样一个需求 {userRequest},你用 spx 命令创建 gitea 工单。工单 body 末尾必须包含 <!-- spx:nonce={nonce} -->。创建完工单立即停下汇报,不要擅自实施。`
|
||||
|
||||
const FALLBACK_BRAINSTORM_CONTINUE_PROMPT = `/superpowers:brainstorming 讨论下 {issueNumber} 号工单。注意:不要 git checkout / 不要写代码 / 不要建 PR,只讨论需求与 spec/plan,用 spx issue marker 更新 marker。`
|
||||
const FALLBACK_BRAINSTORM_CONTINUE_PROMPT = `/ecc:plan-prd 讨论下 {issueNumber} 号工单。PRD 写到 .claude/prds/<slug>.prd.md,我确认后用 ecc:plan 出 .claude/plans/<slug>.plan.md,两个文件在 main 上提交并用 spx issue marker 同步 marker。不要 git checkout / 不要写代码 / 不要建 PR。`
|
||||
|
||||
const FALLBACK_IMPLEMENT_PLAN_PROMPT = `/goal 使用子代理全程绿灯实施 @{planFile},发起 PR 时在 body 中包含 "Closes #{issueNumber}"。严禁合并 PR。`
|
||||
const FALLBACK_IMPLEMENT_PLAN_PROMPT = `/ecc:tdd-workflow @{planFile} 按计划实施,编码派子代理,发起 PR 时在 body 中包含 "Closes #{issueNumber}"。严禁合并 PR。`
|
||||
|
||||
const FALLBACK_REVIEW_PROMPT = `/review 用 tea 拿到 #{prNumber} PR 审查。审查意见用 spx pr review-comment 发评论,body 第一行写 <!-- spx:review=1 -->。`
|
||||
|
||||
|
||||
Reference in new issue
Block a user