feat(vscode): 多人协作二期:状态分层/写校验/签名/团队视图

- 本机状态(会话id/worktree/profile/prDiffFile)迁 workspaceState,
  共享 state JSON 只留团队字段;读侧本地记录叠加,旧数据兜底
- mergeStateJsonComment 写后读回校验,并发覆盖时在最新状态上重放一次
- webhook 支持 Gitea secret,校验 X-Gitea-Signature(HMAC-SHA256),
  不匹配 401;设置面板新增 Webhook Secret 字段
- 看板新增 我的/全部 范围切换(团队视图),按工作区持久化
This commit is contained in:
2026-08-19 00:32:06 +08:00
parent c370214d12
commit faf227412e
18 changed files with 665 additions and 202 deletions
+11 -2
View File
@@ -1,8 +1,9 @@
import type { PastedImage } from './components/NewIssueModal'
import type { Issue } from './types'
import { useEffect, useMemo, useRef, useState } from 'react'
import { BottomTabs } from './components/BottomTabs'
import { KanbanBoard } from './components/KanbanBoard'
import { LogModal } from './components/LogModal'
import type { PastedImage } from './components/NewIssueModal'
import { NewIssueModal } from './components/NewIssueModal'
import { PanelHeader } from './components/PanelHeader'
import { SettingsModal } from './components/SettingsModal'
@@ -11,7 +12,6 @@ import { useIssues } from './hooks/useIssues'
import { useManagedSessions } from './hooks/useManagedSessions'
import { useProfiles } from './hooks/useProfiles'
import { compareIssuesInColumn } from './lib/issueSort'
import type { Issue } from './types'
import { COLUMN_ORDER } from './types'
export function App() {
@@ -23,6 +23,8 @@ export function App() {
importYouTrack,
toasts,
profiles,
scope,
toggleScope,
setIssues,
refresh,
saveSettings,
@@ -283,6 +285,8 @@ export function App() {
<>
<PanelHeader
onRefresh={refresh}
scope={scope}
onToggleScope={toggleScope}
onEditAuth={requestEditAuth}
commitRunning={commitRunning}
hasChanges={hasChanges}
@@ -310,6 +314,8 @@ export function App() {
<>
<PanelHeader
onRefresh={refresh}
scope={scope}
onToggleScope={toggleScope}
onEditAuth={requestEditAuth}
commitRunning={commitRunning}
hasChanges={hasChanges}
@@ -391,6 +397,8 @@ export function App() {
<>
<PanelHeader
onRefresh={refresh}
scope={scope}
onToggleScope={toggleScope}
onEditAuth={requestEditAuth}
commitRunning={commitRunning}
hasChanges={hasChanges}
@@ -452,6 +460,7 @@ export function App() {
canCancel={settings?.canCancel}
initialTokenSaved={settings?.tokenSaved ?? false}
initialWebhookPort={settings?.webhookPort ?? 17421}
initialWebhookSecret={settings?.webhookSecret ?? ''}
initialBrainstormPrompt={settings?.brainstormPrompt ?? ''}
initialBrainstormContinuePrompt={settings?.brainstormContinuePrompt ?? ''}
initialImplementPlanPrompt={settings?.implementPlanPrompt ?? ''}
@@ -1,49 +1,72 @@
import { GitCommit, GitMerge, ListPlus, Loader2, Lock, RefreshCw, Settings, Unlock } from 'lucide-react'
import { GitCommit, GitMerge, ListPlus, Loader2, Lock, RefreshCw, Settings, Unlock, User, Users } from 'lucide-react'
interface Props {
onRefresh: () => void
/** 看板范围:'mine' 只看我的(assigned/created),'all' 团队全部工单。 */
scope: 'mine' | 'all'
onToggleScope: () => void
onEditAuth: () => void
commitRunning: boolean
/** Whether the workspace git working tree has uncommitted changes. The
/**
* Whether the workspace git working tree has uncommitted changes. The
* commit button is only rendered when this is true (or `commitRunning` is
* true — we keep it visible mid-run so the spinner stays on screen even
* if `cc` clears the tree before finishing). */
* if `cc` clears the tree before finishing).
*/
hasChanges: boolean
onCommit: () => void
/** Commits the remote auto-build branch is behind the remote dev branch.
/**
* Commits the remote auto-build branch is behind the remote dev branch.
* Combined with `branchSyncDisabled` to compute the button's disabled
* state. */
* state.
*/
branchSyncBehind: number
branchSyncRunning: boolean
/** True when sync is structurally unavailable (same-branch / fetch fail
* / not a repo / …). Disables the button regardless of `branchSyncBehind`. */
/**
* True when sync is structurally unavailable (same-branch / fetch fail
* / not a repo / …). Disables the button regardless of `branchSyncBehind`.
*/
branchSyncDisabled: boolean
/** Hover tooltip, already includes branch names + behind count or the
* unavailable reason. */
/**
* Hover tooltip, already includes branch names + behind count or the
* unavailable reason.
*/
branchSyncTitle: string
onSyncBranch: () => void
/** Whether the workspace's `.env*` files are currently chmod-locked (444).
* Icon flips between Lock and Unlock based on this. */
/**
* Whether the workspace's `.env*` files are currently chmod-locked (444).
* Icon flips between Lock and Unlock based on this.
*/
envLocked: boolean
/** Number of `.env*` files discovered in the workspace at the last scan.
* The button is disabled when 0 — nothing to lock. */
/**
* Number of `.env*` files discovered in the workspace at the last scan.
* The button is disabled when 0 — nothing to lock.
*/
envFileCount: number
/** True while a chmod batch is in flight; disables the button to avoid
* duplicate toggles. */
/**
* True while a chmod batch is in flight; disables the button to avoid
* duplicate toggles.
*/
envLockRunning: boolean
/** Hover tooltip, already includes file count + current lock state. */
envLockTitle: string
onToggleEnvLock: () => void
/** Whether YouTrack is configured. The「导入 YouTrack 工单」button is only
* rendered when true. */
/**
* Whether YouTrack is configured. The「导入 YouTrack 工单」button is only
* rendered when true.
*/
youtrackConfigured: boolean
/** Open the native multi-select dialog to pick which YouTrack issues to
* mirror onto the board. */
/**
* Open the native multi-select dialog to pick which YouTrack issues to
* mirror onto the board.
*/
onImportYouTrack: () => void
}
export function PanelHeader({
onRefresh,
scope,
onToggleScope,
onEditAuth,
commitRunning,
hasChanges,
@@ -122,6 +145,15 @@ export function PanelHeader({
<ListPlus size={14} />
</button>
)}
<button
type="button"
onClick={onToggleScope}
title={scope === 'mine' ? '当前:只看我的工单,点击切换为全部' : '当前:团队全部工单,点击切换为只看我的'}
aria-label={scope === 'mine' ? '切换为全部工单' : '切换为只看我的'}
className="flex h-6 w-6 items-center justify-center rounded hover:bg-[var(--vscode-toolbar-hoverBackground)]"
>
{scope === 'mine' ? <User size={14} /> : <Users size={14} />}
</button>
<button
type="button"
onClick={onRefresh}
@@ -8,9 +8,9 @@
*/
import type { ReactElement } from 'react'
import { useEffect, useState } from 'react'
import { X } from 'lucide-react'
import type { ClaudeProfile } from '../hooks/useIssues'
import { X } from 'lucide-react'
import { useEffect, useState } from 'react'
type GroupKey = 'auth' | 'network' | 'youtrack' | 'prompts' | 'hooks'
@@ -18,6 +18,7 @@ interface SubmitValues {
host: string
token: string
webhookPort: number
webhookSecret: string
brainstormPrompt: string
brainstormContinuePrompt: string
implementPlanPrompt: string
@@ -63,6 +64,7 @@ export interface SettingsModalProps {
canCancel?: boolean
initialTokenSaved: boolean
initialWebhookPort: number
initialWebhookSecret: string
initialBrainstormPrompt: string
initialBrainstormContinuePrompt: string
initialImplementPlanPrompt: string
@@ -127,8 +129,10 @@ const GROUPS: Array<{ key: GroupKey, label: string }> = [
type ErrorKey = 'host' | 'token' | 'webhookPort'
/** Map a field name to the group it lives in, so we can auto-switch to the
* first invalid field's group on save. */
/**
* Map a field name to the group it lives in, so we can auto-switch to the
* first invalid field's group on save.
*/
const FIELD_TO_GROUP: Record<ErrorKey, GroupKey> = {
host: 'auth',
token: 'auth',
@@ -142,6 +146,7 @@ export function SettingsModal({
canCancel,
initialTokenSaved,
initialWebhookPort,
initialWebhookSecret,
initialBrainstormPrompt,
initialBrainstormContinuePrompt,
initialImplementPlanPrompt,
@@ -175,6 +180,7 @@ export function SettingsModal({
const [host, setHost] = useState(initialHost)
const [token, setToken] = useState('')
const [webhookPort, setWebhookPort] = useState<string>(String(initialWebhookPort))
const [webhookSecret, setWebhookSecret] = useState(initialWebhookSecret)
const [brainstormPrompt, setBrainstormPrompt] = useState(initialBrainstormPrompt)
const [brainstormContinuePrompt, setBrainstormContinuePrompt] = useState(initialBrainstormContinuePrompt)
const [implementPlanPrompt, setImplementPlanPrompt] = useState(initialImplementPlanPrompt)
@@ -210,6 +216,7 @@ export function SettingsModal({
setHost(initialHost)
setToken('')
setWebhookPort(String(initialWebhookPort))
setWebhookSecret(initialWebhookSecret)
setBrainstormPrompt(initialBrainstormPrompt)
setBrainstormContinuePrompt(initialBrainstormContinuePrompt)
setImplementPlanPrompt(initialImplementPlanPrompt)
@@ -294,6 +301,7 @@ export function SettingsModal({
// extension side detects empty + existing-saved as "preserve".
token: trimmedToken,
webhookPort: portNum,
webhookSecret: webhookSecret.trim(),
brainstormPrompt,
brainstormContinuePrompt,
implementPlanPrompt,
@@ -578,7 +586,13 @@ export function SettingsModal({
HTTP server gitea Webhooks URL
{' '}
<code>{'http://<你的本机/公网域名>:<此端口>/webhook'}</code>
<b></b>issue opened/edited<b></b>PR opened/synchronize/closed<b></b>codex
<b></b>
issue opened/edited
<b></b>
PR opened/synchronize/closed
<b></b>
codex
</>
)}
>
@@ -592,6 +606,30 @@ export function SettingsModal({
/>
</Field>
<Field
label="Webhook Secret"
hint={(
<>
gitea Webhooks
{' '}
<b></b>
{' '}
{' '}
<code>X-Gitea-Signature</code>
webhook =
</>
)}
>
<input
type="text"
value={webhookSecret}
onChange={e => setWebhookSecret(e.target.value)}
placeholder="留空表示不校验签名"
className={inputClass}
/>
</Field>
<Field
label="开启自动审查"
hint="PR 创建后自动用 codex exec review 跑审查并把结果灌到实施会话终端"
@@ -791,7 +829,12 @@ export function SettingsModal({
<Field
label="审查提示词"
hint={<><code>{'{prNumber}'}</code></>}
hint={(
<>
<code>{'{prNumber}'}</code>
</>
)}
>
<textarea
rows={4}
+106 -49
View File
@@ -16,10 +16,10 @@
* after a drag. Persisting drag moves back to Gitea is step 3+.
*/
import { useCallback, useEffect, useRef, useState } from 'react'
import type { Issue, IssueColumn } from '../types'
import type { ToastItem } from '../components/ToastStack'
import type { LogEntry } from '../lib/messages'
import type { Issue, IssueColumn } from '../types'
import { useCallback, useEffect, useRef, useState } from 'react'
import { compareIssuesInColumn } from '../lib/issueSort'
import { onMessage, postMessage } from '../lib/vscode'
@@ -37,6 +37,7 @@ export interface SettingsValues {
host: string
token: string
webhookPort: number
webhookSecret: string
brainstormPrompt: string
brainstormContinuePrompt: string
implementPlanPrompt: string
@@ -68,6 +69,7 @@ export interface SettingsOverlayState {
canCancel: boolean
tokenSaved: boolean
webhookPort: number
webhookSecret: string
brainstormPrompt: string
brainstormContinuePrompt: string
implementPlanPrompt: string
@@ -107,10 +109,10 @@ export interface YouTrackProjectsState {
error?: string
}
export type UseIssuesState =
| { status: 'loading' }
| { status: 'ready', issues: Issue[] }
| { status: 'error', message: string }
export type UseIssuesState
= | { status: 'loading' }
| { status: 'ready', issues: Issue[] }
| { status: 'error', message: string }
/** Mirror of the extension-side ClaudeProfile in src/cc/profiles.ts. */
export interface ClaudeProfile {
@@ -121,23 +123,34 @@ export interface ClaudeProfile {
export interface UseIssuesResult {
state: UseIssuesState
settings: SettingsOverlayState | null
/** Latest known global `autoReview` value pushed alongside `issues/update`.
/** 看板范围:'mine' 只看我的(assigned/created),'all' 团队全部。 */
scope: 'mine' | 'all'
toggleScope: () => void
/**
* Latest known global `autoReview` value pushed alongside `issues/update`.
* Used by the detail panel to display the fallback value when an issue has
* no per-issue override. */
* no per-issue override.
*/
globalAutoReview: boolean
/** Whether YouTrack is configured (base URL + project set), pushed alongside
* `issues/update`. Drives whether the「导入 YouTrack 工单」toolbar button shows. */
/**
* Whether YouTrack is configured (base URL + project set), pushed alongside
* `issues/update`. Drives whether the「导入 YouTrack 工单」toolbar button shows.
*/
youtrackConfigured: boolean
/** Open the native multi-select dialog to pick which YouTrack issues to
* mirror onto the board. */
/**
* Open the native multi-select dialog to pick which YouTrack issues to
* mirror onto the board.
*/
importYouTrack: () => void
toasts: ToastItem[]
profiles: ClaudeProfile[]
setIssues: (issues: Issue[]) => void
refresh: () => void
saveSettings: (values: SettingsValues) => void
/** Fetch the YouTrack project list for the settings dropdown using the
* in-form base URL + token (so the user can pick before saving). */
/**
* Fetch the YouTrack project list for the settings dropdown using the
* in-form base URL + token (so the user can pick before saving).
*/
listYouTrackProjects: (baseUrl: string, token: string) => void
/** Result of the most recent `listYouTrackProjects` call. */
youtrackProjects: YouTrackProjectsState
@@ -159,81 +172,115 @@ export interface UseIssuesResult {
openWorktree: (path: string) => void
deleteWorktree: (issueNumber: number, path: string) => void
mergeBranch: (issueNumber: number, branch: string) => void
/** 硬删 Gitea 工单及关联资源(worktree / PR / feature branch / cc tabs)。
/**
* 硬删 Gitea 工单及关联资源(worktree / PR / feature branch / cc tabs)。
* 用户在详情面板顶部点垃圾桶按钮触发。扩展端做 modal confirm + 串行清理,
* 全部成功后 push `issue/remove`webview 将该 issue 从 issues 数组移除。 */
* 全部成功后 push `issue/remove`webview 将该 issue 从 issues 数组移除。
*/
deleteIssue: (issueNumber: number) => void
/** 关闭 Gitea 工单,不清理本地会话、worktree、PR 或分支。 */
closeIssue: (issueNumber: number) => void
/** Dispose the matching session terminal tab. Extension reacts via
* onDidCloseTerminal and clears `*TabOpen` so the X button disappears. */
/**
* Dispose the matching session terminal tab. Extension reacts via
* onDidCloseTerminal and clears `*TabOpen` so the X button disappears.
*/
closeSessionTab: (issueNumber: number, kind: 'brainstorm' | 'implement' | 'review' | 'test') => void
/** Spawn a fresh "规划" cc tab for an existing issue whose sessionId is
/**
* Spawn a fresh "规划" cc tab for an existing issue whose sessionId is
* still empty. Extension 用工单级 brainstormProfilePath(空 = 默认),
* watches `~/.claude/projects/<encoded-workspaceRoot>` for the new
* session jsonl, then writes the id back as `sessionId`. */
* session jsonl, then writes the id back as `sessionId`.
*/
startBrainstormSession: (issueNumber: number) => void
changeColumn: (issueNumber: number, toColumn: IssueColumn, source?: 'gitea' | 'youtrack', externalId?: string) => void
setDependency: (issueNumber: number, prerequisiteNumber: number) => void
clearDependency: (issueNumber: number, prerequisiteNumber: number) => void
updateIssueAutoReview: (issueNumber: number, value: boolean) => void
/** 覆盖该工单实施会话使用的 Claude 配置文件,乐观更新本地 state 后
* postMessage 持久化到 state JSON。失败时扩展端 push `issue/patch` 回滚。 */
/**
* 覆盖该工单实施会话使用的 Claude 配置文件,乐观更新本地 state 后
* postMessage 持久化到 state JSON。失败时扩展端 push `issue/patch` 回滚。
*/
updateIssueProfilePath: (issueNumber: number, profilePath: string) => void
/** 覆盖该工单头脑风暴会话专用的 Claude 配置文件,乐观更新本地 state 后
* postMessage 持久化到 state JSON。失败时扩展端 push `issue/patch` 回滚。 */
/**
* 覆盖该工单头脑风暴会话专用的 Claude 配置文件,乐观更新本地 state 后
* postMessage 持久化到 state JSON。失败时扩展端 push `issue/patch` 回滚。
*/
updateIssueBrainstormProfilePath: (issueNumber: number, brainstormProfilePath: string) => void
/** 覆盖该工单测试会话专用的 Claude 配置文件,乐观更新本地 state 后
* postMessage 持久化到 state JSON。失败时扩展端 push `issue/patch` 回滚。 */
/**
* 覆盖该工单测试会话专用的 Claude 配置文件,乐观更新本地 state 后
* postMessage 持久化到 state JSON。失败时扩展端 push `issue/patch` 回滚。
*/
updateIssueTestProfilePath: (issueNumber: number, testProfilePath: string) => void
logs: LogEntry[]
fetchLogs: () => void
clearLogs: () => void
/** True while the "提交当前代码" claude -p run is in flight. Used to
* disable the toolbar button and swap its icon to a spinner. */
/**
* True while the "提交当前代码" claude -p run is in flight. Used to
* disable the toolbar button and swap its icon to a spinner.
*/
commitRunning: boolean
/** Trigger a background `claude -p "提交下代码"` run in the workspace
/**
* Trigger a background `claude -p "提交下代码"` run in the workspace
* root, gated by `commitRunning`. The extension responds with
* `commit/state` messages and a `toast/show` on completion. */
* `commit/state` messages and a `toast/show` on completion.
*/
runCommit: () => void
/** Whether the workspace git working tree currently has any uncommitted
/**
* Whether the workspace git working tree currently has any uncommitted
* changes (working tree / index / untracked). Pushed by the extension via
* `commit/has-changes` and used by `PanelHeader` to skip rendering the
* commit button when the tree is clean. Defaults to `false` until the
* extension reports its first observation. */
* extension reports its first observation.
*/
hasChanges: boolean
/** Commits the remote auto-build branch is behind the remote dev branch
* (per latest `branch-sync/status`). 0 ⇒ in sync ⇒ disable button. */
/**
* Commits the remote auto-build branch is behind the remote dev branch
* (per latest `branch-sync/status`). 0 ⇒ in sync ⇒ disable button.
*/
branchSyncBehind: number
/** True while a `branch-sync/run` request is in flight. The extension
/**
* True while a `branch-sync/run` request is in flight. The extension
* doesn't echo a "running" state itself — we set this on click and clear
* it when the next `branch-sync/status` arrives. */
* it when the next `branch-sync/status` arrives.
*/
branchSyncRunning: boolean
/** True when sync is structurally unavailable (same branch, missing
* remote, fetch error, …). Disables the button regardless of behind. */
/**
* True when sync is structurally unavailable (same branch, missing
* remote, fetch error, …). Disables the button regardless of behind.
*/
branchSyncDisabled: boolean
/** Tooltip text for the sync button (already includes branch names /
* behind count / unavailable reason). */
/**
* Tooltip text for the sync button (already includes branch names /
* behind count / unavailable reason).
*/
branchSyncTitle: string
/** Trigger a fast-forward push from remote dev to remote auto-build. */
runBranchSync: () => void
/** Whether the workspace's `.env*` files are currently chmod-locked
* according to `workspaceState`. Persisted per-workspace. */
/**
* Whether the workspace's `.env*` files are currently chmod-locked
* according to `workspaceState`. Persisted per-workspace.
*/
envLocked: boolean
/** File count from the latest `env-lock/status`. 0 disables the toggle. */
envFileCount: number
/** True while a `env-lock/toggle` chmod batch is in flight. Cleared when
* the next `env-lock/status` arrives. */
/**
* True while a `env-lock/toggle` chmod batch is in flight. Cleared when
* the next `env-lock/status` arrives.
*/
envLockRunning: boolean
/** Tooltip text for the env-lock button, pre-composed from lock state +
* file count. */
/**
* Tooltip text for the env-lock button, pre-composed from lock state +
* file count.
*/
envLockTitle: string
/** Flip the env-lock state: chmod all `.env*` files to 444 or 644. */
toggleEnvLock: () => void
/** ID of an issue that should be auto-selected after an `issue/append`
/**
* ID of an issue that should be auto-selected after an `issue/append`
* with `select: true` (i.e. user-initiated webhook creation). Consumers
* read this in a `useEffect`, apply the selection, then call
* `clearPendingSelect()` to reset. */
* `clearPendingSelect()` to reset.
*/
pendingSelectId: string | null
clearPendingSelect: () => void
}
@@ -243,6 +290,7 @@ export function useIssues(): UseIssuesResult {
const [settings, setSettings] = useState<SettingsOverlayState | null>(null)
const [youtrackProjects, setYoutrackProjects] = useState<YouTrackProjectsState>({ status: 'idle', projects: [] })
const [globalAutoReview, setGlobalAutoReview] = useState<boolean>(true)
const [scope, setScope] = useState<'mine' | 'all'>('mine')
const [youtrackConfigured, setYoutrackConfigured] = useState<boolean>(false)
const [toasts, setToasts] = useState<ToastItem[]>([])
const [profiles, setProfiles] = useState<ClaudeProfile[]>([])
@@ -271,6 +319,12 @@ export function useIssues(): UseIssuesResult {
setPendingSelectId(null)
}, [])
const toggleScope = useCallback((): void => {
setState({ status: 'loading' })
setScope(prev => prev === 'mine' ? 'all' : 'mine')
postMessage({ type: 'issues/set-scope', scope: scope === 'mine' ? 'all' : 'mine' })
}, [scope])
const refresh = useCallback((): void => {
setState({ status: 'loading' })
postMessage({ type: 'issues/refresh' })
@@ -290,6 +344,7 @@ export function useIssues(): UseIssuesResult {
host: values.host,
token: values.token,
webhookPort: values.webhookPort,
webhookSecret: values.webhookSecret,
brainstormPrompt: values.brainstormPrompt,
brainstormContinuePrompt: values.brainstormContinuePrompt,
implementPlanPrompt: values.implementPlanPrompt,
@@ -557,6 +612,7 @@ export function useIssues(): UseIssuesResult {
break
case 'issues/update':
setState({ status: 'ready', issues: msg.issues })
setScope(msg.scope)
setGlobalAutoReview(msg.globalAutoReview)
setYoutrackConfigured(msg.youtrackConfigured)
break
@@ -638,6 +694,7 @@ export function useIssues(): UseIssuesResult {
canCancel: msg.canCancel === true,
tokenSaved: msg.tokenSaved,
webhookPort: msg.webhookPort,
webhookSecret: msg.webhookSecret,
brainstormPrompt: msg.brainstormPrompt,
brainstormContinuePrompt: msg.brainstormContinuePrompt,
implementPlanPrompt: msg.implementPlanPrompt,
@@ -760,5 +817,5 @@ export function useIssues(): UseIssuesResult {
return cleanup
}, [clearPrDiffSummaryRunning])
return { state, settings, globalAutoReview, youtrackConfigured, importYouTrack, toasts, profiles, setIssues, refresh, saveSettings, listYouTrackProjects, youtrackProjects, dismissSettings, requestEditAuth, createIssue, dismissToast, openUrl, resumeSession, resumeReviewSession, startTestSession, resumeTestSession, focusSession, openFile, implement, generatePrDiffSummary, isPrDiffSummaryRunning, openPr, openWorktree, deleteWorktree, mergeBranch, deleteIssue, closeIssue, closeSessionTab, startBrainstormSession, changeColumn, setDependency, clearDependency, updateIssueAutoReview, updateIssueProfilePath, updateIssueBrainstormProfilePath, updateIssueTestProfilePath, logs, fetchLogs, clearLogs, pendingSelectId, clearPendingSelect, commitRunning, runCommit, hasChanges, branchSyncBehind, branchSyncRunning, branchSyncDisabled, branchSyncTitle, runBranchSync, envLocked, envFileCount, envLockRunning, envLockTitle, toggleEnvLock }
return { state, settings, globalAutoReview, youtrackConfigured, importYouTrack, toasts, profiles, setIssues, refresh, scope, toggleScope, saveSettings, listYouTrackProjects, youtrackProjects, dismissSettings, requestEditAuth, createIssue, dismissToast, openUrl, resumeSession, resumeReviewSession, startTestSession, resumeTestSession, focusSession, openFile, implement, generatePrDiffSummary, isPrDiffSummaryRunning, openPr, openWorktree, deleteWorktree, mergeBranch, deleteIssue, closeIssue, closeSessionTab, startBrainstormSession, changeColumn, setDependency, clearDependency, updateIssueAutoReview, updateIssueProfilePath, updateIssueBrainstormProfilePath, updateIssueTestProfilePath, logs, fetchLogs, clearLogs, pendingSelectId, clearPendingSelect, commitRunning, runCommit, hasChanges, branchSyncBehind, branchSyncRunning, branchSyncDisabled, branchSyncTitle, runBranchSync, envLocked, envFileCount, envLockRunning, envLockTitle, toggleEnvLock }
}
+5 -2
View File
@@ -55,7 +55,7 @@ export interface ProfilesData {
export type ExtensionToWebview
= | { type: 'issues/loading' }
| { type: 'issues/update', issues: Issue[], globalAutoReview: boolean, youtrackConfigured: boolean }
| { type: 'issues/update', issues: Issue[], scope: 'mine' | 'all', globalAutoReview: boolean, youtrackConfigured: boolean }
| { type: 'issues/error', message: string }
| { type: 'issue/patch', issueNumber: number, patch: { autoReview?: boolean, specFile?: string, planFile?: string, prDiffFile?: string, sessionId?: string, implementSessionId?: string, reviewSessionId?: string, testSessionId?: string, pr?: string | null, implementStatus?: 'running' | 'done' | 'failed', column?: IssueColumn, worktreePath?: string | null, prMerged?: boolean, prMergedAt?: string, branch?: string | null, color?: string, worktreeExists?: boolean, brainstormTabOpen?: boolean, implementTabOpen?: boolean, reviewTabOpen?: boolean, testTabOpen?: boolean, profilePath?: string, brainstormProfilePath?: string, testProfilePath?: string } }
| { type: 'issue/pr-diff-summary-done', issueNumber: number }
@@ -68,6 +68,7 @@ export type ExtensionToWebview
canCancel?: boolean
tokenSaved: boolean
webhookPort: number
webhookSecret: string
brainstormPrompt: string
brainstormContinuePrompt: string
implementPlanPrompt: string
@@ -132,11 +133,13 @@ export type ExtensionToWebview
export type WebviewToExtension
= | { type: 'issues/refresh' }
| { type: 'issues/set-scope', scope: 'mine' | 'all' }
| {
type: 'settings/save'
host: string
token: string
webhookPort: number
webhookSecret: string
brainstormPrompt: string
brainstormContinuePrompt: string
implementPlanPrompt: string
@@ -178,7 +181,7 @@ export type WebviewToExtension
| { type: 'pr/open', pr: string }
| { type: 'worktree/open', path: string }
| { type: 'worktree/delete', issueNumber: number, path: string }
| { type: 'git/merge-preview', issueNumber: number, branch: string }
| { type: 'git/merge-preview', issueNumber: number, branch: string }
| { type: 'column/change', issueNumber: number, toColumn: 'todo' | 'in-progress' | 'review' | 'done', source?: 'gitea' | 'youtrack', externalId?: string }
| { type: 'dependency/set', issueNumber: number, prerequisiteNumber: number }
| { type: 'dependency/clear', issueNumber: number, prerequisiteNumber: number }