🎨 style(vscode): eslint 自动修复格式与排序
Claude-Session: https://claude.ai/code/session_01HTMgwaE3woqM5yvsHiuSAa
This commit is contained in:
1 parent
df35bcfd49
commit
8c22a29ce5
41 files changed
+248
-167
No files matched your search
@@ -24,8 +24,10 @@ export async function deleteToken(ctx: ExtensionContext, host: string): Promise<
|
||||
await ctx.secrets.delete(tokenKey(host))
|
||||
}
|
||||
|
||||
/** YouTrack permanent tokens, keyed by host so they never collide with the
|
||||
* gitea token of the same host. */
|
||||
/**
|
||||
* YouTrack permanent tokens, keyed by host so they never collide with the
|
||||
* gitea token of the same host.
|
||||
*/
|
||||
export function youtrackTokenKey(host: string): string {
|
||||
return `youtrack-token:${host}`
|
||||
}
|
||||
|
||||
@@ -34,24 +34,24 @@ describe('resolveClaudeBin', () => {
|
||||
expect(resolveClaudeBin({ PATH: dir }, '/no-such-home')).toBe(bin)
|
||||
})
|
||||
|
||||
it('PATH 未命中时回退到 $HOME/.local/bin/claude', () => {
|
||||
it('pATH 未命中时回退到 $HOME/.local/bin/claude', () => {
|
||||
const home = tempDir()
|
||||
const bin = touchClaude(path.join(home, '.local', 'bin'))
|
||||
expect(resolveClaudeBin({ PATH: '' }, home)).toBe(bin)
|
||||
})
|
||||
|
||||
it('PATH 未命中时回退到 $HOME/.claude/local/claude', () => {
|
||||
it('pATH 未命中时回退到 $HOME/.claude/local/claude', () => {
|
||||
const home = tempDir()
|
||||
const bin = touchClaude(path.join(home, '.claude', 'local'))
|
||||
expect(resolveClaudeBin({ PATH: '' }, home)).toBe(bin)
|
||||
})
|
||||
|
||||
it('PATH 与候选皆空 → null', () => {
|
||||
it('pATH 与候选皆空 → null', () => {
|
||||
const home = tempDir()
|
||||
expect(resolveClaudeBin({ PATH: '' }, home)).toBeNull()
|
||||
})
|
||||
|
||||
it('PATH 中目录不存在时跳过并继续', () => {
|
||||
it('pATH 中目录不存在时跳过并继续', () => {
|
||||
const home = tempDir()
|
||||
const bin = touchClaude(path.join(home, '.local', 'bin'))
|
||||
expect(resolveClaudeBin({ PATH: '/definitely/missing/bin' }, home)).toBe(bin)
|
||||
|
||||
@@ -169,13 +169,11 @@ export async function watchForNewCodexSession(opts: {
|
||||
})
|
||||
})
|
||||
watcher.on('error', (err) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('[superpowers] codexSessionWatcher: watch error', err)
|
||||
done(null)
|
||||
})
|
||||
}
|
||||
catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('[superpowers] codexSessionWatcher: fs.watch threw', err)
|
||||
done(null)
|
||||
return
|
||||
|
||||
@@ -57,7 +57,7 @@ describe('resolveCommitProfileForRun', () => {
|
||||
const result = resolveCommitProfileForRun(
|
||||
missing,
|
||||
p => p,
|
||||
p => {
|
||||
(p) => {
|
||||
try {
|
||||
accessSync(p, constants.R_OK)
|
||||
return true
|
||||
@@ -77,7 +77,7 @@ describe('resolveCommitProfileForRun', () => {
|
||||
const result = resolveCommitProfileForRun(
|
||||
file,
|
||||
p => p,
|
||||
p => {
|
||||
(p) => {
|
||||
try {
|
||||
accessSync(p, constants.R_OK)
|
||||
return true
|
||||
@@ -98,7 +98,7 @@ describe('resolveCommitProfileForRun', () => {
|
||||
const result = resolveCommitProfileForRun(
|
||||
foreign,
|
||||
() => local,
|
||||
p => {
|
||||
(p) => {
|
||||
try {
|
||||
accessSync(p, constants.R_OK)
|
||||
return true
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import type { PrDiffMode } from './prDiffMode'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
DEFAULT_PR_DIFF_MODE,
|
||||
isPrDiffMode,
|
||||
parsePrDiffMode,
|
||||
type PrDiffMode,
|
||||
|
||||
} from './prDiffMode'
|
||||
|
||||
describe('DEFAULT_PR_DIFF_MODE', () => {
|
||||
describe('dEFAULT_PR_DIFF_MODE', () => {
|
||||
it('默认是 unified', () => {
|
||||
expect(DEFAULT_PR_DIFF_MODE).toBe('unified')
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { accessSync, constants, mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { accessSync, constants, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import * as os from 'node:os'
|
||||
import * as path from 'node:path'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
|
||||
@@ -35,7 +35,6 @@ export async function watchForNewSession(opts: {
|
||||
snapshot = new Set(existing.filter(n => n.endsWith('.jsonl')))
|
||||
}
|
||||
catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('[superpowers] sessionWatcher: readdir failed', err)
|
||||
return null
|
||||
}
|
||||
@@ -89,13 +88,11 @@ export async function watchForNewSession(opts: {
|
||||
})
|
||||
})
|
||||
watcher.on('error', (err) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('[superpowers] sessionWatcher: watch error', err)
|
||||
done(null)
|
||||
})
|
||||
}
|
||||
catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('[superpowers] sessionWatcher: fs.watch threw', err)
|
||||
done(null)
|
||||
return
|
||||
@@ -131,7 +128,6 @@ export async function pollForNewSession(opts: {
|
||||
snapshot = new Set(existing.filter(n => n.endsWith('.jsonl')))
|
||||
}
|
||||
catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('[superpowers] sessionWatcher: poll readdir failed', err)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -105,7 +105,6 @@ function extractClaudePayload(stdout: string): ParsedClaudeJson | null {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
function truncateDiagnostic(text: string): string {
|
||||
if (text.length <= EXIT_DIAGNOSTIC_MAX_CHARS)
|
||||
return text
|
||||
|
||||
@@ -15,9 +15,11 @@ import { RelativePattern, workspace } from 'vscode'
|
||||
const ENV_PATTERN = '**/.env*'
|
||||
const EXCLUDE_PATTERN = '{**/node_modules/**,**/.git/**,**/.claude/**}'
|
||||
|
||||
/** Scan the workspace folder rooted at `workspaceRoot` for all `.env*`
|
||||
/**
|
||||
* Scan the workspace folder rooted at `workspaceRoot` for all `.env*`
|
||||
* files, skipping `node_modules`, `.git`, and `.claude`. Returns absolute
|
||||
* fs paths. Empty array if the workspace folder isn't currently open. */
|
||||
* fs paths. Empty array if the workspace folder isn't currently open.
|
||||
*/
|
||||
export async function findEnvFiles(workspaceRoot: string): Promise<string[]> {
|
||||
const folder = workspace.workspaceFolders?.find(f => f.uri.fsPath === workspaceRoot)
|
||||
if (!folder)
|
||||
|
||||
@@ -15,14 +15,18 @@
|
||||
import { execFile } from 'node:child_process'
|
||||
|
||||
export interface BranchSyncStatus {
|
||||
/** Commits remote `autoBuildBranch` is behind remote `devBranch`. 0 when
|
||||
/**
|
||||
* Commits remote `autoBuildBranch` is behind remote `devBranch`. 0 when
|
||||
* already in sync, or when `unavailable` (the field is still numeric so
|
||||
* callers don't have to special-case it). */
|
||||
* callers don't have to special-case it).
|
||||
*/
|
||||
behind: number
|
||||
devBranch: string
|
||||
autoBuildBranch: string
|
||||
/** True when sync is structurally impossible (same branch, missing remote
|
||||
* ref, fetch failed, not a git repo, …). Disables the toolbar button. */
|
||||
/**
|
||||
* True when sync is structurally impossible (same branch, missing remote
|
||||
* ref, fetch failed, not a git repo, …). Disables the toolbar button.
|
||||
*/
|
||||
unavailable?: boolean
|
||||
/** Human-readable explanation, suitable for the button's tooltip. */
|
||||
reason?: string
|
||||
|
||||
@@ -34,8 +34,10 @@ export interface HookContext {
|
||||
branch: string
|
||||
issueNumber: number
|
||||
mainBranch: string
|
||||
/** User-configured override path. Empty / undefined → use the default
|
||||
* `<workspaceRoot>/.spx/worktree-*.sh` location. */
|
||||
/**
|
||||
* User-configured override path. Empty / undefined → use the default
|
||||
* `<workspaceRoot>/.spx/worktree-*.sh` location.
|
||||
*/
|
||||
customScriptPath?: string
|
||||
}
|
||||
|
||||
|
||||
+56
-28
@@ -8,26 +8,36 @@
|
||||
export type IssueColumn = 'todo' | 'in-progress' | 'review' | 'done'
|
||||
|
||||
export interface Issue {
|
||||
/** Stable identifier: `${owner}/${repo}#${index}` (gitea) or
|
||||
/**
|
||||
* Stable identifier: `${owner}/${repo}#${index}` (gitea) or
|
||||
* `youtrack:${idReadable}` (youtrack). Used to route mutations to the right
|
||||
* source when both kinds of cards share the board. */
|
||||
* source when both kinds of cards share the board.
|
||||
*/
|
||||
id: string
|
||||
/** Which tracker this issue came from. Absent is treated as `'gitea'` for
|
||||
* backward compatibility with state written before the second source. */
|
||||
/**
|
||||
* Which tracker this issue came from. Absent is treated as `'gitea'` for
|
||||
* backward compatibility with state written before the second source.
|
||||
*/
|
||||
source?: 'gitea' | 'youtrack'
|
||||
/** YouTrack readable id (`LXF-12`) — present only on youtrack issues; needed
|
||||
* for REST calls that key by readable id. */
|
||||
/**
|
||||
* YouTrack readable id (`LXF-12`) — present only on youtrack issues; needed
|
||||
* for REST calls that key by readable id.
|
||||
*/
|
||||
externalId?: string
|
||||
/** Equals the Gitea issue number / tea's `index` field; for youtrack it's the
|
||||
/**
|
||||
* Equals the Gitea issue number / tea's `index` field; for youtrack it's the
|
||||
* numeric tail of `externalId` (collides across sources — never use alone as
|
||||
* a key, pair with `source`). */
|
||||
* a key, pair with `source`).
|
||||
*/
|
||||
number: number
|
||||
title: string
|
||||
column: IssueColumn
|
||||
/** YouTrack attachments (name + absolute URL). Links only; not downloaded. */
|
||||
attachments?: Array<{ name: string, url: string }>
|
||||
/** Optional Claude Code session id stored alongside the column marker in
|
||||
* the issue's state-JSON comment. Used to resume the conversation. */
|
||||
/**
|
||||
* Optional Claude Code session id stored alongside the column marker in
|
||||
* the issue's state-JSON comment. Used to resume the conversation.
|
||||
*/
|
||||
sessionId?: string
|
||||
/** 实施会话专用的 Claude 配置文件;空 = DEFAULT_PROFILE_PATH,不回退其它字段。 */
|
||||
profilePath?: string
|
||||
@@ -35,21 +45,27 @@ export interface Issue {
|
||||
brainstormProfilePath?: string
|
||||
/** 测试会话专用的 Claude 配置文件;空 = DEFAULT_PROFILE_PATH,不回退其它字段。 */
|
||||
testProfilePath?: string
|
||||
/** Optional workspace-relative path to the spec file for this issue, as
|
||||
/**
|
||||
* Optional workspace-relative path to the spec file for this issue, as
|
||||
* surfaced from the Claude session transcript. Lives under
|
||||
* `docs/superpowers/specs/*.md`. */
|
||||
* `docs/superpowers/specs/*.md`.
|
||||
*/
|
||||
specFile?: string
|
||||
/** Optional workspace-relative path to the plan file for this issue, as
|
||||
/**
|
||||
* Optional workspace-relative path to the plan file for this issue, as
|
||||
* surfaced from the Claude session transcript. Lives under
|
||||
* `docs/superpowers/plans/*.md`. */
|
||||
* `docs/superpowers/plans/*.md`.
|
||||
*/
|
||||
planFile?: string
|
||||
/** Optional workspace-relative path to the generated PR diff summary. */
|
||||
prDiffFile?: string
|
||||
/** PR number associated with this issue (set after webhook fires). */
|
||||
pr?: string
|
||||
/** Whether the associated PR has been merged. Set when the user drags to
|
||||
/**
|
||||
* Whether the associated PR has been merged. Set when the user drags to
|
||||
* the 完成 列 (扩展代为合并) or when a `pull_request.closed` webhook arrives
|
||||
* with `merged === true`. Persisted in the state JSON. */
|
||||
* with `merged === true`. Persisted in the state JSON.
|
||||
*/
|
||||
prMerged?: boolean
|
||||
/** PR 合并时间(ISO 8601,来自 PR API 的 merged_at)。完成列按它降序排序,未合并为 undefined。合并时持久化进 state JSON;完成工单加载时不再实时拉 PR,直接读持久化值(缺失则按工单号回退排序)。 */
|
||||
prMergedAt?: string
|
||||
@@ -61,32 +77,44 @@ export interface Issue {
|
||||
worktreeExists?: boolean
|
||||
/** Lifecycle of the implementation flow. */
|
||||
implementStatus?: 'running' | 'done' | 'failed'
|
||||
/** Session id of the implementation conversation (separate from
|
||||
* discussion-session `sessionId`). */
|
||||
/**
|
||||
* Session id of the implementation conversation (separate from
|
||||
* discussion-session `sessionId`).
|
||||
*/
|
||||
implementSessionId?: string
|
||||
/** Backend-agnostic review session id (v1 stores a codex thread id). Set
|
||||
/**
|
||||
* Backend-agnostic review session id (v1 stores a codex thread id). Set
|
||||
* the first time auto-review runs; reused for subsequent `synchronize`
|
||||
* webhook callbacks via `codex exec resume`. */
|
||||
* webhook callbacks via `codex exec resume`.
|
||||
*/
|
||||
reviewSessionId?: string
|
||||
/** Whether the codex session file (`~/.codex/sessions/YYYY/MM/DD/rollout-...-<id>.jsonl`)
|
||||
/**
|
||||
* Whether the codex session file (`~/.codex/sessions/YYYY/MM/DD/rollout-...-<id>.jsonl`)
|
||||
* for `reviewSessionId` exists on this machine; computed on load, never
|
||||
* persisted. codex resume 只依赖这个文件,与 worktree 无关。 */
|
||||
* persisted. codex resume 只依赖这个文件,与 worktree 无关。
|
||||
*/
|
||||
reviewSessionFileExists?: boolean
|
||||
/** Session id of the test conversation (a Claude Code session id). Set the
|
||||
/**
|
||||
* Session id of the test conversation (a Claude Code session id). Set the
|
||||
* first time the user manually starts a test session after the issue's PR
|
||||
* is merged; reused for resume from the detail panel. */
|
||||
* is merged; reused for resume from the detail panel.
|
||||
*/
|
||||
testSessionId?: string
|
||||
/** Browser URL to the Gitea issue page (escape hatch button in the UI). */
|
||||
htmlUrl: string
|
||||
/** 前置任务 issue number (Gitea dependencies 取第一个) */
|
||||
prerequisite?: number
|
||||
/** Terminal/tab color id (a `terminal.ansi*` ThemeColor key) chosen the
|
||||
/**
|
||||
* Terminal/tab color id (a `terminal.ansi*` ThemeColor key) chosen the
|
||||
* first time a session is opened for this issue, persisted in the state
|
||||
* JSON so all subsequent sessions reuse the same tone. */
|
||||
* JSON so all subsequent sessions reuse the same tone.
|
||||
*/
|
||||
color?: string
|
||||
/** Per-issue override of the global `autoReview` setting. When set, the
|
||||
/**
|
||||
* Per-issue override of the global `autoReview` setting. When set, the
|
||||
* webhook coordinator uses this value instead of the global flag. Stored
|
||||
* in the issue's state-JSON comment so it survives reloads. */
|
||||
* in the issue's state-JSON comment so it survives reloads.
|
||||
*/
|
||||
autoReview?: boolean
|
||||
/** Gitea assignee login 列表;接管按钮只对 assignee 含当前登录者的工单显示。 */
|
||||
assignees?: string[]
|
||||
|
||||
@@ -46,13 +46,13 @@ class Logger {
|
||||
|
||||
const line = `[${full.source}] ${full.message}`
|
||||
// Mirror to Extension Host console for parity with existing console.warn/error.
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
if (full.level === 'error')
|
||||
console.error(line, full.details ?? '')
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
else if (full.level === 'warn')
|
||||
console.warn(line, full.details ?? '')
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
else
|
||||
console.log(line, full.details ?? '')
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@ import type { KanbanWebviewPanel } from '../KanbanPanel'
|
||||
import { promises as fsp } from 'node:fs'
|
||||
import { window, workspace } from 'vscode'
|
||||
import { buildCcCommand } from '../../cc/ccCommand'
|
||||
import { resolveProfilePath } from '../../cc/profiles'
|
||||
import { pollForNewSession, projectsDirFor } from '../../cc/sessionWatcher'
|
||||
import { logger } from '../../logging/logger'
|
||||
import { listImportableNamedSessions } from '../../sessions/importableNamedSessions'
|
||||
import { decideManagedResumeProfile } from '../../sessions/managedResumeProfile'
|
||||
import { readManagedSessions, writeManagedSessions } from '../../sessions/managedStore'
|
||||
import { inferProfilePathForSession } from '../../sessions/sessionProfileInference'
|
||||
import { resolveProfilePath } from '../../cc/profiles'
|
||||
import { resolveTerminalLocation } from './terminals'
|
||||
|
||||
/** 默认会话名:有 prompt 取前 20 字符,否则用短 id(前 8 位)。 */
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
*/
|
||||
|
||||
import type { IssueColumn } from '../../gitea/types'
|
||||
import type { KanbanWebviewPanel } from '../KanbanPanel'
|
||||
import type { YouTrackAuth } from '../../youtrack/api'
|
||||
import type { KanbanWebviewPanel } from '../KanbanPanel'
|
||||
import { ThemeIcon, window, workspace } from 'vscode'
|
||||
import { getYouTrackToken } from '../../auth/secrets'
|
||||
import { logger } from '../../logging/logger'
|
||||
@@ -24,8 +24,10 @@ function toast(panel: KanbanWebviewPanel, level: 'info' | 'success' | 'error', m
|
||||
panel.postMessage({ type: 'toast/show', id: makeNonce(), level, message, dismissOnTimer: 6000 })
|
||||
}
|
||||
|
||||
/** Resolve {baseUrl, token} from settings + SecretStorage, or null if YouTrack
|
||||
* isn't fully configured. */
|
||||
/**
|
||||
* Resolve {baseUrl, token} from settings + SecretStorage, or null if YouTrack
|
||||
* isn't fully configured.
|
||||
*/
|
||||
async function resolveAuth(panel: KanbanWebviewPanel): Promise<YouTrackAuth | null> {
|
||||
const baseUrl = getSettings(panel.context).youtrackBaseUrl.trim()
|
||||
if (!baseUrl)
|
||||
@@ -61,8 +63,10 @@ export async function handleYouTrackColumnChange(panel: KanbanWebviewPanel, exte
|
||||
await panel.loadAndPush()
|
||||
}
|
||||
|
||||
/** Resolve the issue in YouTrack. Uses the configured close command, else
|
||||
* auto-detects the project's `isResolved` state value. */
|
||||
/**
|
||||
* Resolve the issue in YouTrack. Uses the configured close command, else
|
||||
* auto-detects the project's `isResolved` state value.
|
||||
*/
|
||||
async function closeYouTrackIssue(panel: KanbanWebviewPanel, auth: YouTrackAuth, externalId: string): Promise<void> {
|
||||
const configured = getSettings(panel.context).youtrackCloseCommand.trim()
|
||||
const command = configured || (await resolvedStateCommand(auth, externalId))
|
||||
@@ -74,8 +78,10 @@ async function closeYouTrackIssue(panel: KanbanWebviewPanel, auth: YouTrackAuth,
|
||||
await applyCommand(auth, externalId, command)
|
||||
}
|
||||
|
||||
/** Populate the settings project dropdown from the in-form base URL + token
|
||||
* (lets the user pick a project before saving). */
|
||||
/**
|
||||
* Populate the settings project dropdown from the in-form base URL + token
|
||||
* (lets the user pick a project before saving).
|
||||
*/
|
||||
export async function handleListProjects(panel: KanbanWebviewPanel, baseUrl: string, token: string): Promise<void> {
|
||||
const trimmedBase = baseUrl.trim()
|
||||
const trimmedToken = token.trim()
|
||||
|
||||
@@ -34,7 +34,7 @@ export function pickRandomIssueColor(): string {
|
||||
* `isNew` is true the caller is expected to persist the picked color back to
|
||||
* the issue's state JSON so subsequent sessions reuse it.
|
||||
*/
|
||||
export function resolveIssueColor(stored: string | undefined): { id: string; isNew: boolean } {
|
||||
export function resolveIssueColor(stored: string | undefined): { id: string, isNew: boolean } {
|
||||
if (stored && (PALETTE as readonly string[]).includes(stored))
|
||||
return { id: stored, isNew: false }
|
||||
return { id: pickRandomIssueColor(), isNew: true }
|
||||
|
||||
@@ -6,15 +6,15 @@ export function decideManagedResumeProfile(input: {
|
||||
const override = input.overrideProfile?.trim() || undefined
|
||||
const stored = input.storedProfile?.trim() || undefined
|
||||
|
||||
if (override) { // ① 行内覆盖:与 store 不同才写回;忽略 jsonl 推断
|
||||
if (override) { // ① 行内覆盖:与 store 不同才写回;忽略 jsonl 推断
|
||||
return {
|
||||
effective: override,
|
||||
persist: override !== stored ? override : undefined,
|
||||
}
|
||||
}
|
||||
if (stored) // ② store 已有 → 沿用,不写回
|
||||
if (stored) // ② store 已有 → 沿用,不写回
|
||||
return { effective: stored, persist: undefined }
|
||||
if (input.inferredProfile) // ③ 缺 profile 才用 jsonl 回填并写回
|
||||
if (input.inferredProfile) // ③ 缺 profile 才用 jsonl 回填并写回
|
||||
return { effective: input.inferredProfile, persist: input.inferredProfile }
|
||||
return { effective: undefined, persist: undefined }
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import * as os from 'node:os'
|
||||
import * as path from 'node:path'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
|
||||
@@ -52,8 +52,8 @@ function normalizeBase(baseUrl: string): string {
|
||||
|
||||
function authHeaders(token: string): Record<string, string> {
|
||||
return {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Accept': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
Accept: 'application/json',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
*/
|
||||
|
||||
import type { ExtensionContext } from 'vscode'
|
||||
import { workspace } from 'vscode'
|
||||
import type { Issue, IssueColumn } from '../gitea/types'
|
||||
import type { YouTrackIssue } from './api'
|
||||
import { workspace } from 'vscode'
|
||||
import { getYouTrackToken } from '../auth/secrets'
|
||||
import { getSettings } from '../settings/store'
|
||||
import type { YouTrackIssue } from './api'
|
||||
import { issueHtmlUrl, listIssues } from './api'
|
||||
import { readImportedIds } from './importStore'
|
||||
import { findLatestState } from './stateComment'
|
||||
|
||||
Reference in new issue
Block a user