This commit is contained in:
2026-07-24 14:26:56 +08:00
parent dc522064ce
commit 8d00676bba
6 changed files with 96 additions and 45 deletions
+57 -30
View File
@@ -1,23 +1,25 @@
# the name by which the project can be referenced within Serena # the name by which the project can be referenced within Serena/when chatting with the LLM.
project_name: "superwork" project_name: "superwork"
# list of languages for which language servers are started (LSP backend only); choose from:
# list of languages for which language servers are started; choose from: # ada al angular ansible bash
# al angular ansible bash clojure # bsl clojure cpp cpp_ccls crystal
# cpp cpp_ccls crystal csharp csharp_omnisharp # csharp csharp_omnisharp cue dart elixir
# dart elixir elm erlang fortran # elm erlang fortran fsharp gdscript
# fsharp go groovy haskell haxe # go groovy haskell haxe hlsl
# hlsl html java json julia # html java json julia kotlin
# kotlin lean4 lua luau markdown # latex lean4 lua luau markdown
# matlab msl nix ocaml pascal # matlab msl nix ocaml pascal
# perl php php_phpactor powershell python # perl php php_phpactor php_phpantom powershell
# python_jedi python_ty r rego ruby # python python_jedi python_pyrefly python_ty r
# ruby_solargraph rust scala scss solidity # rego ruby ruby_solargraph rust scala
# svelte swift systemverilog terraform toml # scss solidity svelte swift systemverilog
# typescript typescript_vts vue yaml zig # terraform toml typescript typescript_vts vue
# (This list may be outdated. For the current list, see values of Language enum here: # yaml zig
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py # (This list may be outdated; generated with scripts/print_language_list.py;
# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) # For the current list, see values of Language enum here:
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py)
# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.)
# Note: # Note:
# - For C, use cpp # - For C, use cpp
# - For JavaScript, use typescript # - For JavaScript, use typescript
@@ -56,21 +58,10 @@ ignore_all_files_in_gitignore: true
# advanced configuration option allowing to configure language server-specific options. # advanced configuration option allowing to configure language server-specific options.
# Maps the language key to the options. # Maps the language key to the options.
# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available. # The settings are considered only if the project is trusted (see global configuration to define trusted projects).
# No documentation on options means no options are available. # See https://oraios.github.io/serena/02-usage/050_configuration.html#language-server-specific-settings
ls_specific_settings: {} ls_specific_settings: {}
# list of additional workspace folder paths for cross-package reference support (e.g. in monorepos).
# Paths can be absolute or relative to the project root.
# Each folder is registered as an LSP workspace folder, enabling language servers to discover
# symbols and references across package boundaries.
# Currently supported for: TypeScript.
# Example:
# additional_workspace_folders:
# - ../sibling-package
# - ../shared-lib
additional_workspace_folders: []
# list of additional paths to ignore in this project. # list of additional paths to ignore in this project.
# Same syntax as gitignore, so you can use * and **. # Same syntax as gitignore, so you can use * and **.
# Note: global ignored_paths from serena_config.yml are also applied additively. # Note: global ignored_paths from serena_config.yml are also applied additively.
@@ -132,3 +123,39 @@ read_only_memory_patterns: []
# Extends the list from the global configuration, merging the two lists. # Extends the list from the global configuration, merging the two lists.
# Example: ["_archive/.*", "_episodes/.*"] # Example: ["_archive/.*", "_episodes/.*"]
ignored_memory_patterns: [] ignored_memory_patterns: []
# list of additional workspace folder paths for cross-package reference support.
# Paths can be absolute or relative to the project root.
# Each folder is registered as an LSP workspace folder, enabling language servers to discover
# symbols and references across package boundaries, but these folders are not indexed by Serena,
# i.e. the respective symbols will not be found using Serena's symbol search tools.
# Example:
# additional_workspace_folders:
# - ../sibling-package
# - ../shared-lib
ls_additional_workspace_folders: []
# list of workspace folder paths (LSP backend only).
# These folders will be used to build up Serena's symbol index.
# Paths must be within the project root and should thus be relative to the project root.
# Furthermore, the paths should not be filtered by ignore settings.
# Default setting: The entire project root folder (".") is considered.
# In (large) monorepos, this can be used to index only subfolders of the project root, e.g.
# ls_workspace_folders:
# - "./subproject1"
# - "./subproject2"
ls_workspace_folders:
- .
# optional shell command to run before the language backend (LSP or JetBrains) is initialised.
# the command runs in the project root directory and is only executed if the project is trusted
# (see trusted_project_path_patterns in the global configuration).
# serena waits for the command to exit: a non-zero exit code is logged as an error but does not
# abort activation. a per-project timeout (activation_command_timeout, default 180s) is the safety
# backstop for non-terminating commands; on expiry the process is killed and activation continues.
# example: activation_command: "npx nx run-many -t build"
activation_command:
# maximum time in seconds to wait for activation_command to complete before killing it (default 180s).
# must be a positive number.
activation_command_timeout: 180.0
+1 -1
View File
@@ -2,7 +2,7 @@
"publisher": "clurdra", "publisher": "clurdra",
"name": "superpowers-vscode-clurdra", "name": "superpowers-vscode-clurdra",
"displayName": "Superpowers-clurdra", "displayName": "Superpowers-clurdra",
"version": "0.2.60", "version": "0.2.66",
"packageManager": "pnpm@10.27.0", "packageManager": "pnpm@10.27.0",
"description": "Superpowers specs and plans Kanban explorer", "description": "Superpowers specs and plans Kanban explorer",
"author": "clurdra", "author": "clurdra",
+7 -6
View File
@@ -32,7 +32,8 @@ import { logger } from '../../logging/logger'
import { getSettings } from '../../settings/store' import { getSettings } from '../../settings/store'
import { webhookCoordinator } from '../../webhook/coordinator' import { webhookCoordinator } from '../../webhook/coordinator'
import { pickRandomIssueColor, themeColorIdToIconUri } from '../issueColor' import { pickRandomIssueColor, themeColorIdToIconUri } from '../issueColor'
import { DEFAULT_PROFILE_PATH, makeNonce, PR_DIFF_SUMMARY_PROFILE_PATH } from '../KanbanPanel' import { getDefaultProfilePath, getPrDiffSummaryProfilePath } from '../../cc/profiles'
import { makeNonce } from '../KanbanPanel'
import { cleanupFeatureBranch } from './branchCleanup' import { cleanupFeatureBranch } from './branchCleanup'
import * as sessions from './sessions' import * as sessions from './sessions'
@@ -1180,7 +1181,7 @@ export async function handleIssueCreate(
} }
const effectiveProfilePath const effectiveProfilePath
= brainstormProfilePath && brainstormProfilePath.trim() !== '' ? brainstormProfilePath : DEFAULT_PROFILE_PATH = brainstormProfilePath && brainstormProfilePath.trim() !== '' ? brainstormProfilePath : getDefaultProfilePath()
if (effectiveProfilePath.includes('\'')) { if (effectiveProfilePath.includes('\'')) {
panel.postMessage({ panel.postMessage({
type: 'toast/show', type: 'toast/show',
@@ -1616,7 +1617,7 @@ export async function handleUpdateAutoReview(panel: KanbanWebviewPanel, issueNum
/** /**
* Persist a per-issue `profilePath` override into the issue's state JSON * Persist a per-issue `profilePath` override into the issue's state JSON
* comment. Implement / implement-resume sessions use this profile * comment. Implement / implement-resume sessions use this profile
* (see resolveImplementProfilePath); empty = DEFAULT_PROFILE_PATH. * (see resolveImplementProfilePath); empty = getDefaultProfilePath().
* Mirrors handleUpdateAutoReview: optimistic update on the webview, rolled * Mirrors handleUpdateAutoReview: optimistic update on the webview, rolled
* back via `issue/patch` if the persist fails. * back via `issue/patch` if the persist fails.
*/ */
@@ -1707,7 +1708,7 @@ export async function handleUpdateProfilePath(panel: KanbanWebviewPanel, issueNu
/** /**
* Persist a per-issue `brainstormProfilePath` override into the issue's state * Persist a per-issue `brainstormProfilePath` override into the issue's state
* JSON. Brainstorm start/resume use this profile (see resolveBrainstormProfilePath); * JSON. Brainstorm start/resume use this profile (see resolveBrainstormProfilePath);
* empty = DEFAULT_PROFILE_PATH. 与实施/测试 profile 互不回退。 * empty = getDefaultProfilePath(). 与实施/测试 profile 互不回退。
*/ */
export async function handleUpdateBrainstormProfilePath(panel: KanbanWebviewPanel, issueNumber: number, brainstormProfilePath: string): Promise<void> { export async function handleUpdateBrainstormProfilePath(panel: KanbanWebviewPanel, issueNumber: number, brainstormProfilePath: string): Promise<void> {
const workspaceRoot = workspace.workspaceFolders?.[0]?.uri.fsPath const workspaceRoot = workspace.workspaceFolders?.[0]?.uri.fsPath
@@ -1791,7 +1792,7 @@ export async function handleUpdateBrainstormProfilePath(panel: KanbanWebviewPane
/** /**
* Persist a per-issue `testProfilePath` override into the issue's state JSON * Persist a per-issue `testProfilePath` override into the issue's state JSON
* comment. Test start/resume use this profile (see resolveTestProfilePath); * comment. Test start/resume use this profile (see resolveTestProfilePath);
* empty = DEFAULT_PROFILE_PATH,不回退实施 profile。 * empty = getDefaultProfilePath(),不回退实施 profile。
*/ */
export async function handleUpdateTestProfilePath(panel: KanbanWebviewPanel, issueNumber: number, testProfilePath: string): Promise<void> { export async function handleUpdateTestProfilePath(panel: KanbanWebviewPanel, issueNumber: number, testProfilePath: string): Promise<void> {
const workspaceRoot = workspace.workspaceFolders?.[0]?.uri.fsPath const workspaceRoot = workspace.workspaceFolders?.[0]?.uri.fsPath
@@ -2008,7 +2009,7 @@ export async function handleGeneratePrDiffSummary(panel: KanbanWebviewPanel, iss
dismissOnTimer: 5000, dismissOnTimer: 5000,
}) })
const profilePath = PR_DIFF_SUMMARY_PROFILE_PATH const profilePath = getPrDiffSummaryProfilePath()
const prompt = `你在一个 VS Code 扩展启动的后台 Claude 会话中工作。\n\n硬性限制:\n- 不要修改代码。\n- 不要创建或切换分支。\n- 不要提交。\n- 不要 push。\n- 不要写任何文件。\n- 只分析当前 PR 的代码变动,并把 Markdown 摘要正文直接输出到 stdout。\n\n任务:\n- 当前工作目录是工单 #\${issueNumber} 的 worktree。\n- 分析 PR #\${pr} 与主分支的代码差异。\n- 用 git 拿差异:先 git fetch origin main,再 git diff origin/main...HEAD(概览可加 --stat)。注意 tea 没有 diff 子命令,不要尝试 tea pulls diff。\n\n输出必须是合法 Markdown,预览器要能渲染出标题层级与代码块高亮,结构如下:\n1. 第一行是一级标题:# PR #\${pr} 代码变更摘要\n2. 紧接着用 1~2 句话概述本次变更:做了什么、新增了哪些包/模块、关键依赖方向(例如「stockpool/ 是与 orders/ catalog/ 平级的新子域包,其 service 只单向依赖 orders/catalog 的 repository,无循环导入」)。\n3. 然后写一行图例:图例:\\\`+\\\` = 新建文件 \\\`\\\` = 修改文件\n4. 然后是一棵统一的目录树,放进单个围栏代码块(语言标注用 text):树前一行写三个反引号紧跟 text,树后一行写三个反引号。\n\n目录树规则:\n- 用 box-drawing 连线字符画树:├──、└──、│,缩进对齐,像 tree 命令的输出,不要只用纯空格缩进糊一棵没有连线的树。\n- 从仓库根开始保留真实目录层级。\n- 每个文件行以标记开头:新建文件用「+ 」、修改文件用「✎ 」,后接文件名 + 若干空格对齐 + 一句中文目的描述(简短,点出关键符号/端点/职责)。\n- 目录节点行尾可加可选的阶段/分组标签(如 [Phase N]),仅当本次变更本身有清晰阶段时才加;没有就不加,不要硬编。\n- 关键:整棵树一定要写在围栏代码块之内,否则 Markdown 预览器会折叠空白、丢掉层级与连线。\n\n排序与重点:\n- 按架构重要性排列:核心业务/领域代码(实现逻辑、入口、API routes、数据模型、service/repository)靠前。\n- alembic 数据库迁移文件、测试文件不算重点:放在各自模块或整棵树的靠后位置,照常用 +/✎ 标记列出,但不要放在最前、不要强调。\n- 测试文件判定:文件名形如 test_* / *_test.* / *.test.* / *.spec.*,或路径包含 tests/、__tests__/ 目录,或 conftest.py、fixture、mock 文件。\n- 迁移文件判定:路径在 alembic/、migrations/、versions/ 之下的文件。\n\n格式示例(请照此结构输出,不要照抄内容):\n\n# PR #123 代码变更摘要\n\nstockpool/ 是与 orders/ catalog/ 平级的新子域包,其 service 只单向依赖 orders/catalog 的 repository,无循环导入。\n\n图例:\\\`+\\\` = 新建文件 \\\`\\\` = 修改文件\n\n\\\`\\\`\\\`text\nbackend/\n└── src/luffy_agent/commerce/\n ├── models.py ✎ +StockPoolProductCatalog 加 pool_id\n ├── stockpool/\n │ ├── service.py + reserve_pool_or_raise + compute_pool_views\n │ └── routes.py + /admin/commerce/stock-pools\n ├── orders/service.py ✎ create_order 绑定池分支\n └── alembic/versions/ + stock_pool 建表 + pool_id 加列\n\\\`\\\`\\\`\n\n要求:\n- 每个新增/修改文件一行,一句话说明新增/修改了什么及其目的。\n- 整棵树只用一个围栏代码块,用 box-drawing 连线保留真实目录层级。\n- 只输出上述 Markdown 正文本身,不要写文件,不要任何前导说明/寒暄/结尾总结,第一行就是 # PR #\${pr} 代码变更摘要 标题。` const prompt = `你在一个 VS Code 扩展启动的后台 Claude 会话中工作。\n\n硬性限制:\n- 不要修改代码。\n- 不要创建或切换分支。\n- 不要提交。\n- 不要 push。\n- 不要写任何文件。\n- 只分析当前 PR 的代码变动,并把 Markdown 摘要正文直接输出到 stdout。\n\n任务:\n- 当前工作目录是工单 #\${issueNumber} 的 worktree。\n- 分析 PR #\${pr} 与主分支的代码差异。\n- 用 git 拿差异:先 git fetch origin main,再 git diff origin/main...HEAD(概览可加 --stat)。注意 tea 没有 diff 子命令,不要尝试 tea pulls diff。\n\n输出必须是合法 Markdown,预览器要能渲染出标题层级与代码块高亮,结构如下:\n1. 第一行是一级标题:# PR #\${pr} 代码变更摘要\n2. 紧接着用 1~2 句话概述本次变更:做了什么、新增了哪些包/模块、关键依赖方向(例如「stockpool/ 是与 orders/ catalog/ 平级的新子域包,其 service 只单向依赖 orders/catalog 的 repository,无循环导入」)。\n3. 然后写一行图例:图例:\\\`+\\\` = 新建文件 \\\`\\\` = 修改文件\n4. 然后是一棵统一的目录树,放进单个围栏代码块(语言标注用 text):树前一行写三个反引号紧跟 text,树后一行写三个反引号。\n\n目录树规则:\n- 用 box-drawing 连线字符画树:├──、└──、│,缩进对齐,像 tree 命令的输出,不要只用纯空格缩进糊一棵没有连线的树。\n- 从仓库根开始保留真实目录层级。\n- 每个文件行以标记开头:新建文件用「+ 」、修改文件用「✎ 」,后接文件名 + 若干空格对齐 + 一句中文目的描述(简短,点出关键符号/端点/职责)。\n- 目录节点行尾可加可选的阶段/分组标签(如 [Phase N]),仅当本次变更本身有清晰阶段时才加;没有就不加,不要硬编。\n- 关键:整棵树一定要写在围栏代码块之内,否则 Markdown 预览器会折叠空白、丢掉层级与连线。\n\n排序与重点:\n- 按架构重要性排列:核心业务/领域代码(实现逻辑、入口、API routes、数据模型、service/repository)靠前。\n- alembic 数据库迁移文件、测试文件不算重点:放在各自模块或整棵树的靠后位置,照常用 +/✎ 标记列出,但不要放在最前、不要强调。\n- 测试文件判定:文件名形如 test_* / *_test.* / *.test.* / *.spec.*,或路径包含 tests/、__tests__/ 目录,或 conftest.py、fixture、mock 文件。\n- 迁移文件判定:路径在 alembic/、migrations/、versions/ 之下的文件。\n\n格式示例(请照此结构输出,不要照抄内容):\n\n# PR #123 代码变更摘要\n\nstockpool/ 是与 orders/ catalog/ 平级的新子域包,其 service 只单向依赖 orders/catalog 的 repository,无循环导入。\n\n图例:\\\`+\\\` = 新建文件 \\\`\\\` = 修改文件\n\n\\\`\\\`\\\`text\nbackend/\n└── src/luffy_agent/commerce/\n ├── models.py ✎ +StockPoolProductCatalog 加 pool_id\n ├── stockpool/\n │ ├── service.py + reserve_pool_or_raise + compute_pool_views\n │ └── routes.py + /admin/commerce/stock-pools\n ├── orders/service.py ✎ create_order 绑定池分支\n └── alembic/versions/ + stock_pool 建表 + pool_id 加列\n\\\`\\\`\\\`\n\n要求:\n- 每个新增/修改文件一行,一句话说明新增/修改了什么及其目的。\n- 整棵树只用一个围栏代码块,用 box-drawing 连线保留真实目录层级。\n- 只输出上述 Markdown 正文本身,不要写文件,不要任何前导说明/寒暄/结尾总结,第一行就是 # PR #\${pr} 代码变更摘要 标题。`
const result = await spawnClaude({ const result = await spawnClaude({
+1 -1
View File
@@ -286,7 +286,7 @@ export async function handleGeneratePrFileSummary(
type: 'toast/show', type: 'toast/show',
id: makeNonce(), id: makeNonce(),
level: 'error', level: 'error',
message: '未找到 deepseek profile,请在 /home/cruldra/Sources/cruldra-profile/claude-config/profiles/ 下创建 deepseek.json', message: '未找到 deepseek profiledeepseek-v4-pro / deepseek),请在 Claude profiles 目录下创建对应 .json',
dismissOnTimer: 8000, dismissOnTimer: 8000,
}) })
panel.postMessage({ type: 'pr-file-summary/show', issueNumber, path: filePath, error: '未找到 deepseek profile' }) panel.postMessage({ type: 'pr-file-summary/show', issueNumber, path: filePath, error: '未找到 deepseek profile' })
+1 -1
View File
@@ -61,7 +61,7 @@ export async function handleCommitRun(panel: KanbanWebviewPanel): Promise<void>
type: 'toast/show', type: 'toast/show',
id: makeNonce(), id: makeNonce(),
level: 'error', level: 'error',
message: '未找到 deepseek profile,请在 /home/cruldra/Sources/cruldra-profile/claude-config/profiles/ 下创建 deepseek.json', message: '未找到 deepseek profiledeepseek-v4-pro / deepseek),请在 Claude profiles 目录下创建对应 .json',
dismissOnTimer: 8000, dismissOnTimer: 8000,
}) })
return return
@@ -8,6 +8,27 @@ import { isIssueLocked } from '../lib/dependencies'
import type { PropertyGroup } from './property-grid' import type { PropertyGroup } from './property-grid'
import { PropertyGrid } from './property-grid' import { PropertyGrid } from './property-grid'
/** 跨机绝对路径只比 basename(同名 .json)映射到本机 profiles 列表。 */
function matchProfileOption(
current: string | undefined,
profiles: ClaudeProfile[],
): string {
const raw = current?.trim()
if (!raw)
return ''
if (profiles.some(p => p.path === raw))
return raw
const base = raw.split(/[/\\]/).pop() ?? ''
if (!base)
return raw
const byFile = profiles.find(p => p.path.split(/[/\\]/).pop() === base)
if (byFile)
return byFile.path
const name = base.endsWith('.json') ? base.slice(0, -'.json'.length) : base
const byName = profiles.find(p => p.name === name)
return byName?.path ?? raw
}
interface IssueDetailPanelProps { interface IssueDetailPanelProps {
issue: Issue | null issue: Issue | null
/** 当前所有 issues,用于解析前置依赖锁定态。 */ /** 当前所有 issues,用于解析前置依赖锁定态。 */
@@ -331,8 +352,9 @@ export function IssueDetailPanel({
// 占位项:value='' 对应「未设置」。原生 select 在 value 不匹配任何 // 占位项:value='' 对应「未设置」。原生 select 在 value 不匹配任何
// option 时会假显示第一项且 selectedIndex=0,导致点第一项不触发 // option 时会假显示第一项且 selectedIndex=0,导致点第一项不触发
// onChange、永远存不进去;占位项让未设置态有真实匹配项。 // onChange、永远存不进去;占位项让未设置态有真实匹配项。
// 跨机绝对路径按 basename 对齐本机 profiles,避免「自定义:/Users/...」。
const opts = [{ label: '(默认)', value: '' }, ...profiles.map(p => ({ label: p.name, value: p.path }))] const opts = [{ label: '(默认)', value: '' }, ...profiles.map(p => ({ label: p.name, value: p.path }))]
const current = issue?.brainstormProfilePath const current = matchProfileOption(issue?.brainstormProfilePath, profiles)
if (current && !opts.some(o => o.value === current)) if (current && !opts.some(o => o.value === current))
opts.push({ label: `自定义:${current}`, value: current }) opts.push({ label: `自定义:${current}`, value: current })
return opts return opts
@@ -345,7 +367,7 @@ export function IssueDetailPanel({
type: 'select', type: 'select',
options: (() => { options: (() => {
const opts = [{ label: '(默认)', value: '' }, ...profiles.map(p => ({ label: p.name, value: p.path }))] const opts = [{ label: '(默认)', value: '' }, ...profiles.map(p => ({ label: p.name, value: p.path }))]
const current = issue?.profilePath const current = matchProfileOption(issue?.profilePath, profiles)
if (current && !opts.some(o => o.value === current)) if (current && !opts.some(o => o.value === current))
opts.push({ label: `自定义:${current}`, value: current }) opts.push({ label: `自定义:${current}`, value: current })
return opts return opts
@@ -358,7 +380,7 @@ export function IssueDetailPanel({
type: 'select', type: 'select',
options: (() => { options: (() => {
const opts = [{ label: '(默认)', value: '' }, ...profiles.map(p => ({ label: p.name, value: p.path }))] const opts = [{ label: '(默认)', value: '' }, ...profiles.map(p => ({ label: p.name, value: p.path }))]
const current = issue?.testProfilePath const current = matchProfileOption(issue?.testProfilePath, profiles)
if (current && !opts.some(o => o.value === current)) if (current && !opts.some(o => o.value === current))
opts.push({ label: `自定义:${current}`, value: current }) opts.push({ label: `自定义:${current}`, value: current })
return opts return opts
@@ -485,9 +507,10 @@ export function IssueDetailPanel({
testSessionId: issue.testSessionId ?? null, testSessionId: issue.testSessionId ?? null,
autoReview: issue.autoReview ?? globalAutoReview, autoReview: issue.autoReview ?? globalAutoReview,
color: issue.color ?? null, color: issue.color ?? null,
brainstormProfilePath: issue.brainstormProfilePath ?? null, // 展示/选中值按 basename 映射到本机 profiles,避免跨机绝对路径变成「自定义」项。
profilePath: issue.profilePath ?? null, brainstormProfilePath: matchProfileOption(issue.brainstormProfilePath, profiles) || null,
testProfilePath: issue.testProfilePath ?? null, profilePath: matchProfileOption(issue.profilePath, profiles) || null,
testProfilePath: matchProfileOption(issue.testProfilePath, profiles) || null,
specFile: issue.specFile ?? null, specFile: issue.specFile ?? null,
planFile: issue.planFile ?? null, planFile: issue.planFile ?? null,
prDiffFile: issue.prDiffFile ?? null, prDiffFile: issue.prDiffFile ?? null,