style: auto-fix import/export sorting and formatting
Ran pnpm lint:fix to resolve 220 auto-fixable lint issues. All changes are import/export reordering — no logic changes.
This commit is contained in:
parent
f5d2b7e14f
commit
51f8f3545c
124 changed files with 672 additions and 552 deletions
|
|
@ -16,17 +16,17 @@
|
||||||
// On Windows this saturates all threads, blocking the event loop.
|
// On Windows this saturates all threads, blocking the event loop.
|
||||||
process.env.UV_THREADPOOL_SIZE ??= '16';
|
process.env.UV_THREADPOOL_SIZE ??= '16';
|
||||||
|
|
||||||
import { CrossTeamService } from '@main/services/team/CrossTeamService';
|
import { JsonScheduleRepository } from '@main/services/schedule/JsonScheduleRepository';
|
||||||
import { TeamConfigReader } from '@main/services/team/TeamConfigReader';
|
import { ScheduledTaskExecutor } from '@main/services/schedule/ScheduledTaskExecutor';
|
||||||
import { TeamInboxWriter } from '@main/services/team/TeamInboxWriter';
|
import { SchedulerService } from '@main/services/schedule/SchedulerService';
|
||||||
import { ChangeExtractorService } from '@main/services/team/ChangeExtractorService';
|
import { ChangeExtractorService } from '@main/services/team/ChangeExtractorService';
|
||||||
|
import { CrossTeamService } from '@main/services/team/CrossTeamService';
|
||||||
import { FileContentResolver } from '@main/services/team/FileContentResolver';
|
import { FileContentResolver } from '@main/services/team/FileContentResolver';
|
||||||
import { GitDiffFallback } from '@main/services/team/GitDiffFallback';
|
import { GitDiffFallback } from '@main/services/team/GitDiffFallback';
|
||||||
import { ReviewApplierService } from '@main/services/team/ReviewApplierService';
|
import { ReviewApplierService } from '@main/services/team/ReviewApplierService';
|
||||||
import { TeamBackupService } from '@main/services/team/TeamBackupService';
|
import { TeamBackupService } from '@main/services/team/TeamBackupService';
|
||||||
import { JsonScheduleRepository } from '@main/services/schedule/JsonScheduleRepository';
|
import { TeamConfigReader } from '@main/services/team/TeamConfigReader';
|
||||||
import { ScheduledTaskExecutor } from '@main/services/schedule/ScheduledTaskExecutor';
|
import { TeamInboxWriter } from '@main/services/team/TeamInboxWriter';
|
||||||
import { SchedulerService } from '@main/services/schedule/SchedulerService';
|
|
||||||
import {
|
import {
|
||||||
CONTEXT_CHANGED,
|
CONTEXT_CHANGED,
|
||||||
SCHEDULE_CHANGE,
|
SCHEDULE_CHANGE,
|
||||||
|
|
@ -51,16 +51,32 @@ import { existsSync } from 'fs';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
import { cleanupEditorState, setEditorMainWindow } from './ipc/editor';
|
import { cleanupEditorState, setEditorMainWindow } from './ipc/editor';
|
||||||
import { setReviewMainWindow } from './ipc/review';
|
|
||||||
import { initializeIpcHandlers, removeIpcHandlers } from './ipc/handlers';
|
import { initializeIpcHandlers, removeIpcHandlers } from './ipc/handlers';
|
||||||
|
import { setReviewMainWindow } from './ipc/review';
|
||||||
|
import {
|
||||||
|
ApiKeyService,
|
||||||
|
ExtensionFacadeService,
|
||||||
|
GlamaMcpEnrichmentService,
|
||||||
|
McpCatalogAggregator,
|
||||||
|
McpHealthDiagnosticsService,
|
||||||
|
McpInstallationStateService,
|
||||||
|
McpInstallService,
|
||||||
|
OfficialMcpRegistryService,
|
||||||
|
PluginCatalogService,
|
||||||
|
PluginInstallationStateService,
|
||||||
|
PluginInstallService,
|
||||||
|
SkillsCatalogService,
|
||||||
|
SkillsMutationService,
|
||||||
|
SkillsWatcherService,
|
||||||
|
} from './services/extensions';
|
||||||
import { startEventLoopLagMonitor } from './services/infrastructure/EventLoopLagMonitor';
|
import { startEventLoopLagMonitor } from './services/infrastructure/EventLoopLagMonitor';
|
||||||
import { HttpServer } from './services/infrastructure/HttpServer';
|
import { HttpServer } from './services/infrastructure/HttpServer';
|
||||||
import { TeamInboxReader } from './services/team/TeamInboxReader';
|
|
||||||
import {
|
import {
|
||||||
buildTeamControlApiBaseUrl,
|
buildTeamControlApiBaseUrl,
|
||||||
clearTeamControlApiState,
|
clearTeamControlApiState,
|
||||||
writeTeamControlApiState,
|
writeTeamControlApiState,
|
||||||
} from './services/team/TeamControlApiState';
|
} from './services/team/TeamControlApiState';
|
||||||
|
import { TeamInboxReader } from './services/team/TeamInboxReader';
|
||||||
import { TeamSentMessagesStore } from './services/team/TeamSentMessagesStore';
|
import { TeamSentMessagesStore } from './services/team/TeamSentMessagesStore';
|
||||||
import { getAppIconPath } from './utils/appIcon';
|
import { getAppIconPath } from './utils/appIcon';
|
||||||
import { getProjectsBasePath, getTeamsBasePath, getTodosBasePath } from './utils/pathDecoder';
|
import { getProjectsBasePath, getTeamsBasePath, getTodosBasePath } from './utils/pathDecoder';
|
||||||
|
|
@ -80,22 +96,6 @@ import {
|
||||||
TeamProvisioningService,
|
TeamProvisioningService,
|
||||||
UpdaterService,
|
UpdaterService,
|
||||||
} from './services';
|
} from './services';
|
||||||
import {
|
|
||||||
ApiKeyService,
|
|
||||||
ExtensionFacadeService,
|
|
||||||
GlamaMcpEnrichmentService,
|
|
||||||
McpCatalogAggregator,
|
|
||||||
McpHealthDiagnosticsService,
|
|
||||||
McpInstallationStateService,
|
|
||||||
McpInstallService,
|
|
||||||
OfficialMcpRegistryService,
|
|
||||||
PluginCatalogService,
|
|
||||||
PluginInstallationStateService,
|
|
||||||
PluginInstallService,
|
|
||||||
SkillsCatalogService,
|
|
||||||
SkillsMutationService,
|
|
||||||
SkillsWatcherService,
|
|
||||||
} from './services/extensions';
|
|
||||||
|
|
||||||
import type { FileChangeEvent } from '@main/types';
|
import type { FileChangeEvent } from '@main/types';
|
||||||
import type { TeamChangeEvent } from '@shared/types';
|
import type { TeamChangeEvent } from '@shared/types';
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,12 @@ import {
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
|
|
||||||
import { isAgentActionMode } from '../services/team/actionModeInstructions';
|
import { isAgentActionMode } from '../services/team/actionModeInstructions';
|
||||||
|
|
||||||
import { validateTaskId, validateTeamName } from './guards';
|
import { validateTaskId, validateTeamName } from './guards';
|
||||||
|
|
||||||
import type { CrossTeamService } from '../services/team/CrossTeamService';
|
import type { CrossTeamService } from '../services/team/CrossTeamService';
|
||||||
import type { IpcMain, IpcMainInvokeEvent } from 'electron';
|
|
||||||
import type { IpcResult, TaskRef } from '@shared/types';
|
import type { IpcResult, TaskRef } from '@shared/types';
|
||||||
|
import type { IpcMain, IpcMainInvokeEvent } from 'electron';
|
||||||
|
|
||||||
const logger = createLogger('IPC:crossTeam');
|
const logger = createLogger('IPC:crossTeam');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,30 +6,6 @@
|
||||||
* Phase 5: install/uninstall mutations.
|
* Phase 5: install/uninstall mutations.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createLogger } from '@shared/utils/logger';
|
|
||||||
import type {
|
|
||||||
ApiKeyEntry,
|
|
||||||
ApiKeyLookupResult,
|
|
||||||
ApiKeySaveRequest,
|
|
||||||
ApiKeyStorageStatus,
|
|
||||||
EnrichedPlugin,
|
|
||||||
InstalledMcpEntry,
|
|
||||||
McpCatalogItem,
|
|
||||||
McpCustomInstallRequest,
|
|
||||||
McpInstallRequest,
|
|
||||||
McpServerDiagnostic,
|
|
||||||
McpSearchResult,
|
|
||||||
OperationResult,
|
|
||||||
PluginInstallRequest,
|
|
||||||
} from '@shared/types/extensions';
|
|
||||||
import type { IpcMain, IpcMainInvokeEvent } from 'electron';
|
|
||||||
|
|
||||||
import type { ExtensionFacadeService } from '../services/extensions/ExtensionFacadeService';
|
|
||||||
import type { PluginInstallService } from '../services/extensions/install/PluginInstallService';
|
|
||||||
import type { McpInstallService } from '../services/extensions/install/McpInstallService';
|
|
||||||
import type { ApiKeyService } from '../services/extensions/apikeys/ApiKeyService';
|
|
||||||
import type { McpHealthDiagnosticsService } from '../services/extensions/state/McpHealthDiagnosticsService';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
API_KEYS_DELETE,
|
API_KEYS_DELETE,
|
||||||
API_KEYS_LIST,
|
API_KEYS_LIST,
|
||||||
|
|
@ -50,9 +26,32 @@ import {
|
||||||
PLUGIN_INSTALL,
|
PLUGIN_INSTALL,
|
||||||
PLUGIN_UNINSTALL,
|
PLUGIN_UNINSTALL,
|
||||||
} from '@preload/constants/ipcChannels';
|
} from '@preload/constants/ipcChannels';
|
||||||
|
import { createLogger } from '@shared/utils/logger';
|
||||||
|
|
||||||
import { GitHubStarsService } from '../services/extensions/catalog/GitHubStarsService';
|
import { GitHubStarsService } from '../services/extensions/catalog/GitHubStarsService';
|
||||||
|
|
||||||
|
import type { ApiKeyService } from '../services/extensions/apikeys/ApiKeyService';
|
||||||
|
import type { ExtensionFacadeService } from '../services/extensions/ExtensionFacadeService';
|
||||||
|
import type { McpInstallService } from '../services/extensions/install/McpInstallService';
|
||||||
|
import type { PluginInstallService } from '../services/extensions/install/PluginInstallService';
|
||||||
|
import type { McpHealthDiagnosticsService } from '../services/extensions/state/McpHealthDiagnosticsService';
|
||||||
|
import type {
|
||||||
|
ApiKeyEntry,
|
||||||
|
ApiKeyLookupResult,
|
||||||
|
ApiKeySaveRequest,
|
||||||
|
ApiKeyStorageStatus,
|
||||||
|
EnrichedPlugin,
|
||||||
|
InstalledMcpEntry,
|
||||||
|
McpCatalogItem,
|
||||||
|
McpCustomInstallRequest,
|
||||||
|
McpInstallRequest,
|
||||||
|
McpSearchResult,
|
||||||
|
McpServerDiagnostic,
|
||||||
|
OperationResult,
|
||||||
|
PluginInstallRequest,
|
||||||
|
} from '@shared/types/extensions';
|
||||||
|
import type { IpcMain, IpcMainInvokeEvent } from 'electron';
|
||||||
|
|
||||||
const logger = createLogger('IPC:extensions');
|
const logger = createLogger('IPC:extensions');
|
||||||
|
|
||||||
/** Allowed scope values */
|
/** Allowed scope values */
|
||||||
|
|
|
||||||
|
|
@ -65,8 +65,8 @@ import {
|
||||||
registerSessionHandlers,
|
registerSessionHandlers,
|
||||||
removeSessionHandlers,
|
removeSessionHandlers,
|
||||||
} from './sessions';
|
} from './sessions';
|
||||||
import { initializeSshHandlers, registerSshHandlers, removeSshHandlers } from './ssh';
|
|
||||||
import { initializeSkillsHandlers, registerSkillsHandlers, removeSkillsHandlers } from './skills';
|
import { initializeSkillsHandlers, registerSkillsHandlers, removeSkillsHandlers } from './skills';
|
||||||
|
import { initializeSshHandlers, registerSshHandlers, removeSshHandlers } from './ssh';
|
||||||
import {
|
import {
|
||||||
initializeSubagentHandlers,
|
initializeSubagentHandlers,
|
||||||
registerSubagentHandlers,
|
registerSubagentHandlers,
|
||||||
|
|
@ -103,18 +103,18 @@ import type {
|
||||||
TeamProvisioningService,
|
TeamProvisioningService,
|
||||||
UpdaterService,
|
UpdaterService,
|
||||||
} from '../services';
|
} from '../services';
|
||||||
import type { HttpServer } from '../services/infrastructure/HttpServer';
|
import type { ApiKeyService } from '../services/extensions/apikeys/ApiKeyService';
|
||||||
import type { CrossTeamService } from '../services/team/CrossTeamService';
|
|
||||||
import type { TeamBackupService } from '../services/team/TeamBackupService';
|
|
||||||
import type { ExtensionFacadeService } from '../services/extensions/ExtensionFacadeService';
|
import type { ExtensionFacadeService } from '../services/extensions/ExtensionFacadeService';
|
||||||
import type { McpInstallService } from '../services/extensions/install/McpInstallService';
|
import type { McpInstallService } from '../services/extensions/install/McpInstallService';
|
||||||
import type { PluginInstallService } from '../services/extensions/install/PluginInstallService';
|
import type { PluginInstallService } from '../services/extensions/install/PluginInstallService';
|
||||||
import type { ApiKeyService } from '../services/extensions/apikeys/ApiKeyService';
|
|
||||||
import type { McpHealthDiagnosticsService } from '../services/extensions/state/McpHealthDiagnosticsService';
|
|
||||||
import type { SkillsCatalogService } from '../services/extensions/skills/SkillsCatalogService';
|
import type { SkillsCatalogService } from '../services/extensions/skills/SkillsCatalogService';
|
||||||
import type { SkillsMutationService } from '../services/extensions/skills/SkillsMutationService';
|
import type { SkillsMutationService } from '../services/extensions/skills/SkillsMutationService';
|
||||||
import type { SkillsWatcherService } from '../services/extensions/skills/SkillsWatcherService';
|
import type { SkillsWatcherService } from '../services/extensions/skills/SkillsWatcherService';
|
||||||
|
import type { McpHealthDiagnosticsService } from '../services/extensions/state/McpHealthDiagnosticsService';
|
||||||
|
import type { HttpServer } from '../services/infrastructure/HttpServer';
|
||||||
import type { SchedulerService } from '../services/schedule/SchedulerService';
|
import type { SchedulerService } from '../services/schedule/SchedulerService';
|
||||||
|
import type { CrossTeamService } from '../services/team/CrossTeamService';
|
||||||
|
import type { TeamBackupService } from '../services/team/TeamBackupService';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes IPC handlers with service registry.
|
* Initializes IPC handlers with service registry.
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,3 @@
|
||||||
import { createLogger } from '@shared/utils/logger';
|
|
||||||
import type {
|
|
||||||
SkillCatalogItem,
|
|
||||||
SkillDeleteRequest,
|
|
||||||
SkillDetail,
|
|
||||||
SkillImportRequest,
|
|
||||||
SkillReviewPreview,
|
|
||||||
SkillUpsertRequest,
|
|
||||||
} from '@shared/types/extensions';
|
|
||||||
import type { IpcMain, IpcMainInvokeEvent } from 'electron';
|
|
||||||
|
|
||||||
import type { SkillsCatalogService } from '../services/extensions/skills/SkillsCatalogService';
|
|
||||||
import type { SkillsMutationService } from '../services/extensions/skills/SkillsMutationService';
|
|
||||||
import type { SkillsWatcherService } from '../services/extensions/skills/SkillsWatcherService';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SKILLS_APPLY_IMPORT,
|
SKILLS_APPLY_IMPORT,
|
||||||
SKILLS_APPLY_UPSERT,
|
SKILLS_APPLY_UPSERT,
|
||||||
|
|
@ -24,6 +9,20 @@ import {
|
||||||
SKILLS_START_WATCHING,
|
SKILLS_START_WATCHING,
|
||||||
SKILLS_STOP_WATCHING,
|
SKILLS_STOP_WATCHING,
|
||||||
} from '@preload/constants/ipcChannels';
|
} from '@preload/constants/ipcChannels';
|
||||||
|
import { createLogger } from '@shared/utils/logger';
|
||||||
|
|
||||||
|
import type { SkillsCatalogService } from '../services/extensions/skills/SkillsCatalogService';
|
||||||
|
import type { SkillsMutationService } from '../services/extensions/skills/SkillsMutationService';
|
||||||
|
import type { SkillsWatcherService } from '../services/extensions/skills/SkillsWatcherService';
|
||||||
|
import type {
|
||||||
|
SkillCatalogItem,
|
||||||
|
SkillDeleteRequest,
|
||||||
|
SkillDetail,
|
||||||
|
SkillImportRequest,
|
||||||
|
SkillReviewPreview,
|
||||||
|
SkillUpsertRequest,
|
||||||
|
} from '@shared/types/extensions';
|
||||||
|
import type { IpcMain, IpcMainInvokeEvent } from 'electron';
|
||||||
|
|
||||||
const logger = createLogger('IPC:skills');
|
const logger = createLogger('IPC:skills');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import crypto from 'crypto';
|
|
||||||
|
|
||||||
import { setCurrentMainOp } from '@main/services/infrastructure/EventLoopLagMonitor';
|
import { setCurrentMainOp } from '@main/services/infrastructure/EventLoopLagMonitor';
|
||||||
import { getAppIconPath } from '@main/utils/appIcon';
|
import { getAppIconPath } from '@main/utils/appIcon';
|
||||||
import { getAppDataPath } from '@main/utils/pathDecoder';
|
import { getAppDataPath } from '@main/utils/pathDecoder';
|
||||||
|
|
@ -29,8 +27,8 @@ import {
|
||||||
TEAM_LAUNCH,
|
TEAM_LAUNCH,
|
||||||
TEAM_LEAD_ACTIVITY,
|
TEAM_LEAD_ACTIVITY,
|
||||||
TEAM_LEAD_CONTEXT,
|
TEAM_LEAD_CONTEXT,
|
||||||
TEAM_MEMBER_SPAWN_STATUSES,
|
|
||||||
TEAM_LIST,
|
TEAM_LIST,
|
||||||
|
TEAM_MEMBER_SPAWN_STATUSES,
|
||||||
TEAM_PERMANENTLY_DELETE,
|
TEAM_PERMANENTLY_DELETE,
|
||||||
TEAM_PREPARE_PROVISIONING,
|
TEAM_PREPARE_PROVISIONING,
|
||||||
TEAM_PROCESS_ALIVE,
|
TEAM_PROCESS_ALIVE,
|
||||||
|
|
@ -72,17 +70,18 @@ import {
|
||||||
} from '@shared/utils/cliArgsParser';
|
} from '@shared/utils/cliArgsParser';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
import { isRateLimitMessage } from '@shared/utils/rateLimitDetector';
|
import { isRateLimitMessage } from '@shared/utils/rateLimitDetector';
|
||||||
|
import crypto from 'crypto';
|
||||||
import { BrowserWindow, type IpcMain, type IpcMainInvokeEvent, Notification } from 'electron';
|
import { BrowserWindow, type IpcMain, type IpcMainInvokeEvent, Notification } from 'electron';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { ConfigManager } from '../services/infrastructure/ConfigManager';
|
import { ConfigManager } from '../services/infrastructure/ConfigManager';
|
||||||
import { NotificationManager } from '../services/infrastructure/NotificationManager';
|
import { NotificationManager } from '../services/infrastructure/NotificationManager';
|
||||||
|
import { gitIdentityResolver } from '../services/parsing/GitIdentityResolver';
|
||||||
import {
|
import {
|
||||||
buildActionModeAgentBlock,
|
buildActionModeAgentBlock,
|
||||||
isAgentActionMode,
|
isAgentActionMode,
|
||||||
} from '../services/team/actionModeInstructions';
|
} from '../services/team/actionModeInstructions';
|
||||||
import { gitIdentityResolver } from '../services/parsing/GitIdentityResolver';
|
|
||||||
import { TeamAttachmentStore } from '../services/team/TeamAttachmentStore';
|
import { TeamAttachmentStore } from '../services/team/TeamAttachmentStore';
|
||||||
import { buildAddMemberSpawnMessage } from '../services/team/TeamProvisioningService';
|
import { buildAddMemberSpawnMessage } from '../services/team/TeamProvisioningService';
|
||||||
import { TeamTaskAttachmentStore } from '../services/team/TeamTaskAttachmentStore';
|
import { TeamTaskAttachmentStore } from '../services/team/TeamTaskAttachmentStore';
|
||||||
|
|
@ -113,13 +112,13 @@ import type {
|
||||||
GlobalTask,
|
GlobalTask,
|
||||||
IpcResult,
|
IpcResult,
|
||||||
KanbanColumnId,
|
KanbanColumnId,
|
||||||
LeadContextUsage,
|
|
||||||
LeadActivitySnapshot,
|
LeadActivitySnapshot,
|
||||||
|
LeadContextUsage,
|
||||||
LeadContextUsageSnapshot,
|
LeadContextUsageSnapshot,
|
||||||
MemberFullStats,
|
MemberFullStats,
|
||||||
MemberSpawnStatusesSnapshot,
|
|
||||||
MemberLogSummary,
|
MemberLogSummary,
|
||||||
MemberSpawnStatusEntry,
|
MemberSpawnStatusEntry,
|
||||||
|
MemberSpawnStatusesSnapshot,
|
||||||
SendMessageRequest,
|
SendMessageRequest,
|
||||||
SendMessageResult,
|
SendMessageResult,
|
||||||
TaskAttachmentMeta,
|
TaskAttachmentMeta,
|
||||||
|
|
@ -548,7 +547,10 @@ async function handlePermanentlyDeleteTeam(
|
||||||
.rm(path.join(appData, 'attachments', validated.value!), { recursive: true, force: true })
|
.rm(path.join(appData, 'attachments', validated.value!), { recursive: true, force: true })
|
||||||
.catch(() => undefined);
|
.catch(() => undefined);
|
||||||
await fs.promises
|
await fs.promises
|
||||||
.rm(path.join(appData, 'task-attachments', validated.value!), { recursive: true, force: true })
|
.rm(path.join(appData, 'task-attachments', validated.value!), {
|
||||||
|
recursive: true,
|
||||||
|
force: true,
|
||||||
|
})
|
||||||
.catch(() => undefined);
|
.catch(() => undefined);
|
||||||
// Mark in backup registry AFTER successful deletion
|
// Mark in backup registry AFTER successful deletion
|
||||||
if (teamBackupService) {
|
if (teamBackupService) {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
|
|
||||||
|
import { type McpCatalogAggregator } from './catalog/McpCatalogAggregator';
|
||||||
|
import { type PluginCatalogService } from './catalog/PluginCatalogService';
|
||||||
|
import { type McpInstallationStateService } from './state/McpInstallationStateService';
|
||||||
|
import { type PluginInstallationStateService } from './state/PluginInstallationStateService';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
EnrichedPlugin,
|
EnrichedPlugin,
|
||||||
InstalledMcpEntry,
|
InstalledMcpEntry,
|
||||||
|
|
@ -15,11 +21,6 @@ import type {
|
||||||
PluginCatalogItem,
|
PluginCatalogItem,
|
||||||
} from '@shared/types/extensions';
|
} from '@shared/types/extensions';
|
||||||
|
|
||||||
import { PluginCatalogService } from './catalog/PluginCatalogService';
|
|
||||||
import { McpCatalogAggregator } from './catalog/McpCatalogAggregator';
|
|
||||||
import { PluginInstallationStateService } from './state/PluginInstallationStateService';
|
|
||||||
import { McpInstallationStateService } from './state/McpInstallationStateService';
|
|
||||||
|
|
||||||
const logger = createLogger('Extensions:Facade');
|
const logger = createLogger('Extensions:Facade');
|
||||||
|
|
||||||
export class ExtensionFacadeService {
|
export class ExtensionFacadeService {
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,14 @@
|
||||||
* Storage file: ~/.claude/api-keys.json
|
* Storage file: ~/.claude/api-keys.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { safeStorage } from 'electron';
|
|
||||||
import fs from 'node:fs/promises';
|
|
||||||
import path from 'node:path';
|
|
||||||
import crypto from 'node:crypto';
|
import crypto from 'node:crypto';
|
||||||
|
import fs from 'node:fs/promises';
|
||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
|
import { safeStorage } from 'electron';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiKeyEntry,
|
ApiKeyEntry,
|
||||||
ApiKeyLookupResult,
|
ApiKeyLookupResult,
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@
|
||||||
|
|
||||||
import https from 'node:https';
|
import https from 'node:https';
|
||||||
|
|
||||||
import { createLogger } from '@shared/utils/logger';
|
|
||||||
import { parseGitHubOwnerRepo } from '@shared/utils/extensionNormalizers';
|
import { parseGitHubOwnerRepo } from '@shared/utils/extensionNormalizers';
|
||||||
|
import { createLogger } from '@shared/utils/logger';
|
||||||
|
|
||||||
const logger = createLogger('Extensions:GitHubStars');
|
const logger = createLogger('Extensions:GitHubStars');
|
||||||
|
|
||||||
|
|
@ -39,7 +39,7 @@ export class GitHubStarsService {
|
||||||
*/
|
*/
|
||||||
async fetchStars(repositoryUrls: string[]): Promise<Record<string, number>> {
|
async fetchStars(repositoryUrls: string[]): Promise<Record<string, number>> {
|
||||||
const result: Record<string, number> = {};
|
const result: Record<string, number> = {};
|
||||||
const tasks: Array<{ url: string; owner: string; repo: string }> = [];
|
const tasks: { url: string; owner: string; repo: string }[] = [];
|
||||||
|
|
||||||
for (const url of repositoryUrls) {
|
for (const url of repositoryUrls) {
|
||||||
const parsed = parseGitHubOwnerRepo(url);
|
const parsed = parseGitHubOwnerRepo(url);
|
||||||
|
|
@ -107,10 +107,10 @@ export class GitHubStarsService {
|
||||||
* Run async tasks with a concurrency limit.
|
* Run async tasks with a concurrency limit.
|
||||||
*/
|
*/
|
||||||
private async withConcurrencyLimit(
|
private async withConcurrencyLimit(
|
||||||
tasks: Array<() => Promise<void>>,
|
tasks: (() => Promise<void>)[],
|
||||||
limit: number
|
limit: number
|
||||||
): Promise<Array<'ok' | 'error'>> {
|
): Promise<('ok' | 'error')[]> {
|
||||||
const results: Array<'ok' | 'error'> = [];
|
const results: ('ok' | 'error')[] = [];
|
||||||
let index = 0;
|
let index = 0;
|
||||||
|
|
||||||
const run = async (): Promise<void> => {
|
const run = async (): Promise<void> => {
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,11 @@
|
||||||
* Cursor-based pagination (after), no auth required.
|
* Cursor-based pagination (after), no auth required.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import https from 'node:https';
|
|
||||||
import http from 'node:http';
|
import http from 'node:http';
|
||||||
|
import https from 'node:https';
|
||||||
|
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
|
|
||||||
import type { McpCatalogItem, McpHostingType, McpToolDef } from '@shared/types/extensions';
|
import type { McpCatalogItem, McpHostingType, McpToolDef } from '@shared/types/extensions';
|
||||||
|
|
||||||
const logger = createLogger('Extensions:GlamaMcp');
|
const logger = createLogger('Extensions:GlamaMcp');
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,13 @@
|
||||||
* - Provides getById() for secure install flow
|
* - Provides getById() for secure install flow
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createLogger } from '@shared/utils/logger';
|
|
||||||
import type { McpCatalogItem, McpSearchResult } from '@shared/types/extensions';
|
|
||||||
import { normalizeRepoUrl } from '@shared/utils/extensionNormalizers';
|
import { normalizeRepoUrl } from '@shared/utils/extensionNormalizers';
|
||||||
|
import { createLogger } from '@shared/utils/logger';
|
||||||
|
|
||||||
import { OfficialMcpRegistryService } from './OfficialMcpRegistryService';
|
import { type GlamaMcpEnrichmentService } from './GlamaMcpEnrichmentService';
|
||||||
import { GlamaMcpEnrichmentService } from './GlamaMcpEnrichmentService';
|
import { type OfficialMcpRegistryService } from './OfficialMcpRegistryService';
|
||||||
|
|
||||||
|
import type { McpCatalogItem, McpSearchResult } from '@shared/types/extensions';
|
||||||
|
|
||||||
const logger = createLogger('Extensions:McpAggregator');
|
const logger = createLogger('Extensions:McpAggregator');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,11 @@
|
||||||
* Filters for _meta.isLatest to pick only latest versions.
|
* Filters for _meta.isLatest to pick only latest versions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import https from 'node:https';
|
|
||||||
import http from 'node:http';
|
import http from 'node:http';
|
||||||
|
import https from 'node:https';
|
||||||
|
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
McpAuthHeaderDef,
|
McpAuthHeaderDef,
|
||||||
McpCatalogItem,
|
McpCatalogItem,
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,13 @@
|
||||||
* - Deduplicates concurrent requests
|
* - Deduplicates concurrent requests
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import https from 'node:https';
|
|
||||||
import http from 'node:http';
|
import http from 'node:http';
|
||||||
|
import https from 'node:https';
|
||||||
|
|
||||||
import { createLogger } from '@shared/utils/logger';
|
|
||||||
import type { PluginCatalogItem } from '@shared/types/extensions';
|
|
||||||
import { buildPluginId } from '@shared/utils/extensionNormalizers';
|
import { buildPluginId } from '@shared/utils/extensionNormalizers';
|
||||||
|
import { createLogger } from '@shared/utils/logger';
|
||||||
|
|
||||||
|
import type { PluginCatalogItem } from '@shared/types/extensions';
|
||||||
|
|
||||||
const logger = createLogger('Extensions:PluginCatalog');
|
const logger = createLogger('Extensions:PluginCatalog');
|
||||||
|
|
||||||
|
|
@ -260,7 +261,7 @@ export class PluginCatalogService {
|
||||||
|
|
||||||
const json = JSON.parse(response.body) as MarketplaceJson;
|
const json = JSON.parse(response.body) as MarketplaceJson;
|
||||||
const items = this.parseMarketplace(json);
|
const items = this.parseMarketplace(json);
|
||||||
const etag = (response.headers['etag'] as string) ?? null;
|
const etag = (response.headers.etag as string) ?? null;
|
||||||
|
|
||||||
this.cache = { items, etag, fetchedAt: Date.now() };
|
this.cache = { items, etag, fetchedAt: Date.now() };
|
||||||
logger.info(`Fetched ${items.length} plugins from marketplace "${json.name}"`);
|
logger.info(`Fetched ${items.length} plugins from marketplace "${json.name}"`);
|
||||||
|
|
@ -311,7 +312,7 @@ export class PluginCatalogService {
|
||||||
* e.g. https://github.com/org/repo → https://raw.githubusercontent.com/org/repo/main/README.md
|
* e.g. https://github.com/org/repo → https://raw.githubusercontent.com/org/repo/main/README.md
|
||||||
*/
|
*/
|
||||||
private buildReadmeUrl(repoUrl: string): string | null {
|
private buildReadmeUrl(repoUrl: string): string | null {
|
||||||
const match = repoUrl.match(/github\.com\/([^/]+)\/([^/]+)/);
|
const match = /github\.com\/([^/]+)\/([^/]+)/.exec(repoUrl);
|
||||||
if (!match) return null;
|
if (!match) return null;
|
||||||
const [, owner, repo] = match;
|
const [, owner, repo] = match;
|
||||||
return `https://raw.githubusercontent.com/${owner}/${repo}/main/README.md`;
|
return `https://raw.githubusercontent.com/${owner}/${repo}/main/README.md`;
|
||||||
|
|
|
||||||
|
|
@ -2,26 +2,26 @@
|
||||||
* Extension services barrel export.
|
* Extension services barrel export.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { PluginCatalogService } from './catalog/PluginCatalogService';
|
|
||||||
export { OfficialMcpRegistryService } from './catalog/OfficialMcpRegistryService';
|
|
||||||
export { GlamaMcpEnrichmentService } from './catalog/GlamaMcpEnrichmentService';
|
|
||||||
export { McpCatalogAggregator } from './catalog/McpCatalogAggregator';
|
|
||||||
export { PluginInstallationStateService } from './state/PluginInstallationStateService';
|
|
||||||
export { McpInstallationStateService } from './state/McpInstallationStateService';
|
|
||||||
export { McpHealthDiagnosticsService } from './state/McpHealthDiagnosticsService';
|
|
||||||
export { ExtensionFacadeService } from './ExtensionFacadeService';
|
|
||||||
export { PluginInstallService } from './install/PluginInstallService';
|
|
||||||
export { McpInstallService } from './install/McpInstallService';
|
|
||||||
export { ApiKeyService } from './apikeys/ApiKeyService';
|
export { ApiKeyService } from './apikeys/ApiKeyService';
|
||||||
export { GitHubStarsService } from './catalog/GitHubStarsService';
|
export { GitHubStarsService } from './catalog/GitHubStarsService';
|
||||||
export { SkillRootsResolver } from './skills/SkillRootsResolver';
|
export { GlamaMcpEnrichmentService } from './catalog/GlamaMcpEnrichmentService';
|
||||||
export { SkillScanner } from './skills/SkillScanner';
|
export { McpCatalogAggregator } from './catalog/McpCatalogAggregator';
|
||||||
export { SkillMetadataParser } from './skills/SkillMetadataParser';
|
export { OfficialMcpRegistryService } from './catalog/OfficialMcpRegistryService';
|
||||||
export { SkillValidator } from './skills/SkillValidator';
|
export { PluginCatalogService } from './catalog/PluginCatalogService';
|
||||||
export { SkillsCatalogService } from './skills/SkillsCatalogService';
|
export { ExtensionFacadeService } from './ExtensionFacadeService';
|
||||||
export { SkillScaffoldService } from './skills/SkillScaffoldService';
|
export { McpInstallService } from './install/McpInstallService';
|
||||||
|
export { PluginInstallService } from './install/PluginInstallService';
|
||||||
export { SkillImportService } from './skills/SkillImportService';
|
export { SkillImportService } from './skills/SkillImportService';
|
||||||
|
export { SkillMetadataParser } from './skills/SkillMetadataParser';
|
||||||
export { SkillPlanService } from './skills/SkillPlanService';
|
export { SkillPlanService } from './skills/SkillPlanService';
|
||||||
export { SkillReviewService } from './skills/SkillReviewService';
|
export { SkillReviewService } from './skills/SkillReviewService';
|
||||||
|
export { SkillRootsResolver } from './skills/SkillRootsResolver';
|
||||||
|
export { SkillScaffoldService } from './skills/SkillScaffoldService';
|
||||||
|
export { SkillScanner } from './skills/SkillScanner';
|
||||||
|
export { SkillsCatalogService } from './skills/SkillsCatalogService';
|
||||||
export { SkillsMutationService } from './skills/SkillsMutationService';
|
export { SkillsMutationService } from './skills/SkillsMutationService';
|
||||||
export { SkillsWatcherService } from './skills/SkillsWatcherService';
|
export { SkillsWatcherService } from './skills/SkillsWatcherService';
|
||||||
|
export { SkillValidator } from './skills/SkillValidator';
|
||||||
|
export { McpHealthDiagnosticsService } from './state/McpHealthDiagnosticsService';
|
||||||
|
export { McpInstallationStateService } from './state/McpInstallationStateService';
|
||||||
|
export { PluginInstallationStateService } from './state/PluginInstallationStateService';
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@
|
||||||
import { execCli } from '@main/utils/childProcess';
|
import { execCli } from '@main/utils/childProcess';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
|
|
||||||
|
import type { McpCatalogAggregator } from '../catalog/McpCatalogAggregator';
|
||||||
import type {
|
import type {
|
||||||
McpCustomInstallRequest,
|
McpCustomInstallRequest,
|
||||||
McpInstallRequest,
|
McpInstallRequest,
|
||||||
OperationResult,
|
OperationResult,
|
||||||
} from '@shared/types/extensions';
|
} from '@shared/types/extensions';
|
||||||
import type { McpCatalogAggregator } from '../catalog/McpCatalogAggregator';
|
|
||||||
|
|
||||||
const logger = createLogger('Extensions:McpInstall');
|
const logger = createLogger('Extensions:McpInstall');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@
|
||||||
import { execCli } from '@main/utils/childProcess';
|
import { execCli } from '@main/utils/childProcess';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
|
|
||||||
import type { OperationResult, PluginInstallRequest } from '@shared/types/extensions';
|
|
||||||
import type { PluginCatalogService } from '../catalog/PluginCatalogService';
|
import type { PluginCatalogService } from '../catalog/PluginCatalogService';
|
||||||
|
import type { OperationResult, PluginInstallRequest } from '@shared/types/extensions';
|
||||||
|
|
||||||
const logger = createLogger('Extensions:PluginInstall');
|
const logger = createLogger('Extensions:PluginInstall');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -101,13 +101,11 @@ export class SkillImportService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async walkDirectory(
|
private async walkDirectory(rootDir: string): Promise<{
|
||||||
rootDir: string
|
files: { absolutePath: string; relativePath: string }[];
|
||||||
): Promise<{
|
|
||||||
files: Array<{ absolutePath: string; relativePath: string }>;
|
|
||||||
hiddenEntriesSkipped: number;
|
hiddenEntriesSkipped: number;
|
||||||
}> {
|
}> {
|
||||||
const allFiles: Array<{ absolutePath: string; relativePath: string }> = [];
|
const allFiles: { absolutePath: string; relativePath: string }[] = [];
|
||||||
let hiddenEntriesSkipped = 0;
|
let hiddenEntriesSkipped = 0;
|
||||||
let totalBytes = 0;
|
let totalBytes = 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
|
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
|
import YAML from 'yaml';
|
||||||
|
|
||||||
|
import type { ResolvedSkillRoot } from './SkillRootsResolver';
|
||||||
import type {
|
import type {
|
||||||
SkillCatalogItem,
|
SkillCatalogItem,
|
||||||
SkillDetail,
|
SkillDetail,
|
||||||
|
|
@ -8,9 +11,6 @@ import type {
|
||||||
SkillInvocationMode,
|
SkillInvocationMode,
|
||||||
SkillValidationIssue,
|
SkillValidationIssue,
|
||||||
} from '@shared/types/extensions';
|
} from '@shared/types/extensions';
|
||||||
import YAML from 'yaml';
|
|
||||||
|
|
||||||
import type { ResolvedSkillRoot } from './SkillRootsResolver';
|
|
||||||
|
|
||||||
const logger = createLogger('Extensions:SkillParser');
|
const logger = createLogger('Extensions:SkillParser');
|
||||||
|
|
||||||
|
|
@ -203,7 +203,7 @@ export class SkillMetadataParser {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/u);
|
const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/u.exec(content);
|
||||||
if (!match) {
|
if (!match) {
|
||||||
return {
|
return {
|
||||||
rawFrontmatter: null,
|
rawFrontmatter: null,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
|
import { createHash } from 'node:crypto';
|
||||||
import * as fs from 'node:fs/promises';
|
import * as fs from 'node:fs/promises';
|
||||||
import * as os from 'node:os';
|
import * as os from 'node:os';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
import { createHash } from 'node:crypto';
|
|
||||||
|
|
||||||
|
import { SkillScanner } from './SkillScanner';
|
||||||
|
|
||||||
|
import type { ImportedSkillSourceFile } from './SkillImportService';
|
||||||
import type {
|
import type {
|
||||||
SkillDraftFile,
|
SkillDraftFile,
|
||||||
SkillReviewFileChange,
|
SkillReviewFileChange,
|
||||||
|
|
@ -10,10 +13,6 @@ import type {
|
||||||
SkillReviewSummary,
|
SkillReviewSummary,
|
||||||
} from '@shared/types/extensions';
|
} from '@shared/types/extensions';
|
||||||
|
|
||||||
import type { ImportedSkillSourceFile } from './SkillImportService';
|
|
||||||
|
|
||||||
import { SkillScanner } from './SkillScanner';
|
|
||||||
|
|
||||||
type SkillPlanInputFile =
|
type SkillPlanInputFile =
|
||||||
| { relativePath: string; isBinary: false; content: string }
|
| { relativePath: string; isBinary: false; content: string }
|
||||||
| { relativePath: string; isBinary: true; sourceAbsolutePath: string };
|
| { relativePath: string; isBinary: true; sourceAbsolutePath: string };
|
||||||
|
|
@ -73,7 +72,7 @@ export class SkillPlanService {
|
||||||
async applyPlan(plan: SkillExecutionPlan): Promise<void> {
|
async applyPlan(plan: SkillExecutionPlan): Promise<void> {
|
||||||
const backupRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'skill-plan-backup-'));
|
const backupRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'skill-plan-backup-'));
|
||||||
const createdPaths: string[] = [];
|
const createdPaths: string[] = [];
|
||||||
const backups: Array<{ absolutePath: string; backupPath: string }> = [];
|
const backups: { absolutePath: string; backupPath: string }[] = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (const [index, change] of plan.changes.entries()) {
|
for (const [index, change] of plan.changes.entries()) {
|
||||||
|
|
@ -200,7 +199,7 @@ export class SkillPlanService {
|
||||||
|
|
||||||
const summary = changes.reduce<SkillReviewSummary>(
|
const summary = changes.reduce<SkillReviewSummary>(
|
||||||
(acc, change) => {
|
(acc, change) => {
|
||||||
acc[`${change.action}d` as 'created' | 'updated' | 'deleted'] += 1;
|
acc[`${change.action}d`] += 1;
|
||||||
if (change.isBinary) {
|
if (change.isBinary) {
|
||||||
acc.binary += 1;
|
acc.binary += 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ import * as fs from 'node:fs/promises';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
|
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
import type { SkillDraftFile, SkillReviewFileChange } from '@shared/types/extensions';
|
|
||||||
|
|
||||||
import type { ImportedSkillSourceFile } from './SkillImportService';
|
import type { ImportedSkillSourceFile } from './SkillImportService';
|
||||||
|
import type { SkillDraftFile, SkillReviewFileChange } from '@shared/types/extensions';
|
||||||
|
|
||||||
const logger = createLogger('Extensions:SkillReview');
|
const logger = createLogger('Extensions:SkillReview');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
|
|
||||||
import { getHomeDir } from '@main/utils/pathDecoder';
|
import { getHomeDir } from '@main/utils/pathDecoder';
|
||||||
|
|
||||||
import type { SkillRootKind, SkillScope } from '@shared/types/extensions';
|
import type { SkillRootKind, SkillScope } from '@shared/types/extensions';
|
||||||
|
|
||||||
export interface ResolvedSkillRoot {
|
export interface ResolvedSkillRoot {
|
||||||
|
|
@ -10,7 +11,7 @@ export interface ResolvedSkillRoot {
|
||||||
rootPath: string;
|
rootPath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const USER_ROOTS: Array<{ rootKind: SkillRootKind; segments: string[] }> = [
|
const USER_ROOTS: { rootKind: SkillRootKind; segments: string[] }[] = [
|
||||||
{ rootKind: 'claude', segments: ['.claude', 'skills'] },
|
{ rootKind: 'claude', segments: ['.claude', 'skills'] },
|
||||||
{ rootKind: 'cursor', segments: ['.cursor', 'skills'] },
|
{ rootKind: 'cursor', segments: ['.cursor', 'skills'] },
|
||||||
{ rootKind: 'agents', segments: ['.agents', 'skills'] },
|
{ rootKind: 'agents', segments: ['.agents', 'skills'] },
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,11 @@ import * as fs from 'node:fs/promises';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
|
|
||||||
import { isPathWithinRoot, validateFileName } from '@main/utils/pathValidation';
|
import { isPathWithinRoot, validateFileName } from '@main/utils/pathValidation';
|
||||||
import type { SkillDraftFile, SkillRootKind, SkillScope } from '@shared/types/extensions';
|
|
||||||
|
|
||||||
import { SkillRootsResolver } from './SkillRootsResolver';
|
import { SkillRootsResolver } from './SkillRootsResolver';
|
||||||
|
|
||||||
|
import type { SkillDraftFile, SkillRootKind, SkillScope } from '@shared/types/extensions';
|
||||||
|
|
||||||
export class SkillScaffoldService {
|
export class SkillScaffoldService {
|
||||||
constructor(private readonly rootsResolver = new SkillRootsResolver()) {}
|
constructor(private readonly rootsResolver = new SkillRootsResolver()) {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import * as fs from 'node:fs/promises';
|
import * as fs from 'node:fs/promises';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
|
|
||||||
import type { SkillCatalogItem, SkillDirectoryFlags } from '@shared/types/extensions';
|
|
||||||
|
|
||||||
import { SkillMetadataParser, type SkillRelatedFiles } from './SkillMetadataParser';
|
import { SkillMetadataParser, type SkillRelatedFiles } from './SkillMetadataParser';
|
||||||
|
|
||||||
import type { ResolvedSkillRoot } from './SkillRootsResolver';
|
import type { ResolvedSkillRoot } from './SkillRootsResolver';
|
||||||
|
import type { SkillCatalogItem, SkillDirectoryFlags } from '@shared/types/extensions';
|
||||||
|
|
||||||
const SKILL_FILE_CANDIDATES = ['SKILL.md', 'Skill.md', 'skill.md'] as const;
|
const SKILL_FILE_CANDIDATES = ['SKILL.md', 'Skill.md', 'skill.md'] as const;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,14 @@ import * as fs from 'node:fs/promises';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
|
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
import type { SkillCatalogItem, SkillDetail } from '@shared/types/extensions';
|
|
||||||
|
|
||||||
import { SkillMetadataParser } from './SkillMetadataParser';
|
import { SkillMetadataParser } from './SkillMetadataParser';
|
||||||
import { SkillRootsResolver, type ResolvedSkillRoot } from './SkillRootsResolver';
|
import { type ResolvedSkillRoot, SkillRootsResolver } from './SkillRootsResolver';
|
||||||
import { SkillScanner } from './SkillScanner';
|
import { SkillScanner } from './SkillScanner';
|
||||||
import { SkillValidator } from './SkillValidator';
|
import { SkillValidator } from './SkillValidator';
|
||||||
|
|
||||||
|
import type { SkillCatalogItem, SkillDetail } from '@shared/types/extensions';
|
||||||
|
|
||||||
const logger = createLogger('Extensions:SkillsCatalog');
|
const logger = createLogger('Extensions:SkillsCatalog');
|
||||||
|
|
||||||
export class SkillsCatalogService {
|
export class SkillsCatalogService {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,15 @@
|
||||||
import * as fs from 'node:fs/promises';
|
import * as fs from 'node:fs/promises';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
|
|
||||||
|
import { isPathWithinRoot, validateFileName } from '@main/utils/pathValidation';
|
||||||
|
import { shell } from 'electron';
|
||||||
|
|
||||||
|
import { SkillImportService } from './SkillImportService';
|
||||||
|
import { SkillPlanService } from './SkillPlanService';
|
||||||
|
import { SkillRootsResolver } from './SkillRootsResolver';
|
||||||
|
import { SkillScaffoldService } from './SkillScaffoldService';
|
||||||
|
import { SkillsCatalogService } from './SkillsCatalogService';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
SkillDeleteRequest,
|
SkillDeleteRequest,
|
||||||
SkillDetail,
|
SkillDetail,
|
||||||
|
|
@ -8,15 +17,6 @@ import type {
|
||||||
SkillReviewPreview,
|
SkillReviewPreview,
|
||||||
SkillUpsertRequest,
|
SkillUpsertRequest,
|
||||||
} from '@shared/types/extensions';
|
} from '@shared/types/extensions';
|
||||||
import { shell } from 'electron';
|
|
||||||
|
|
||||||
import { isPathWithinRoot, validateFileName } from '@main/utils/pathValidation';
|
|
||||||
|
|
||||||
import { SkillImportService } from './SkillImportService';
|
|
||||||
import { SkillPlanService } from './SkillPlanService';
|
|
||||||
import { SkillScaffoldService } from './SkillScaffoldService';
|
|
||||||
import { SkillRootsResolver } from './SkillRootsResolver';
|
|
||||||
import { SkillsCatalogService } from './SkillsCatalogService';
|
|
||||||
|
|
||||||
export class SkillsMutationService {
|
export class SkillsMutationService {
|
||||||
constructor(
|
constructor(
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { createLogger } from '@shared/utils/logger';
|
|
||||||
import type { SkillWatcherEvent } from '@shared/types/extensions';
|
|
||||||
import { isPathWithinRoot } from '@main/utils/pathValidation';
|
import { isPathWithinRoot } from '@main/utils/pathValidation';
|
||||||
|
import { createLogger } from '@shared/utils/logger';
|
||||||
import { watch } from 'chokidar';
|
import { watch } from 'chokidar';
|
||||||
|
|
||||||
import { SkillRootsResolver } from './SkillRootsResolver';
|
import { SkillRootsResolver } from './SkillRootsResolver';
|
||||||
|
|
||||||
|
import type { SkillWatcherEvent } from '@shared/types/extensions';
|
||||||
import type { FSWatcher } from 'chokidar';
|
import type { FSWatcher } from 'chokidar';
|
||||||
|
|
||||||
const logger = createLogger('Extensions:SkillsWatcher');
|
const logger = createLogger('Extensions:SkillsWatcher');
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,10 @@
|
||||||
import * as fs from 'node:fs/promises';
|
import * as fs from 'node:fs/promises';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
|
|
||||||
import { createLogger } from '@shared/utils/logger';
|
|
||||||
import type { InstalledMcpEntry } from '@shared/types/extensions';
|
|
||||||
import { getHomeDir } from '@main/utils/pathDecoder';
|
import { getHomeDir } from '@main/utils/pathDecoder';
|
||||||
|
import { createLogger } from '@shared/utils/logger';
|
||||||
|
|
||||||
|
import type { InstalledMcpEntry } from '@shared/types/extensions';
|
||||||
|
|
||||||
const logger = createLogger('Extensions:McpState');
|
const logger = createLogger('Extensions:McpState');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,11 @@
|
||||||
import * as fs from 'node:fs/promises';
|
import * as fs from 'node:fs/promises';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
|
|
||||||
|
import { getClaudeBasePath } from '@main/utils/pathDecoder';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
|
|
||||||
import type { InstalledPluginEntry } from '@shared/types/extensions';
|
import type { InstalledPluginEntry } from '@shared/types/extensions';
|
||||||
import type { InstallScope } from '@shared/types/extensions';
|
import type { InstallScope } from '@shared/types/extensions';
|
||||||
import { getClaudeBasePath } from '@main/utils/pathDecoder';
|
|
||||||
|
|
||||||
const logger = createLogger('Extensions:PluginState');
|
const logger = createLogger('Extensions:PluginState');
|
||||||
|
|
||||||
|
|
@ -29,24 +30,24 @@ interface InstalledPluginsJson {
|
||||||
version: number;
|
version: number;
|
||||||
plugins: Record<
|
plugins: Record<
|
||||||
string, // qualifiedName
|
string, // qualifiedName
|
||||||
Array<{
|
{
|
||||||
scope: string;
|
scope: string;
|
||||||
installPath?: string;
|
installPath?: string;
|
||||||
version?: string;
|
version?: string;
|
||||||
installedAt?: string;
|
installedAt?: string;
|
||||||
lastUpdated?: string;
|
lastUpdated?: string;
|
||||||
gitCommitSha?: string;
|
gitCommitSha?: string;
|
||||||
}>
|
}[]
|
||||||
>;
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InstallCountsJson {
|
interface InstallCountsJson {
|
||||||
version: number;
|
version: number;
|
||||||
fetchedAt: string;
|
fetchedAt: string;
|
||||||
counts: Array<{
|
counts: {
|
||||||
plugin: string; // qualifiedName format
|
plugin: string; // qualifiedName format
|
||||||
unique_installs: number;
|
unique_installs: number;
|
||||||
}>;
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Cache ──────────────────────────────────────────────────────────────────
|
// ── Cache ──────────────────────────────────────────────────────────────────
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@
|
||||||
export * from './analysis';
|
export * from './analysis';
|
||||||
export * from './discovery';
|
export * from './discovery';
|
||||||
export * from './error';
|
export * from './error';
|
||||||
|
export * from './extensions';
|
||||||
export * from './infrastructure';
|
export * from './infrastructure';
|
||||||
export * from './parsing';
|
export * from './parsing';
|
||||||
export * from './team';
|
|
||||||
export * from './schedule';
|
export * from './schedule';
|
||||||
export * from './extensions';
|
export * from './team';
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ import { createLogger } from '@shared/utils/logger';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import type { Schedule, ScheduleRun } from '@shared/types';
|
|
||||||
import type { ScheduleRepository } from './ScheduleRepository';
|
import type { ScheduleRepository } from './ScheduleRepository';
|
||||||
|
import type { Schedule, ScheduleRun } from '@shared/types';
|
||||||
|
|
||||||
const logger = createLogger('Service:JsonScheduleRepo');
|
const logger = createLogger('Service:JsonScheduleRepo');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ function extractSummaryFromStreamJson(stdout: string): string {
|
||||||
|
|
||||||
const content = (parsed.content ??
|
const content = (parsed.content ??
|
||||||
(parsed.message as Record<string, unknown> | undefined)?.content) as
|
(parsed.message as Record<string, unknown> | undefined)?.content) as
|
||||||
| Array<{ type?: string; text?: string }>
|
| { type?: string; text?: string }[]
|
||||||
| undefined;
|
| undefined;
|
||||||
if (!Array.isArray(content)) continue;
|
if (!Array.isArray(content)) continue;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ import { createLogger } from '@shared/utils/logger';
|
||||||
import { Cron } from 'croner';
|
import { Cron } from 'croner';
|
||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
|
|
||||||
|
import type { ScheduledTaskExecutor } from './ScheduledTaskExecutor';
|
||||||
|
import type { ScheduleRepository } from './ScheduleRepository';
|
||||||
import type {
|
import type {
|
||||||
CreateScheduleInput,
|
CreateScheduleInput,
|
||||||
Schedule,
|
Schedule,
|
||||||
|
|
@ -18,8 +20,6 @@ import type {
|
||||||
ScheduleRunStatus,
|
ScheduleRunStatus,
|
||||||
UpdateSchedulePatch,
|
UpdateSchedulePatch,
|
||||||
} from '@shared/types';
|
} from '@shared/types';
|
||||||
import type { ScheduleRepository } from './ScheduleRepository';
|
|
||||||
import type { ScheduledTaskExecutor } from './ScheduledTaskExecutor';
|
|
||||||
|
|
||||||
const logger = createLogger('Service:Scheduler');
|
const logger = createLogger('Service:Scheduler');
|
||||||
|
|
||||||
|
|
@ -511,7 +511,7 @@ export class SchedulerService {
|
||||||
|
|
||||||
private async onCronTick(scheduleId: string): Promise<void> {
|
private async onCronTick(scheduleId: string): Promise<void> {
|
||||||
const schedule = await this.repository.getSchedule(scheduleId);
|
const schedule = await this.repository.getSchedule(scheduleId);
|
||||||
if (!schedule || schedule.status !== 'active') {
|
if (schedule?.status !== 'active') {
|
||||||
logger.debug(`Cron tick for ${scheduleId} skipped (not active)`);
|
logger.debug(`Cron tick for ${scheduleId} skipped (not active)`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -659,7 +659,7 @@ export class SchedulerService {
|
||||||
}
|
}
|
||||||
|
|
||||||
const freshSchedule = await this.repository.getSchedule(schedule.id);
|
const freshSchedule = await this.repository.getSchedule(schedule.id);
|
||||||
if (!freshSchedule || freshSchedule.status !== 'active') {
|
if (freshSchedule?.status !== 'active') {
|
||||||
await this.completeRun(retryRun, 'failed', exitCode, undefined, error);
|
await this.completeRun(retryRun, 'failed', exitCode, undefined, error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { JsonScheduleRepository } from './JsonScheduleRepository';
|
export { JsonScheduleRepository } from './JsonScheduleRepository';
|
||||||
export type { ScheduleRepository } from './ScheduleRepository';
|
|
||||||
export { ScheduledTaskExecutor } from './ScheduledTaskExecutor';
|
|
||||||
export type {
|
export type {
|
||||||
ExecutionRequest,
|
ExecutionRequest,
|
||||||
InternalScheduleRun,
|
InternalScheduleRun,
|
||||||
ScheduledTaskResult,
|
ScheduledTaskResult,
|
||||||
} from './ScheduledTaskExecutor';
|
} from './ScheduledTaskExecutor';
|
||||||
export { SchedulerService } from './SchedulerService';
|
export { ScheduledTaskExecutor } from './ScheduledTaskExecutor';
|
||||||
|
export type { ScheduleRepository } from './ScheduleRepository';
|
||||||
export type { WarmUpFn } from './SchedulerService';
|
export type { WarmUpFn } from './SchedulerService';
|
||||||
|
export { SchedulerService } from './SchedulerService';
|
||||||
|
|
|
||||||
|
|
@ -577,7 +577,7 @@ export class ChangeExtractorService {
|
||||||
|
|
||||||
private async parseJSONLFilesWithConcurrency(
|
private async parseJSONLFilesWithConcurrency(
|
||||||
paths: string[]
|
paths: string[]
|
||||||
): Promise<Array<{ snippets: SnippetDiff[]; mtime: number }>> {
|
): Promise<{ snippets: SnippetDiff[]; mtime: number }[]> {
|
||||||
if (paths.length === 0) return [];
|
if (paths.length === 0) return [];
|
||||||
|
|
||||||
const results = new Array<{ snippets: SnippetDiff[]; mtime: number }>(paths.length);
|
const results = new Array<{ snippets: SnippetDiff[]; mtime: number }>(paths.length);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { CROSS_TEAM_SENT_SOURCE, CROSS_TEAM_SOURCE, formatCrossTeamText } from '@shared/constants';
|
|
||||||
import { getClaudeBasePath, getTeamsBasePath } from '@main/utils/pathDecoder';
|
import { getClaudeBasePath, getTeamsBasePath } from '@main/utils/pathDecoder';
|
||||||
|
import { CROSS_TEAM_SENT_SOURCE, CROSS_TEAM_SOURCE, formatCrossTeamText } from '@shared/constants';
|
||||||
import { isLeadMember } from '@shared/utils/leadDetection';
|
import { isLeadMember } from '@shared/utils/leadDetection';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
import * as agentTeamsControllerModule from 'agent-teams-controller';
|
import * as agentTeamsControllerModule from 'agent-teams-controller';
|
||||||
|
|
|
||||||
|
|
@ -300,7 +300,8 @@ export class TeamBackupService {
|
||||||
if (raw && isValidConfig(raw)) {
|
if (raw && isValidConfig(raw)) {
|
||||||
const parsed = JSON.parse(raw) as Record<string, unknown>;
|
const parsed = JSON.parse(raw) as Record<string, unknown>;
|
||||||
manifest.displayName = typeof parsed.name === 'string' ? parsed.name : undefined;
|
manifest.displayName = typeof parsed.name === 'string' ? parsed.name : undefined;
|
||||||
manifest.projectPath = typeof parsed.projectPath === 'string' ? parsed.projectPath : undefined;
|
manifest.projectPath =
|
||||||
|
typeof parsed.projectPath === 'string' ? parsed.projectPath : undefined;
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// best-effort
|
// best-effort
|
||||||
|
|
@ -392,7 +393,7 @@ export class TeamBackupService {
|
||||||
}
|
}
|
||||||
|
|
||||||
const cached = manifest.fileStats[descriptor.relPath];
|
const cached = manifest.fileStats[descriptor.relPath];
|
||||||
if (cached && cached.mtime === stat.mtimeMs && cached.size === stat.size) {
|
if (cached?.mtime === stat.mtimeMs && cached.size === stat.size) {
|
||||||
return false; // not dirty
|
return false; // not dirty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -431,7 +432,7 @@ export class TeamBackupService {
|
||||||
if (stat.size > MAX_FILE_SIZE_BYTES) return; // skip oversized silently during shutdown
|
if (stat.size > MAX_FILE_SIZE_BYTES) return; // skip oversized silently during shutdown
|
||||||
|
|
||||||
const cached = manifest.fileStats[descriptor.relPath];
|
const cached = manifest.fileStats[descriptor.relPath];
|
||||||
if (cached && cached.mtime === stat.mtimeMs && cached.size === stat.size) return;
|
if (cached?.mtime === stat.mtimeMs && cached.size === stat.size) return;
|
||||||
|
|
||||||
const destPath = path.join(backupDir, descriptor.relPath);
|
const destPath = path.join(backupDir, descriptor.relPath);
|
||||||
|
|
||||||
|
|
@ -572,10 +573,7 @@ export class TeamBackupService {
|
||||||
return count > 0;
|
return count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async restoreGenericPartial(
|
private async restoreGenericPartial(teamName: string, manifest: BackupManifest): Promise<number> {
|
||||||
teamName: string,
|
|
||||||
manifest: BackupManifest
|
|
||||||
): Promise<number> {
|
|
||||||
const backupDir = this.getBackupDir(teamName);
|
const backupDir = this.getBackupDir(teamName);
|
||||||
const backupFiles = await this.enumerateBackupFiles(teamName);
|
const backupFiles = await this.enumerateBackupFiles(teamName);
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
@ -892,10 +890,20 @@ export class TeamBackupService {
|
||||||
return path.join(getTasksBasePath(), teamName, relPath.slice('tasks/'.length));
|
return path.join(getTasksBasePath(), teamName, relPath.slice('tasks/'.length));
|
||||||
}
|
}
|
||||||
if (relPath.startsWith('attachments/')) {
|
if (relPath.startsWith('attachments/')) {
|
||||||
return path.join(getAppDataPath(), 'attachments', teamName, relPath.slice('attachments/'.length));
|
return path.join(
|
||||||
|
getAppDataPath(),
|
||||||
|
'attachments',
|
||||||
|
teamName,
|
||||||
|
relPath.slice('attachments/'.length)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (relPath.startsWith('task-attachments/')) {
|
if (relPath.startsWith('task-attachments/')) {
|
||||||
return path.join(getAppDataPath(), 'task-attachments', teamName, relPath.slice('task-attachments/'.length));
|
return path.join(
|
||||||
|
getAppDataPath(),
|
||||||
|
'task-attachments',
|
||||||
|
teamName,
|
||||||
|
relPath.slice('task-attachments/'.length)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return path.join(getTeamsBasePath(), teamName, relPath);
|
return path.join(getTeamsBasePath(), teamName, relPath);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
|
import { getHomeDir } from '@main/utils/pathDecoder';
|
||||||
|
import { createLogger } from '@shared/utils/logger';
|
||||||
import { execFile } from 'child_process';
|
import { execFile } from 'child_process';
|
||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { getHomeDir } from '@main/utils/pathDecoder';
|
|
||||||
import { createLogger } from '@shared/utils/logger';
|
|
||||||
|
|
||||||
import { atomicWriteAsync } from './atomicWrite';
|
import { atomicWriteAsync } from './atomicWrite';
|
||||||
|
|
||||||
interface McpLaunchSpec {
|
interface McpLaunchSpec {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
/* eslint-disable no-param-reassign -- ProvisioningRun object is intentionally mutated as a state tracker throughout the provisioning lifecycle */
|
/* eslint-disable no-param-reassign -- ProvisioningRun object is intentionally mutated as a state tracker throughout the provisioning lifecycle */
|
||||||
import { ConfigManager } from '@main/services/infrastructure/ConfigManager';
|
import { ConfigManager } from '@main/services/infrastructure/ConfigManager';
|
||||||
import { killProcessTree, spawnCli } from '@main/utils/childProcess';
|
import { killProcessTree, spawnCli } from '@main/utils/childProcess';
|
||||||
import { shouldAutoAllow } from '@main/utils/toolApprovalRules';
|
|
||||||
import { FileReadTimeoutError, readFileUtf8WithTimeout } from '@main/utils/fsRead';
|
import { FileReadTimeoutError, readFileUtf8WithTimeout } from '@main/utils/fsRead';
|
||||||
import { resolveInteractiveShellEnv } from '@main/utils/shellEnv';
|
|
||||||
import {
|
import {
|
||||||
encodePath,
|
encodePath,
|
||||||
extractBaseDir,
|
extractBaseDir,
|
||||||
|
|
@ -14,6 +12,8 @@ import {
|
||||||
getTasksBasePath,
|
getTasksBasePath,
|
||||||
getTeamsBasePath,
|
getTeamsBasePath,
|
||||||
} from '@main/utils/pathDecoder';
|
} from '@main/utils/pathDecoder';
|
||||||
|
import { resolveInteractiveShellEnv } from '@main/utils/shellEnv';
|
||||||
|
import { shouldAutoAllow } from '@main/utils/toolApprovalRules';
|
||||||
import {
|
import {
|
||||||
AGENT_BLOCK_CLOSE,
|
AGENT_BLOCK_CLOSE,
|
||||||
AGENT_BLOCK_OPEN,
|
AGENT_BLOCK_OPEN,
|
||||||
|
|
@ -21,8 +21,8 @@ import {
|
||||||
} from '@shared/constants/agentBlocks';
|
} from '@shared/constants/agentBlocks';
|
||||||
import {
|
import {
|
||||||
CROSS_TEAM_PREFIX_TAG,
|
CROSS_TEAM_PREFIX_TAG,
|
||||||
CROSS_TEAM_SOURCE,
|
|
||||||
CROSS_TEAM_SENT_SOURCE,
|
CROSS_TEAM_SENT_SOURCE,
|
||||||
|
CROSS_TEAM_SOURCE,
|
||||||
parseCrossTeamPrefix,
|
parseCrossTeamPrefix,
|
||||||
stripCrossTeamPrefix,
|
stripCrossTeamPrefix,
|
||||||
} from '@shared/constants/crossTeam';
|
} from '@shared/constants/crossTeam';
|
||||||
|
|
@ -41,14 +41,14 @@ import {
|
||||||
import { createCliAutoSuffixNameGuard, parseNumericSuffixName } from '@shared/utils/teamMemberName';
|
import { createCliAutoSuffixNameGuard, parseNumericSuffixName } from '@shared/utils/teamMemberName';
|
||||||
import { extractToolPreview, formatToolSummaryFromCalls } from '@shared/utils/toolSummary';
|
import { extractToolPreview, formatToolSummaryFromCalls } from '@shared/utils/toolSummary';
|
||||||
import * as agentTeamsControllerModule from 'agent-teams-controller';
|
import * as agentTeamsControllerModule from 'agent-teams-controller';
|
||||||
import { spawn, type ChildProcess } from 'child_process';
|
import { type ChildProcess, type spawn } from 'child_process';
|
||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { atomicWriteAsync } from './atomicWrite';
|
|
||||||
import { buildActionModeProtocol } from './actionModeInstructions';
|
import { buildActionModeProtocol } from './actionModeInstructions';
|
||||||
|
import { atomicWriteAsync } from './atomicWrite';
|
||||||
import { ClaudeBinaryResolver } from './ClaudeBinaryResolver';
|
import { ClaudeBinaryResolver } from './ClaudeBinaryResolver';
|
||||||
import { withFileLock } from './fileLock';
|
import { withFileLock } from './fileLock';
|
||||||
import { withInboxLock } from './inboxLock';
|
import { withInboxLock } from './inboxLock';
|
||||||
|
|
@ -231,10 +231,10 @@ interface ProvisioningRun {
|
||||||
rejectOnce: (error: string) => void;
|
rejectOnce: (error: string) => void;
|
||||||
timeoutHandle: NodeJS.Timeout;
|
timeoutHandle: NodeJS.Timeout;
|
||||||
} | null;
|
} | null;
|
||||||
activeCrossTeamReplyHints: Array<{
|
activeCrossTeamReplyHints: {
|
||||||
toTeam: string;
|
toTeam: string;
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
}>;
|
}[];
|
||||||
/** Monotonic counter for individual lead assistant messages. */
|
/** Monotonic counter for individual lead assistant messages. */
|
||||||
leadMsgSeq: number;
|
leadMsgSeq: number;
|
||||||
/** Accumulated tool_use details between text messages. */
|
/** Accumulated tool_use details between text messages. */
|
||||||
|
|
@ -1122,11 +1122,11 @@ interface CachedProbeResult {
|
||||||
cachedAtMs: number;
|
cachedAtMs: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProbeResult = {
|
interface ProbeResult {
|
||||||
claudePath: string;
|
claudePath: string;
|
||||||
authSource: ProvisioningAuthSource;
|
authSource: ProvisioningAuthSource;
|
||||||
warning?: string;
|
warning?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
type AuthWarningSource = 'probe' | 'stdout' | 'stderr' | 'assistant' | 'pre-complete';
|
type AuthWarningSource = 'probe' | 'stdout' | 'stderr' | 'assistant' | 'pre-complete';
|
||||||
|
|
||||||
|
|
@ -1607,21 +1607,21 @@ export class TeamProvisioningService {
|
||||||
private async matchCrossTeamLeadInboxMessages(
|
private async matchCrossTeamLeadInboxMessages(
|
||||||
teamName: string,
|
teamName: string,
|
||||||
leadName: string,
|
leadName: string,
|
||||||
deliveredBlocks: Array<{
|
deliveredBlocks: {
|
||||||
teammateId: string;
|
teammateId: string;
|
||||||
content: string;
|
content: string;
|
||||||
toTeam: string;
|
toTeam: string;
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
}>
|
}[]
|
||||||
): Promise<
|
): Promise<
|
||||||
Array<{
|
{
|
||||||
teammateId: string;
|
teammateId: string;
|
||||||
content: string;
|
content: string;
|
||||||
toTeam: string;
|
toTeam: string;
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
messageId: string;
|
messageId: string;
|
||||||
wasRead: boolean;
|
wasRead: boolean;
|
||||||
}>
|
}[]
|
||||||
> {
|
> {
|
||||||
if (deliveredBlocks.length === 0) return [];
|
if (deliveredBlocks.length === 0) return [];
|
||||||
|
|
||||||
|
|
@ -1633,14 +1633,14 @@ export class TeamProvisioningService {
|
||||||
}
|
}
|
||||||
|
|
||||||
const usedMessageIds = new Set<string>();
|
const usedMessageIds = new Set<string>();
|
||||||
const matches: Array<{
|
const matches: {
|
||||||
teammateId: string;
|
teammateId: string;
|
||||||
content: string;
|
content: string;
|
||||||
toTeam: string;
|
toTeam: string;
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
messageId: string;
|
messageId: string;
|
||||||
wasRead: boolean;
|
wasRead: boolean;
|
||||||
}> = [];
|
}[] = [];
|
||||||
for (const block of deliveredBlocks) {
|
for (const block of deliveredBlocks) {
|
||||||
const matchesBlock = (message: InboxMessage, requireExactText: boolean): boolean => {
|
const matchesBlock = (message: InboxMessage, requireExactText: boolean): boolean => {
|
||||||
if (message.source !== CROSS_TEAM_SOURCE) return false;
|
if (message.source !== CROSS_TEAM_SOURCE) return false;
|
||||||
|
|
@ -1816,7 +1816,7 @@ export class TeamProvisioningService {
|
||||||
private rememberPendingInboxRelayCandidates(
|
private rememberPendingInboxRelayCandidates(
|
||||||
run: ProvisioningRun,
|
run: ProvisioningRun,
|
||||||
recipient: string,
|
recipient: string,
|
||||||
messages: Array<Pick<InboxMessage, 'messageId' | 'text' | 'summary'>>
|
messages: Pick<InboxMessage, 'messageId' | 'text' | 'summary'>[]
|
||||||
): string[] {
|
): string[] {
|
||||||
const candidates = this.prunePendingInboxRelayCandidates(run);
|
const candidates = this.prunePendingInboxRelayCandidates(run);
|
||||||
const queuedAtMs = Date.now();
|
const queuedAtMs = Date.now();
|
||||||
|
|
@ -4167,7 +4167,7 @@ export class TeamProvisioningService {
|
||||||
(mistakenToolHint ? { teamName: mistakenToolHint.toTeam, memberName: 'team-lead' } : null);
|
(mistakenToolHint ? { teamName: mistakenToolHint.toTeam, memberName: 'team-lead' } : null);
|
||||||
if (crossTeamRecipient && this.crossTeamSender) {
|
if (crossTeamRecipient && this.crossTeamSender) {
|
||||||
const inferredReplyMeta =
|
const inferredReplyMeta =
|
||||||
mistakenToolHint && mistakenToolHint.toTeam === crossTeamRecipient.teamName
|
mistakenToolHint?.toTeam === crossTeamRecipient.teamName
|
||||||
? {
|
? {
|
||||||
conversationId: mistakenToolHint.conversationId,
|
conversationId: mistakenToolHint.conversationId,
|
||||||
replyToConversationId: mistakenToolHint.conversationId,
|
replyToConversationId: mistakenToolHint.conversationId,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { getTaskChangeSummariesBasePath } from '@main/utils/pathDecoder';
|
|
||||||
import { atomicWriteAsync } from '@main/utils/atomicWrite';
|
import { atomicWriteAsync } from '@main/utils/atomicWrite';
|
||||||
|
import { getTaskChangeSummariesBasePath } from '@main/utils/pathDecoder';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
import { createLogger } from '@shared/utils/logger';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { TASK_CHANGE_SUMMARY_CACHE_SCHEMA_VERSION } from './taskChangeSummaryCacheTypes';
|
import { TASK_CHANGE_SUMMARY_CACHE_SCHEMA_VERSION } from './taskChangeSummaryCacheTypes';
|
||||||
|
|
||||||
import type { FileChangeSummary, TaskChangeSetV2 } from '@shared/types';
|
|
||||||
import type { PersistedTaskChangeSummaryEntry } from './taskChangeSummaryCacheTypes';
|
import type { PersistedTaskChangeSummaryEntry } from './taskChangeSummaryCacheTypes';
|
||||||
|
import type { FileChangeSummary, TaskChangeSetV2 } from '@shared/types';
|
||||||
|
|
||||||
function normalizeIsoString(value: unknown): string | null {
|
function normalizeIsoString(value: unknown): string | null {
|
||||||
if (typeof value !== 'string' || value.trim() === '') return null;
|
if (typeof value !== 'string' || value.trim() === '') return null;
|
||||||
|
|
|
||||||
|
|
@ -707,8 +707,8 @@ async function readTasksDirForTeam(
|
||||||
? (parsed.attachments as unknown[])
|
? (parsed.attachments as unknown[])
|
||||||
: undefined,
|
: undefined,
|
||||||
sourceMessageId:
|
sourceMessageId:
|
||||||
typeof parsed.sourceMessageId === 'string' && (parsed.sourceMessageId as string).trim()
|
typeof parsed.sourceMessageId === 'string' && parsed.sourceMessageId.trim()
|
||||||
? (parsed.sourceMessageId as string).trim()
|
? parsed.sourceMessageId.trim()
|
||||||
: undefined,
|
: undefined,
|
||||||
sourceMessage:
|
sourceMessage:
|
||||||
parsed.sourceMessage &&
|
parsed.sourceMessage &&
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,11 @@ import { WINDOW_ZOOM_FACTOR_CHANGED_CHANNEL } from '@shared/constants';
|
||||||
import { contextBridge, ipcRenderer } from 'electron';
|
import { contextBridge, ipcRenderer } from 'electron';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
API_KEYS_DELETE,
|
||||||
|
API_KEYS_LIST,
|
||||||
|
API_KEYS_LOOKUP,
|
||||||
|
API_KEYS_SAVE,
|
||||||
|
API_KEYS_STORAGE_STATUS,
|
||||||
APP_RELAUNCH,
|
APP_RELAUNCH,
|
||||||
CLI_INSTALLER_GET_STATUS,
|
CLI_INSTALLER_GET_STATUS,
|
||||||
CLI_INSTALLER_INSTALL,
|
CLI_INSTALLER_INSTALL,
|
||||||
|
|
@ -34,28 +39,30 @@ import {
|
||||||
HTTP_SERVER_GET_STATUS,
|
HTTP_SERVER_GET_STATUS,
|
||||||
HTTP_SERVER_START,
|
HTTP_SERVER_START,
|
||||||
HTTP_SERVER_STOP,
|
HTTP_SERVER_STOP,
|
||||||
|
MCP_GITHUB_STARS,
|
||||||
|
MCP_REGISTRY_BROWSE,
|
||||||
|
MCP_REGISTRY_DIAGNOSE,
|
||||||
|
MCP_REGISTRY_GET_BY_ID,
|
||||||
|
MCP_REGISTRY_GET_INSTALLED,
|
||||||
|
MCP_REGISTRY_INSTALL,
|
||||||
|
MCP_REGISTRY_INSTALL_CUSTOM,
|
||||||
|
MCP_REGISTRY_SEARCH,
|
||||||
|
MCP_REGISTRY_UNINSTALL,
|
||||||
|
PLUGIN_GET_ALL,
|
||||||
|
PLUGIN_GET_README,
|
||||||
|
PLUGIN_INSTALL,
|
||||||
|
PLUGIN_UNINSTALL,
|
||||||
PROJECT_LIST_FILES,
|
PROJECT_LIST_FILES,
|
||||||
RENDERER_BOOT,
|
RENDERER_BOOT,
|
||||||
RENDERER_HEARTBEAT,
|
RENDERER_HEARTBEAT,
|
||||||
RENDERER_LOG,
|
RENDERER_LOG,
|
||||||
SCHEDULE_CHANGE,
|
|
||||||
SCHEDULE_CREATE,
|
|
||||||
SCHEDULE_DELETE,
|
|
||||||
SCHEDULE_GET,
|
|
||||||
SCHEDULE_GET_RUN_LOGS,
|
|
||||||
SCHEDULE_GET_RUNS,
|
|
||||||
SCHEDULE_LIST,
|
|
||||||
SCHEDULE_PAUSE,
|
|
||||||
SCHEDULE_RESUME,
|
|
||||||
SCHEDULE_TRIGGER_NOW,
|
|
||||||
SCHEDULE_UPDATE,
|
|
||||||
REVIEW_APPLY_DECISIONS,
|
REVIEW_APPLY_DECISIONS,
|
||||||
REVIEW_CHECK_CONFLICT,
|
REVIEW_CHECK_CONFLICT,
|
||||||
REVIEW_CLEAR_DECISIONS,
|
REVIEW_CLEAR_DECISIONS,
|
||||||
|
REVIEW_FILE_CHANGE,
|
||||||
REVIEW_GET_AGENT_CHANGES,
|
REVIEW_GET_AGENT_CHANGES,
|
||||||
REVIEW_GET_CHANGE_STATS,
|
REVIEW_GET_CHANGE_STATS,
|
||||||
REVIEW_GET_FILE_CONTENT,
|
REVIEW_GET_FILE_CONTENT,
|
||||||
REVIEW_FILE_CHANGE,
|
|
||||||
REVIEW_GET_GIT_FILE_LOG,
|
REVIEW_GET_GIT_FILE_LOG,
|
||||||
REVIEW_GET_TASK_CHANGES,
|
REVIEW_GET_TASK_CHANGES,
|
||||||
REVIEW_INVALIDATE_TASK_CHANGE_SUMMARIES,
|
REVIEW_INVALIDATE_TASK_CHANGE_SUMMARIES,
|
||||||
|
|
@ -67,6 +74,27 @@ import {
|
||||||
REVIEW_SAVE_EDITED_FILE,
|
REVIEW_SAVE_EDITED_FILE,
|
||||||
REVIEW_UNWATCH_FILES,
|
REVIEW_UNWATCH_FILES,
|
||||||
REVIEW_WATCH_FILES,
|
REVIEW_WATCH_FILES,
|
||||||
|
SCHEDULE_CHANGE,
|
||||||
|
SCHEDULE_CREATE,
|
||||||
|
SCHEDULE_DELETE,
|
||||||
|
SCHEDULE_GET,
|
||||||
|
SCHEDULE_GET_RUN_LOGS,
|
||||||
|
SCHEDULE_GET_RUNS,
|
||||||
|
SCHEDULE_LIST,
|
||||||
|
SCHEDULE_PAUSE,
|
||||||
|
SCHEDULE_RESUME,
|
||||||
|
SCHEDULE_TRIGGER_NOW,
|
||||||
|
SCHEDULE_UPDATE,
|
||||||
|
SKILLS_APPLY_IMPORT,
|
||||||
|
SKILLS_APPLY_UPSERT,
|
||||||
|
SKILLS_CHANGED,
|
||||||
|
SKILLS_DELETE,
|
||||||
|
SKILLS_GET_DETAIL,
|
||||||
|
SKILLS_LIST,
|
||||||
|
SKILLS_PREVIEW_IMPORT,
|
||||||
|
SKILLS_PREVIEW_UPSERT,
|
||||||
|
SKILLS_START_WATCHING,
|
||||||
|
SKILLS_STOP_WATCHING,
|
||||||
SSH_CONNECT,
|
SSH_CONNECT,
|
||||||
SSH_DISCONNECT,
|
SSH_DISCONNECT,
|
||||||
SSH_GET_CONFIG_HOSTS,
|
SSH_GET_CONFIG_HOSTS,
|
||||||
|
|
@ -101,8 +129,8 @@ import {
|
||||||
TEAM_LAUNCH,
|
TEAM_LAUNCH,
|
||||||
TEAM_LEAD_ACTIVITY,
|
TEAM_LEAD_ACTIVITY,
|
||||||
TEAM_LEAD_CONTEXT,
|
TEAM_LEAD_CONTEXT,
|
||||||
TEAM_MEMBER_SPAWN_STATUSES,
|
|
||||||
TEAM_LIST,
|
TEAM_LIST,
|
||||||
|
TEAM_MEMBER_SPAWN_STATUSES,
|
||||||
TEAM_PERMANENTLY_DELETE,
|
TEAM_PERMANENTLY_DELETE,
|
||||||
TEAM_PREPARE_PROVISIONING,
|
TEAM_PREPARE_PROVISIONING,
|
||||||
TEAM_PROCESS_ALIVE,
|
TEAM_PROCESS_ALIVE,
|
||||||
|
|
@ -149,34 +177,6 @@ import {
|
||||||
WINDOW_IS_MAXIMIZED,
|
WINDOW_IS_MAXIMIZED,
|
||||||
WINDOW_MAXIMIZE,
|
WINDOW_MAXIMIZE,
|
||||||
WINDOW_MINIMIZE,
|
WINDOW_MINIMIZE,
|
||||||
PLUGIN_GET_ALL,
|
|
||||||
PLUGIN_GET_README,
|
|
||||||
PLUGIN_INSTALL,
|
|
||||||
PLUGIN_UNINSTALL,
|
|
||||||
MCP_REGISTRY_SEARCH,
|
|
||||||
MCP_REGISTRY_BROWSE,
|
|
||||||
MCP_REGISTRY_DIAGNOSE,
|
|
||||||
MCP_REGISTRY_GET_BY_ID,
|
|
||||||
MCP_REGISTRY_GET_INSTALLED,
|
|
||||||
MCP_REGISTRY_INSTALL,
|
|
||||||
MCP_REGISTRY_INSTALL_CUSTOM,
|
|
||||||
MCP_REGISTRY_UNINSTALL,
|
|
||||||
MCP_GITHUB_STARS,
|
|
||||||
SKILLS_APPLY_IMPORT,
|
|
||||||
SKILLS_APPLY_UPSERT,
|
|
||||||
SKILLS_CHANGED,
|
|
||||||
SKILLS_DELETE,
|
|
||||||
SKILLS_GET_DETAIL,
|
|
||||||
SKILLS_LIST,
|
|
||||||
SKILLS_PREVIEW_IMPORT,
|
|
||||||
SKILLS_PREVIEW_UPSERT,
|
|
||||||
SKILLS_START_WATCHING,
|
|
||||||
SKILLS_STOP_WATCHING,
|
|
||||||
API_KEYS_LIST,
|
|
||||||
API_KEYS_SAVE,
|
|
||||||
API_KEYS_DELETE,
|
|
||||||
API_KEYS_LOOKUP,
|
|
||||||
API_KEYS_STORAGE_STATUS,
|
|
||||||
} from './constants/ipcChannels';
|
} from './constants/ipcChannels';
|
||||||
import {
|
import {
|
||||||
CONFIG_ADD_CUSTOM_PROJECT_PATH,
|
CONFIG_ADD_CUSTOM_PROJECT_PATH,
|
||||||
|
|
@ -236,9 +236,9 @@ import type {
|
||||||
KanbanColumnId,
|
KanbanColumnId,
|
||||||
LeadActivitySnapshot,
|
LeadActivitySnapshot,
|
||||||
LeadContextUsageSnapshot,
|
LeadContextUsageSnapshot,
|
||||||
MemberSpawnStatusesSnapshot,
|
|
||||||
MemberFullStats,
|
MemberFullStats,
|
||||||
MemberLogSummary,
|
MemberLogSummary,
|
||||||
|
MemberSpawnStatusesSnapshot,
|
||||||
NotificationTrigger,
|
NotificationTrigger,
|
||||||
RejectResult,
|
RejectResult,
|
||||||
ReplaceMembersRequest,
|
ReplaceMembersRequest,
|
||||||
|
|
@ -281,28 +281,6 @@ import type {
|
||||||
UpdateSchedulePatch,
|
UpdateSchedulePatch,
|
||||||
WslClaudeRootCandidate,
|
WslClaudeRootCandidate,
|
||||||
} from '@shared/types';
|
} from '@shared/types';
|
||||||
import type {
|
|
||||||
ApiKeyEntry,
|
|
||||||
ApiKeyLookupResult,
|
|
||||||
ApiKeySaveRequest,
|
|
||||||
ApiKeyStorageStatus,
|
|
||||||
EnrichedPlugin,
|
|
||||||
InstalledMcpEntry,
|
|
||||||
McpCatalogItem,
|
|
||||||
McpCustomInstallRequest,
|
|
||||||
McpInstallRequest,
|
|
||||||
McpServerDiagnostic,
|
|
||||||
McpSearchResult,
|
|
||||||
OperationResult,
|
|
||||||
PluginInstallRequest,
|
|
||||||
SkillCatalogItem,
|
|
||||||
SkillDeleteRequest,
|
|
||||||
SkillDetail,
|
|
||||||
SkillImportRequest,
|
|
||||||
SkillReviewPreview,
|
|
||||||
SkillUpsertRequest,
|
|
||||||
SkillWatcherEvent,
|
|
||||||
} from '@shared/types/extensions';
|
|
||||||
import type {
|
import type {
|
||||||
BinaryPreviewResult,
|
BinaryPreviewResult,
|
||||||
CreateDirResponse,
|
CreateDirResponse,
|
||||||
|
|
@ -318,6 +296,28 @@ import type {
|
||||||
SearchInFilesResult,
|
SearchInFilesResult,
|
||||||
WriteFileResponse,
|
WriteFileResponse,
|
||||||
} from '@shared/types/editor';
|
} from '@shared/types/editor';
|
||||||
|
import type {
|
||||||
|
ApiKeyEntry,
|
||||||
|
ApiKeyLookupResult,
|
||||||
|
ApiKeySaveRequest,
|
||||||
|
ApiKeyStorageStatus,
|
||||||
|
EnrichedPlugin,
|
||||||
|
InstalledMcpEntry,
|
||||||
|
McpCatalogItem,
|
||||||
|
McpCustomInstallRequest,
|
||||||
|
McpInstallRequest,
|
||||||
|
McpSearchResult,
|
||||||
|
McpServerDiagnostic,
|
||||||
|
OperationResult,
|
||||||
|
PluginInstallRequest,
|
||||||
|
SkillCatalogItem,
|
||||||
|
SkillDeleteRequest,
|
||||||
|
SkillDetail,
|
||||||
|
SkillImportRequest,
|
||||||
|
SkillReviewPreview,
|
||||||
|
SkillUpsertRequest,
|
||||||
|
SkillWatcherEvent,
|
||||||
|
} from '@shared/types/extensions';
|
||||||
import type { PtySpawnOptions } from '@shared/types/terminal';
|
import type { PtySpawnOptions } from '@shared/types/terminal';
|
||||||
import type { CliArgsValidationResult } from '@shared/utils/cliArgsParser';
|
import type { CliArgsValidationResult } from '@shared/utils/cliArgsParser';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import {
|
||||||
import { getTeamColorSet, getThemedBadge } from '@renderer/constants/teamColors';
|
import { getTeamColorSet, getThemedBadge } from '@renderer/constants/teamColors';
|
||||||
import { useTheme } from '@renderer/hooks/useTheme';
|
import { useTheme } from '@renderer/hooks/useTheme';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import type { SearchMatch } from '@renderer/store/types';
|
|
||||||
import { REHYPE_PLUGINS, REHYPE_PLUGINS_NO_HIGHLIGHT } from '@renderer/utils/markdownPlugins';
|
import { REHYPE_PLUGINS, REHYPE_PLUGINS_NO_HIGHLIGHT } from '@renderer/utils/markdownPlugins';
|
||||||
import { nameColorSet } from '@renderer/utils/projectColor';
|
import { nameColorSet } from '@renderer/utils/projectColor';
|
||||||
import { parseTaskLinkHref } from '@renderer/utils/taskReferenceUtils';
|
import { parseTaskLinkHref } from '@renderer/utils/taskReferenceUtils';
|
||||||
|
|
@ -44,6 +43,8 @@ import {
|
||||||
import { FileLink, isRelativeUrl } from './FileLink';
|
import { FileLink, isRelativeUrl } from './FileLink';
|
||||||
import { MermaidDiagram } from './MermaidDiagram';
|
import { MermaidDiagram } from './MermaidDiagram';
|
||||||
|
|
||||||
|
import type { SearchMatch } from '@renderer/store/types';
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// Types
|
// Types
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
@ -69,7 +70,7 @@ interface MarkdownViewerProps {
|
||||||
onTeamClick?: (teamName: string) => void;
|
onTeamClick?: (teamName: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EMPTY_TEAMS: Array<{ teamName?: string; displayName?: string; color?: string }> = [];
|
const EMPTY_TEAMS: { teamName?: string; displayName?: string; color?: string }[] = [];
|
||||||
const EMPTY_TEAM_COLOR_MAP = new Map<string, string>();
|
const EMPTY_TEAM_COLOR_MAP = new Map<string, string>();
|
||||||
const EMPTY_SEARCH_MATCHES: SearchMatch[] = [];
|
const EMPTY_SEARCH_MATCHES: SearchMatch[] = [];
|
||||||
const NOOP_TEAM_CLICK = (): void => undefined;
|
const NOOP_TEAM_CLICK = (): void => undefined;
|
||||||
|
|
@ -94,31 +95,136 @@ function allowCustomProtocols(url: string): string {
|
||||||
* that appear in agent messages and cause React "unrecognized tag" warnings.
|
* that appear in agent messages and cause React "unrecognized tag" warnings.
|
||||||
*/
|
*/
|
||||||
const STANDARD_HTML_TAGS = new Set([
|
const STANDARD_HTML_TAGS = new Set([
|
||||||
'a', 'abbr', 'address', 'area', 'article', 'aside', 'audio',
|
'a',
|
||||||
'b', 'base', 'bdi', 'bdo', 'blockquote', 'body', 'br', 'button',
|
'abbr',
|
||||||
'canvas', 'caption', 'cite', 'code', 'col', 'colgroup',
|
'address',
|
||||||
'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt',
|
'area',
|
||||||
'em', 'embed',
|
'article',
|
||||||
'fieldset', 'figcaption', 'figure', 'footer', 'form',
|
'aside',
|
||||||
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html',
|
'audio',
|
||||||
'i', 'iframe', 'img', 'input', 'ins',
|
'b',
|
||||||
|
'base',
|
||||||
|
'bdi',
|
||||||
|
'bdo',
|
||||||
|
'blockquote',
|
||||||
|
'body',
|
||||||
|
'br',
|
||||||
|
'button',
|
||||||
|
'canvas',
|
||||||
|
'caption',
|
||||||
|
'cite',
|
||||||
|
'code',
|
||||||
|
'col',
|
||||||
|
'colgroup',
|
||||||
|
'data',
|
||||||
|
'datalist',
|
||||||
|
'dd',
|
||||||
|
'del',
|
||||||
|
'details',
|
||||||
|
'dfn',
|
||||||
|
'dialog',
|
||||||
|
'div',
|
||||||
|
'dl',
|
||||||
|
'dt',
|
||||||
|
'em',
|
||||||
|
'embed',
|
||||||
|
'fieldset',
|
||||||
|
'figcaption',
|
||||||
|
'figure',
|
||||||
|
'footer',
|
||||||
|
'form',
|
||||||
|
'h1',
|
||||||
|
'h2',
|
||||||
|
'h3',
|
||||||
|
'h4',
|
||||||
|
'h5',
|
||||||
|
'h6',
|
||||||
|
'head',
|
||||||
|
'header',
|
||||||
|
'hgroup',
|
||||||
|
'hr',
|
||||||
|
'html',
|
||||||
|
'i',
|
||||||
|
'iframe',
|
||||||
|
'img',
|
||||||
|
'input',
|
||||||
|
'ins',
|
||||||
'kbd',
|
'kbd',
|
||||||
'label', 'legend', 'li', 'link',
|
'label',
|
||||||
'main', 'map', 'mark', 'menu', 'meta', 'meter',
|
'legend',
|
||||||
'nav', 'noscript',
|
'li',
|
||||||
'object', 'ol', 'optgroup', 'option', 'output',
|
'link',
|
||||||
'p', 'picture', 'pre', 'progress',
|
'main',
|
||||||
|
'map',
|
||||||
|
'mark',
|
||||||
|
'menu',
|
||||||
|
'meta',
|
||||||
|
'meter',
|
||||||
|
'nav',
|
||||||
|
'noscript',
|
||||||
|
'object',
|
||||||
|
'ol',
|
||||||
|
'optgroup',
|
||||||
|
'option',
|
||||||
|
'output',
|
||||||
|
'p',
|
||||||
|
'picture',
|
||||||
|
'pre',
|
||||||
|
'progress',
|
||||||
'q',
|
'q',
|
||||||
'rp', 'rt', 'ruby',
|
'rp',
|
||||||
's', 'samp', 'script', 'search', 'section', 'select', 'slot', 'small', 'source', 'span',
|
'rt',
|
||||||
'strong', 'style', 'sub', 'summary', 'sup',
|
'ruby',
|
||||||
'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track',
|
's',
|
||||||
'u', 'ul',
|
'samp',
|
||||||
'var', 'video',
|
'script',
|
||||||
|
'search',
|
||||||
|
'section',
|
||||||
|
'select',
|
||||||
|
'slot',
|
||||||
|
'small',
|
||||||
|
'source',
|
||||||
|
'span',
|
||||||
|
'strong',
|
||||||
|
'style',
|
||||||
|
'sub',
|
||||||
|
'summary',
|
||||||
|
'sup',
|
||||||
|
'table',
|
||||||
|
'tbody',
|
||||||
|
'td',
|
||||||
|
'template',
|
||||||
|
'textarea',
|
||||||
|
'tfoot',
|
||||||
|
'th',
|
||||||
|
'thead',
|
||||||
|
'time',
|
||||||
|
'title',
|
||||||
|
'tr',
|
||||||
|
'track',
|
||||||
|
'u',
|
||||||
|
'ul',
|
||||||
|
'var',
|
||||||
|
'video',
|
||||||
'wbr',
|
'wbr',
|
||||||
// SVG elements commonly used inline
|
// SVG elements commonly used inline
|
||||||
'svg', 'path', 'circle', 'rect', 'line', 'polyline', 'polygon', 'g', 'defs', 'use',
|
'svg',
|
||||||
'text', 'tspan', 'clippath', 'mask', 'pattern', 'image', 'foreignobject',
|
'path',
|
||||||
|
'circle',
|
||||||
|
'rect',
|
||||||
|
'line',
|
||||||
|
'polyline',
|
||||||
|
'polygon',
|
||||||
|
'g',
|
||||||
|
'defs',
|
||||||
|
'use',
|
||||||
|
'text',
|
||||||
|
'tspan',
|
||||||
|
'clippath',
|
||||||
|
'mask',
|
||||||
|
'pattern',
|
||||||
|
'image',
|
||||||
|
'foreignobject',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
import React, { Component, type ErrorInfo, type ReactNode } from 'react';
|
import React, { Component, type ErrorInfo, type ReactNode } from 'react';
|
||||||
|
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { createLogger } from '@shared/utils/logger';
|
|
||||||
import { AlertTriangle, Bug, Check, Copy, RefreshCw } from 'lucide-react';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
type BugReportContext,
|
||||||
buildBugReportText,
|
buildBugReportText,
|
||||||
buildGitHubBugReportUrl,
|
buildGitHubBugReportUrl,
|
||||||
type BugReportContext,
|
|
||||||
} from '@renderer/utils/bugReportUtils';
|
} from '@renderer/utils/bugReportUtils';
|
||||||
|
import { createLogger } from '@shared/utils/logger';
|
||||||
|
import { AlertTriangle, Bug, Check, Copy, RefreshCw } from 'lucide-react';
|
||||||
|
|
||||||
const logger = createLogger('Component:ErrorBoundary');
|
const logger = createLogger('Component:ErrorBoundary');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { api } from '@renderer/api';
|
import { api } from '@renderer/api';
|
||||||
import { Button } from '@renderer/components/ui/button';
|
import { Button } from '@renderer/components/ui/button';
|
||||||
import { useTabIdOptional } from '@renderer/contexts/useTabUIContext';
|
|
||||||
import { useExtensionsTabState } from '@renderer/hooks/useExtensionsTabState';
|
|
||||||
import { useStore } from '@renderer/store';
|
|
||||||
import { Tabs, TabsContent, TabsList } from '@renderer/components/ui/tabs';
|
import { Tabs, TabsContent, TabsList } from '@renderer/components/ui/tabs';
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
|
|
@ -18,14 +15,17 @@ import {
|
||||||
TooltipProvider,
|
TooltipProvider,
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from '@renderer/components/ui/tooltip';
|
} from '@renderer/components/ui/tooltip';
|
||||||
|
import { useTabIdOptional } from '@renderer/contexts/useTabUIContext';
|
||||||
|
import { useExtensionsTabState } from '@renderer/hooks/useExtensionsTabState';
|
||||||
|
import { useStore } from '@renderer/store';
|
||||||
import { AlertTriangle, BookOpen, Info, Key, Plus, Puzzle, RefreshCw, Server } from 'lucide-react';
|
import { AlertTriangle, BookOpen, Info, Key, Plus, Puzzle, RefreshCw, Server } from 'lucide-react';
|
||||||
|
|
||||||
import { ApiKeysPanel } from './apikeys/ApiKeysPanel';
|
import { ApiKeysPanel } from './apikeys/ApiKeysPanel';
|
||||||
import { ExtensionsSubTabTrigger } from './ExtensionsSubTabTrigger';
|
|
||||||
import { CustomMcpServerDialog } from './mcp/CustomMcpServerDialog';
|
import { CustomMcpServerDialog } from './mcp/CustomMcpServerDialog';
|
||||||
import { McpServersPanel } from './mcp/McpServersPanel';
|
import { McpServersPanel } from './mcp/McpServersPanel';
|
||||||
import { PluginsPanel } from './plugins/PluginsPanel';
|
import { PluginsPanel } from './plugins/PluginsPanel';
|
||||||
import { SkillsPanel } from './skills/SkillsPanel';
|
import { SkillsPanel } from './skills/SkillsPanel';
|
||||||
|
import { ExtensionsSubTabTrigger } from './ExtensionsSubTabTrigger';
|
||||||
|
|
||||||
export const ExtensionStoreView = (): React.JSX.Element => {
|
export const ExtensionStoreView = (): React.JSX.Element => {
|
||||||
const tabId = useTabIdOptional();
|
const tabId = useTabIdOptional();
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
import type { LucideIcon } from 'lucide-react';
|
|
||||||
|
|
||||||
import { TabsTrigger } from '@renderer/components/ui/tabs';
|
import { TabsTrigger } from '@renderer/components/ui/tabs';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||||
|
|
||||||
import { Info } from 'lucide-react';
|
import { Info } from 'lucide-react';
|
||||||
|
|
||||||
|
import type { LucideIcon } from 'lucide-react';
|
||||||
|
|
||||||
interface ExtensionsSubTabTriggerProps {
|
interface ExtensionsSubTabTriggerProps {
|
||||||
value: 'plugins' | 'mcp-servers' | 'skills' | 'api-keys';
|
value: 'plugins' | 'mcp-servers' | 'skills' | 'api-keys';
|
||||||
label: string;
|
label: string;
|
||||||
|
|
@ -21,7 +20,7 @@ export const ExtensionsSubTabTrigger = ({
|
||||||
return (
|
return (
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value={value}
|
value={value}
|
||||||
className="relative gap-1.5 rounded-b-none pr-7 data-[state=active]:z-10 data-[state=active]:-mb-px data-[state=active]:bg-[var(--color-surface)] data-[state=active]:shadow-none data-[state=active]:after:absolute data-[state=active]:after:-bottom-px data-[state=active]:after:left-0 data-[state=active]:after:right-0 data-[state=active]:after:h-1 data-[state=active]:after:bg-[var(--color-surface)] data-[state=active]:after:content-['']"
|
className="relative gap-1.5 rounded-b-none pr-7 data-[state=active]:z-10 data-[state=active]:-mb-px data-[state=active]:bg-[var(--color-surface)] data-[state=active]:shadow-none data-[state=active]:after:absolute data-[state=active]:after:inset-x-0 data-[state=active]:after:-bottom-px data-[state=active]:after:h-1 data-[state=active]:after:bg-[var(--color-surface)] data-[state=active]:after:content-['']"
|
||||||
>
|
>
|
||||||
<Icon className="size-3.5" />
|
<Icon className="size-3.5" />
|
||||||
{label}
|
{label}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import {
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from '@renderer/components/ui/tooltip';
|
} from '@renderer/components/ui/tooltip';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { Copy, Check, Pencil, Trash2 } from 'lucide-react';
|
import { Check, Copy, Pencil, Trash2 } from 'lucide-react';
|
||||||
|
|
||||||
import type { ApiKeyEntry } from '@shared/types/extensions';
|
import type { ApiKeyEntry } from '@shared/types/extensions';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { Check, Loader2, Trash2 } from 'lucide-react';
|
|
||||||
|
|
||||||
import { Button } from '@renderer/components/ui/button';
|
import { Button } from '@renderer/components/ui/button';
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
|
|
@ -15,6 +13,7 @@ import {
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from '@renderer/components/ui/tooltip';
|
} from '@renderer/components/ui/tooltip';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
|
import { Check, Loader2, Trash2 } from 'lucide-react';
|
||||||
|
|
||||||
import type { ExtensionOperationState } from '@shared/types/extensions';
|
import type { ExtensionOperationState } from '@shared/types/extensions';
|
||||||
|
|
||||||
|
|
@ -28,7 +27,7 @@ interface InstallButtonProps {
|
||||||
errorMessage?: string;
|
errorMessage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function InstallButton({
|
export const InstallButton = ({
|
||||||
state,
|
state,
|
||||||
isInstalled,
|
isInstalled,
|
||||||
onInstall,
|
onInstall,
|
||||||
|
|
@ -36,7 +35,7 @@ export function InstallButton({
|
||||||
disabled,
|
disabled,
|
||||||
size = 'sm',
|
size = 'sm',
|
||||||
errorMessage,
|
errorMessage,
|
||||||
}: InstallButtonProps) {
|
}: InstallButtonProps) => {
|
||||||
const cliStatus = useStore((s) => s.cliStatus);
|
const cliStatus = useStore((s) => s.cliStatus);
|
||||||
const cliMissing = cliStatus !== null && !cliStatus.installed;
|
const cliMissing = cliStatus !== null && !cliStatus.installed;
|
||||||
const isDisabled = disabled || cliMissing;
|
const isDisabled = disabled || cliMissing;
|
||||||
|
|
@ -52,7 +51,7 @@ export function InstallButton({
|
||||||
if (state === 'pending') {
|
if (state === 'pending') {
|
||||||
return (
|
return (
|
||||||
<Button size={size} variant="outline" disabled>
|
<Button size={size} variant="outline" disabled>
|
||||||
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
<Loader2 className="size-3.5 animate-spin" />
|
||||||
<span className="ml-1.5">
|
<span className="ml-1.5">
|
||||||
{pendingAction === 'uninstall' ? 'Removing...' : 'Installing...'}
|
{pendingAction === 'uninstall' ? 'Removing...' : 'Installing...'}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -63,7 +62,7 @@ export function InstallButton({
|
||||||
if (state === 'success') {
|
if (state === 'success') {
|
||||||
return (
|
return (
|
||||||
<Button size={size} variant="outline" disabled className="text-green-400">
|
<Button size={size} variant="outline" disabled className="text-green-400">
|
||||||
<Check className="h-3.5 w-3.5" />
|
<Check className="size-3.5" />
|
||||||
<span className="ml-1.5">Done</span>
|
<span className="ml-1.5">Done</span>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|
@ -121,7 +120,7 @@ export function InstallButton({
|
||||||
}}
|
}}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
>
|
>
|
||||||
<Trash2 className="h-3.5 w-3.5" />
|
<Trash2 className="size-3.5" />
|
||||||
<span className="ml-1.5">Uninstall</span>
|
<span className="ml-1.5">Uninstall</span>
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -153,4 +152,4 @@ export function InstallButton({
|
||||||
}
|
}
|
||||||
|
|
||||||
return button;
|
return button;
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,8 @@
|
||||||
* InstallCountBadge — formatted download count with icon.
|
* InstallCountBadge — formatted download count with icon.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Download } from 'lucide-react';
|
|
||||||
|
|
||||||
import { formatInstallCount } from '@shared/utils/extensionNormalizers';
|
import { formatInstallCount } from '@shared/utils/extensionNormalizers';
|
||||||
|
import { Download } from 'lucide-react';
|
||||||
|
|
||||||
interface InstallCountBadgeProps {
|
interface InstallCountBadgeProps {
|
||||||
count: number;
|
count: number;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { api } from '@renderer/api';
|
||||||
import { Button } from '@renderer/components/ui/button';
|
import { Button } from '@renderer/components/ui/button';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
|
|
@ -23,7 +24,6 @@ import {
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@renderer/components/ui/select';
|
} from '@renderer/components/ui/select';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { api } from '@renderer/api';
|
|
||||||
import { Plus, Server, Trash2 } from 'lucide-react';
|
import { Plus, Server, Trash2 } from 'lucide-react';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,18 @@
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { api } from '@renderer/api';
|
||||||
import { Badge } from '@renderer/components/ui/badge';
|
import { Badge } from '@renderer/components/ui/badge';
|
||||||
import { Button } from '@renderer/components/ui/button';
|
import { Button } from '@renderer/components/ui/button';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { api } from '@renderer/api';
|
|
||||||
import { formatCompactNumber, formatRelativeTime } from '@renderer/utils/formatters';
|
import { formatCompactNumber, formatRelativeTime } from '@renderer/utils/formatters';
|
||||||
import { Cloud, Clock, Globe, KeyRound, Lock, Monitor, Star, Tag, Wrench } from 'lucide-react';
|
import { sanitizeMcpServerName } from '@shared/utils/extensionNormalizers';
|
||||||
|
import { Clock, Cloud, Globe, KeyRound, Lock, Monitor, Star, Tag, Wrench } from 'lucide-react';
|
||||||
// eslint-disable-next-line @typescript-eslint/no-deprecated -- lucide naming migration, alias is stable
|
|
||||||
import { Github as GithubIcon } from 'lucide-react';
|
import { Github as GithubIcon } from 'lucide-react';
|
||||||
|
|
||||||
import { InstallButton } from '../common/InstallButton';
|
import { InstallButton } from '../common/InstallButton';
|
||||||
import { SourceBadge } from '../common/SourceBadge';
|
import { SourceBadge } from '../common/SourceBadge';
|
||||||
import { sanitizeMcpServerName } from '@shared/utils/extensionNormalizers';
|
|
||||||
|
|
||||||
import type { McpCatalogItem, McpServerDiagnostic } from '@shared/types/extensions';
|
import type { McpCatalogItem, McpServerDiagnostic } from '@shared/types/extensions';
|
||||||
|
|
||||||
|
|
@ -82,7 +80,7 @@ export const McpServerCard = ({
|
||||||
{hasIcon && (
|
{hasIcon && (
|
||||||
<div className="flex size-9 shrink-0 items-center justify-center rounded-lg border border-border bg-surface-raised">
|
<div className="flex size-9 shrink-0 items-center justify-center rounded-lg border border-border bg-surface-raised">
|
||||||
<img
|
<img
|
||||||
src={server.iconUrl!}
|
src={server.iconUrl}
|
||||||
alt=""
|
alt=""
|
||||||
className="size-7 rounded object-contain"
|
className="size-7 rounded object-contain"
|
||||||
onError={() => setImgError(true)}
|
onError={() => setImgError(true)}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { api } from '@renderer/api';
|
||||||
|
import { Badge } from '@renderer/components/ui/badge';
|
||||||
|
import { Button } from '@renderer/components/ui/button';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
|
|
@ -12,8 +15,6 @@ import {
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from '@renderer/components/ui/dialog';
|
} from '@renderer/components/ui/dialog';
|
||||||
import { Badge } from '@renderer/components/ui/badge';
|
|
||||||
import { Button } from '@renderer/components/ui/button';
|
|
||||||
import { Input } from '@renderer/components/ui/input';
|
import { Input } from '@renderer/components/ui/input';
|
||||||
import { Label } from '@renderer/components/ui/label';
|
import { Label } from '@renderer/components/ui/label';
|
||||||
import {
|
import {
|
||||||
|
|
@ -24,12 +25,11 @@ import {
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@renderer/components/ui/select';
|
} from '@renderer/components/ui/select';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { api } from '@renderer/api';
|
import { sanitizeMcpServerName } from '@shared/utils/extensionNormalizers';
|
||||||
import { ExternalLink, Lock, Plus, Star, Trash2, Wrench } from 'lucide-react';
|
import { ExternalLink, Lock, Plus, Star, Trash2, Wrench } from 'lucide-react';
|
||||||
|
|
||||||
import { InstallButton } from '../common/InstallButton';
|
import { InstallButton } from '../common/InstallButton';
|
||||||
import { SourceBadge } from '../common/SourceBadge';
|
import { SourceBadge } from '../common/SourceBadge';
|
||||||
import { sanitizeMcpServerName } from '@shared/utils/extensionNormalizers';
|
|
||||||
|
|
||||||
import type { McpCatalogItem, McpHeaderDef, McpServerDiagnostic } from '@shared/types/extensions';
|
import type { McpCatalogItem, McpHeaderDef, McpServerDiagnostic } from '@shared/types/extensions';
|
||||||
|
|
||||||
|
|
@ -214,7 +214,7 @@ export const McpServerDetailDialog = ({
|
||||||
{hasIcon && (
|
{hasIcon && (
|
||||||
<div className="flex size-10 shrink-0 items-center justify-center rounded-lg border border-border bg-surface-raised">
|
<div className="flex size-10 shrink-0 items-center justify-center rounded-lg border border-border bg-surface-raised">
|
||||||
<img
|
<img
|
||||||
src={server.iconUrl!}
|
src={server.iconUrl}
|
||||||
alt=""
|
alt=""
|
||||||
className="size-8 rounded object-contain"
|
className="size-8 rounded object-contain"
|
||||||
onError={() => setImgError(true)}
|
onError={() => setImgError(true)}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import {
|
||||||
} from '@renderer/components/ui/select';
|
} from '@renderer/components/ui/select';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { formatRelativeTime } from '@renderer/utils/formatters';
|
import { formatRelativeTime } from '@renderer/utils/formatters';
|
||||||
|
import { sanitizeMcpServerName } from '@shared/utils/extensionNormalizers';
|
||||||
import { AlertTriangle, RefreshCw, Search, Server } from 'lucide-react';
|
import { AlertTriangle, RefreshCw, Search, Server } from 'lucide-react';
|
||||||
|
|
||||||
import { SearchInput } from '../common/SearchInput';
|
import { SearchInput } from '../common/SearchInput';
|
||||||
|
|
@ -27,7 +28,6 @@ import type {
|
||||||
McpCatalogItem,
|
McpCatalogItem,
|
||||||
McpServerDiagnostic,
|
McpServerDiagnostic,
|
||||||
} from '@shared/types/extensions';
|
} from '@shared/types/extensions';
|
||||||
import { sanitizeMcpServerName } from '@shared/utils/extensionNormalizers';
|
|
||||||
|
|
||||||
type McpSortValue = 'name-asc' | 'name-desc' | 'tools-desc';
|
type McpSortValue = 'name-asc' | 'name-desc' | 'tools-desc';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,8 @@ export const PluginCard = ({ plugin, index, onClick }: PluginCardProps): React.J
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{plugin.source === 'official' && (
|
{plugin.source === 'official' && (
|
||||||
<div className="pointer-events-none absolute -left-[1px] -top-[1px] size-16 overflow-hidden">
|
<div className="pointer-events-none absolute -left-px -top-px size-16 overflow-hidden">
|
||||||
<div className="absolute left-[-24px] top-[4px] w-[80px] rotate-[-45deg] bg-blue-500/90 text-center text-[9px] font-semibold leading-[18px] text-white shadow-sm">
|
<div className="absolute left-[-24px] top-[4px] w-[80px] -rotate-45 bg-blue-500/90 text-center text-[9px] font-semibold leading-[18px] text-white shadow-sm">
|
||||||
Official
|
Official
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,10 @@
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { api } from '@renderer/api';
|
||||||
|
import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer';
|
||||||
|
import { Badge } from '@renderer/components/ui/badge';
|
||||||
|
import { Button } from '@renderer/components/ui/button';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
|
|
@ -11,8 +15,6 @@ import {
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from '@renderer/components/ui/dialog';
|
} from '@renderer/components/ui/dialog';
|
||||||
import { Badge } from '@renderer/components/ui/badge';
|
|
||||||
import { Button } from '@renderer/components/ui/button';
|
|
||||||
import { Label } from '@renderer/components/ui/label';
|
import { Label } from '@renderer/components/ui/label';
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
|
|
@ -22,7 +24,6 @@ import {
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@renderer/components/ui/select';
|
} from '@renderer/components/ui/select';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { api } from '@renderer/api';
|
|
||||||
import {
|
import {
|
||||||
getCapabilityLabel,
|
getCapabilityLabel,
|
||||||
inferCapabilities,
|
inferCapabilities,
|
||||||
|
|
@ -34,8 +35,6 @@ import { InstallButton } from '../common/InstallButton';
|
||||||
import { InstallCountBadge } from '../common/InstallCountBadge';
|
import { InstallCountBadge } from '../common/InstallCountBadge';
|
||||||
import { SourceBadge } from '../common/SourceBadge';
|
import { SourceBadge } from '../common/SourceBadge';
|
||||||
|
|
||||||
import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer';
|
|
||||||
|
|
||||||
import type { EnrichedPlugin, InstallScope } from '@shared/types/extensions';
|
import type { EnrichedPlugin, InstallScope } from '@shared/types/extensions';
|
||||||
|
|
||||||
interface PluginDetailDialogProps {
|
interface PluginDetailDialogProps {
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ import {
|
||||||
keymap,
|
keymap,
|
||||||
lineNumbers,
|
lineNumbers,
|
||||||
} from '@codemirror/view';
|
} from '@codemirror/view';
|
||||||
import { baseEditorTheme } from '@renderer/utils/codemirrorTheme';
|
|
||||||
import { getSyncLanguageExtension } from '@renderer/utils/codemirrorLanguages';
|
import { getSyncLanguageExtension } from '@renderer/utils/codemirrorLanguages';
|
||||||
|
import { baseEditorTheme } from '@renderer/utils/codemirrorTheme';
|
||||||
|
|
||||||
const skillEditorTheme = EditorView.theme({
|
const skillEditorTheme = EditorView.theme({
|
||||||
'&': {
|
'&': {
|
||||||
|
|
|
||||||
|
|
@ -26,13 +26,13 @@ import { useStore } from '@renderer/store';
|
||||||
import { FileSearch, RotateCcw, X } from 'lucide-react';
|
import { FileSearch, RotateCcw, X } from 'lucide-react';
|
||||||
|
|
||||||
import { SkillCodeEditor } from './SkillCodeEditor';
|
import { SkillCodeEditor } from './SkillCodeEditor';
|
||||||
import { SkillReviewDialog } from './SkillReviewDialog';
|
|
||||||
import {
|
import {
|
||||||
buildSkillDraftFiles,
|
buildSkillDraftFiles,
|
||||||
buildSkillTemplate,
|
buildSkillTemplate,
|
||||||
readSkillTemplateContent,
|
readSkillTemplateContent,
|
||||||
updateSkillTemplateFrontmatter,
|
updateSkillTemplateFrontmatter,
|
||||||
} from './skillDraftUtils';
|
} from './skillDraftUtils';
|
||||||
|
import { SkillReviewDialog } from './SkillReviewDialog';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
SkillDetail,
|
SkillDetail,
|
||||||
|
|
@ -792,7 +792,7 @@ export const SkillEditorDialog = ({
|
||||||
<X className="mr-1.5 size-3.5" />
|
<X className="mr-1.5 size-3.5" />
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<div className="min-w-[16rem] flex-1">
|
<div className="min-w-64 flex-1">
|
||||||
<p className="text-sm text-text-muted">
|
<p className="text-sm text-text-muted">
|
||||||
Review the file changes first, then confirm save in the next step.
|
Review the file changes first, then confirm save in the next step.
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -255,7 +255,7 @@ export const SkillImportDialog = ({
|
||||||
<X className="mr-1.5 size-3.5" />
|
<X className="mr-1.5 size-3.5" />
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<p className="min-w-[16rem] flex-1 text-sm text-text-muted">
|
<p className="min-w-64 flex-1 text-sm text-text-muted">
|
||||||
Review the copied files first, then confirm the import in the next step.
|
Review the copied files first, then confirm the import in the next step.
|
||||||
</p>
|
</p>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import { api } from '@renderer/api';
|
||||||
import { Badge } from '@renderer/components/ui/badge';
|
import { Badge } from '@renderer/components/ui/badge';
|
||||||
import { Button } from '@renderer/components/ui/button';
|
import { Button } from '@renderer/components/ui/button';
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from '@renderer/components/ui/popover';
|
import { Popover, PopoverContent, PopoverTrigger } from '@renderer/components/ui/popover';
|
||||||
import { useStore } from '@renderer/store';
|
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||||
|
import { useStore } from '@renderer/store';
|
||||||
import {
|
import {
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
ArrowUpAZ,
|
ArrowUpAZ,
|
||||||
|
|
@ -25,8 +25,8 @@ import { SkillDetailDialog } from './SkillDetailDialog';
|
||||||
import { SkillEditorDialog } from './SkillEditorDialog';
|
import { SkillEditorDialog } from './SkillEditorDialog';
|
||||||
import { SkillImportDialog } from './SkillImportDialog';
|
import { SkillImportDialog } from './SkillImportDialog';
|
||||||
|
|
||||||
import type { SkillCatalogItem, SkillDetail } from '@shared/types/extensions';
|
|
||||||
import type { SkillsSortState } from '@renderer/hooks/useExtensionsTabState';
|
import type { SkillsSortState } from '@renderer/hooks/useExtensionsTabState';
|
||||||
|
import type { SkillCatalogItem, SkillDetail } from '@shared/types/extensions';
|
||||||
|
|
||||||
const SUCCESS_BANNER_MS = 2500;
|
const SUCCESS_BANNER_MS = 2500;
|
||||||
const NEW_SKILL_HIGHLIGHT_MS = 4000;
|
const NEW_SKILL_HIGHLIGHT_MS = 4000;
|
||||||
|
|
@ -233,7 +233,7 @@ export const SkillsPanel = ({
|
||||||
|
|
||||||
<div className="flex w-full flex-col gap-3 xl:w-auto xl:min-w-[32rem] xl:max-w-[40rem]">
|
<div className="flex w-full flex-col gap-3 xl:w-auto xl:min-w-[32rem] xl:max-w-[40rem]">
|
||||||
<div className="flex flex-col gap-3 lg:flex-row lg:flex-wrap lg:items-center xl:justify-end">
|
<div className="flex flex-col gap-3 lg:flex-row lg:flex-wrap lg:items-center xl:justify-end">
|
||||||
<div className="w-full lg:min-w-[18rem] lg:flex-1 xl:w-80 xl:flex-none">
|
<div className="w-full lg:min-w-72 lg:flex-1 xl:w-80 xl:flex-none">
|
||||||
<SearchInput
|
<SearchInput
|
||||||
value={skillsSearchQuery}
|
value={skillsSearchQuery}
|
||||||
onChange={setSkillsSearchQuery}
|
onChange={setSkillsSearchQuery}
|
||||||
|
|
@ -318,7 +318,7 @@ export const SkillsPanel = ({
|
||||||
['personal', 'Personal'],
|
['personal', 'Personal'],
|
||||||
['needs-attention', 'Needs attention'],
|
['needs-attention', 'Needs attention'],
|
||||||
['has-scripts', 'Has scripts'],
|
['has-scripts', 'Has scripts'],
|
||||||
] as Array<[SkillsQuickFilter, string]>
|
] as [SkillsQuickFilter, string][]
|
||||||
).map(([value, label]) => (
|
).map(([value, label]) => (
|
||||||
<Button
|
<Button
|
||||||
key={value}
|
key={value}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ export function buildSkillTemplate(input: SkillDraftTemplateInput): string {
|
||||||
|
|
||||||
export function readSkillTemplateContent(rawContent: string): SkillTemplateParseResult {
|
export function readSkillTemplateContent(rawContent: string): SkillTemplateParseResult {
|
||||||
const content = rawContent.replace(/^\uFEFF/u, '');
|
const content = rawContent.replace(/^\uFEFF/u, '');
|
||||||
const match = content.match(SKILL_FRONTMATTER_PATTERN);
|
const match = SKILL_FRONTMATTER_PATTERN.exec(content);
|
||||||
if (!match) {
|
if (!match) {
|
||||||
return {
|
return {
|
||||||
hasStructuredSections: false,
|
hasStructuredSections: false,
|
||||||
|
|
@ -130,7 +130,7 @@ export function updateSkillTemplateFrontmatter(
|
||||||
input: SkillDraftTemplateInput
|
input: SkillDraftTemplateInput
|
||||||
): string {
|
): string {
|
||||||
const content = rawContent.replace(/^\uFEFF/u, '');
|
const content = rawContent.replace(/^\uFEFF/u, '');
|
||||||
const match = content.match(SKILL_FRONTMATTER_PATTERN);
|
const match = SKILL_FRONTMATTER_PATTERN.exec(content);
|
||||||
const body = match ? (match[2] ?? '') : content;
|
const body = match ? (match[2] ?? '') : content;
|
||||||
|
|
||||||
let data: Record<string, unknown> = {};
|
let data: Record<string, unknown> = {};
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import { useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { isElectronMode } from '@renderer/api';
|
import { isElectronMode } from '@renderer/api';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { Bell, Puzzle, Settings, Users, PanelRight } from 'lucide-react';
|
import { Bell, PanelRight, Puzzle, Settings, Users } from 'lucide-react';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
|
|
||||||
import { MoreMenu } from './MoreMenu';
|
import { MoreMenu } from './MoreMenu';
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,10 @@ import {
|
||||||
Zap,
|
Zap,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
|
import { LaunchTeamDialog } from '../team/dialogs/LaunchTeamDialog';
|
||||||
import { ScheduleRunLogDialog } from '../team/schedule/ScheduleRunLogDialog';
|
import { ScheduleRunLogDialog } from '../team/schedule/ScheduleRunLogDialog';
|
||||||
import { ScheduleRunRow } from '../team/schedule/ScheduleRunRow';
|
import { ScheduleRunRow } from '../team/schedule/ScheduleRunRow';
|
||||||
import { ScheduleStatusBadge } from '../team/schedule/ScheduleStatusBadge';
|
import { ScheduleStatusBadge } from '../team/schedule/ScheduleStatusBadge';
|
||||||
import { LaunchTeamDialog } from '../team/dialogs/LaunchTeamDialog';
|
|
||||||
|
|
||||||
import type { Schedule, ScheduleRun, ScheduleStatus } from '@shared/types';
|
import type { Schedule, ScheduleRun, ScheduleStatus } from '@shared/types';
|
||||||
|
|
||||||
|
|
@ -34,7 +34,7 @@ import type { Schedule, ScheduleRun, ScheduleStatus } from '@shared/types';
|
||||||
// Constants
|
// Constants
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
const STATUS_OPTIONS: Array<{ value: ScheduleStatus | 'all'; label: string }> = [
|
const STATUS_OPTIONS: { value: ScheduleStatus | 'all'; label: string }[] = [
|
||||||
{ value: 'all', label: 'All' },
|
{ value: 'all', label: 'All' },
|
||||||
{ value: 'active', label: 'Active' },
|
{ value: 'active', label: 'Active' },
|
||||||
{ value: 'paused', label: 'Paused' },
|
{ value: 'paused', label: 'Paused' },
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ export const ConfigEditorDialog = ({
|
||||||
keymap.of([...defaultKeymap, ...historyKeymap, ...foldKeymap, ...searchKeymap]),
|
keymap.of([...defaultKeymap, ...historyKeymap, ...foldKeymap, ...searchKeymap]),
|
||||||
baseEditorTheme,
|
baseEditorTheme,
|
||||||
configEditorTheme,
|
configEditorTheme,
|
||||||
// eslint-disable-next-line sonarjs/no-nested-functions -- CodeMirror listener callback within useEffect setup
|
|
||||||
EditorView.updateListener.of((update) => {
|
EditorView.updateListener.of((update) => {
|
||||||
if (update.docChanged) {
|
if (update.docChanged) {
|
||||||
const text = update.state.doc.toString();
|
const text = update.state.doc.toString();
|
||||||
|
|
|
||||||
|
|
@ -14,18 +14,18 @@ import {
|
||||||
ArrowRightLeft,
|
ArrowRightLeft,
|
||||||
Bell,
|
Bell,
|
||||||
BellRing,
|
BellRing,
|
||||||
|
CheckCircle2,
|
||||||
|
CirclePlus,
|
||||||
Clock,
|
Clock,
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
EyeOff,
|
EyeOff,
|
||||||
|
GitBranch,
|
||||||
HelpCircle,
|
HelpCircle,
|
||||||
Inbox,
|
Inbox,
|
||||||
Info,
|
Info,
|
||||||
Mail,
|
Mail,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
PartyPopper,
|
PartyPopper,
|
||||||
CirclePlus,
|
|
||||||
CheckCircle2,
|
|
||||||
GitBranch,
|
|
||||||
Send,
|
Send,
|
||||||
Users,
|
Users,
|
||||||
Volume2,
|
Volume2,
|
||||||
|
|
@ -39,7 +39,9 @@ import type { NotificationTrigger } from '@renderer/types/data';
|
||||||
import type { TeamReviewState, TeamTaskStatus } from '@shared/types';
|
import type { TeamReviewState, TeamTaskStatus } from '@shared/types';
|
||||||
|
|
||||||
/** Notification targets span workflow status plus the explicit review axis. */
|
/** Notification targets span workflow status plus the explicit review axis. */
|
||||||
type NotifiableStatus = TeamTaskStatus | Extract<TeamReviewState, 'review' | 'needsFix' | 'approved'>;
|
type NotifiableStatus =
|
||||||
|
| TeamTaskStatus
|
||||||
|
| Extract<TeamReviewState, 'review' | 'needsFix' | 'approved'>;
|
||||||
|
|
||||||
// Snooze duration options
|
// Snooze duration options
|
||||||
const SNOOZE_OPTIONS = [
|
const SNOOZE_OPTIONS = [
|
||||||
|
|
|
||||||
|
|
@ -358,7 +358,6 @@ export const GlobalTaskList = ({
|
||||||
// Reset showArchived when archive becomes empty
|
// Reset showArchived when archive becomes empty
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (showArchived && !hasArchivedTasks) {
|
if (showArchived && !hasArchivedTasks) {
|
||||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- intentional sync on prop change
|
|
||||||
setShowArchived(false);
|
setShowArchived(false);
|
||||||
}
|
}
|
||||||
}, [showArchived, hasArchivedTasks]);
|
}, [showArchived, hasArchivedTasks]);
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { getTeamColorSet } from '@renderer/constants/teamColors';
|
||||||
import { useTheme } from '@renderer/hooks/useTheme';
|
import { useTheme } from '@renderer/hooks/useTheme';
|
||||||
import { useUnreadCommentCount } from '@renderer/hooks/useUnreadCommentCount';
|
import { useUnreadCommentCount } from '@renderer/hooks/useUnreadCommentCount';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { REVIEW_STATE_DISPLAY, buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
import { buildMemberColorMap, REVIEW_STATE_DISPLAY } from '@renderer/utils/memberHelpers';
|
||||||
import { nameColorSet } from '@renderer/utils/projectColor';
|
import { nameColorSet } from '@renderer/utils/projectColor';
|
||||||
import { projectColor } from '@renderer/utils/projectColor';
|
import { projectColor } from '@renderer/utils/projectColor';
|
||||||
import { projectLabelFromPath } from '@renderer/utils/taskGrouping';
|
import { projectLabelFromPath } from '@renderer/utils/taskGrouping';
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer
|
||||||
import { MemberBadge } from '@renderer/components/team/MemberBadge';
|
import { MemberBadge } from '@renderer/components/team/MemberBadge';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { REVIEW_STATE_DISPLAY, buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
import { buildMemberColorMap, REVIEW_STATE_DISPLAY } from '@renderer/utils/memberHelpers';
|
||||||
import { linkifyTaskIdsInMarkdown } from '@renderer/utils/taskReferenceUtils';
|
import { linkifyTaskIdsInMarkdown } from '@renderer/utils/taskReferenceUtils';
|
||||||
import { getTaskKanbanColumn } from '@shared/utils/reviewState';
|
import { getTaskKanbanColumn } from '@shared/utils/reviewState';
|
||||||
import { formatTaskDisplayLabel, taskMatchesRef } from '@shared/utils/taskIdentity';
|
import { formatTaskDisplayLabel, taskMatchesRef } from '@shared/utils/taskIdentity';
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import {
|
||||||
type TaskChangeRequestOptions,
|
type TaskChangeRequestOptions,
|
||||||
} from '@renderer/utils/taskChangeRequest';
|
} from '@renderer/utils/taskChangeRequest';
|
||||||
import { stripAgentBlocks } from '@shared/constants/agentBlocks';
|
import { stripAgentBlocks } from '@shared/constants/agentBlocks';
|
||||||
|
import { isLeadAgentType, isLeadMember } from '@shared/utils/leadDetection';
|
||||||
import { deriveTaskDisplayId, formatTaskDisplayLabel } from '@shared/utils/taskIdentity';
|
import { deriveTaskDisplayId, formatTaskDisplayLabel } from '@shared/utils/taskIdentity';
|
||||||
import {
|
import {
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
|
|
@ -50,11 +51,9 @@ import {
|
||||||
UserPlus,
|
UserPlus,
|
||||||
Users,
|
Users,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { isLeadAgentType, isLeadMember } from '@shared/utils/leadDetection';
|
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
|
|
||||||
import { AddMemberDialog } from './dialogs/AddMemberDialog';
|
import { AddMemberDialog } from './dialogs/AddMemberDialog';
|
||||||
import type { AddMemberEntry } from './dialogs/AddMemberDialog';
|
|
||||||
import { CreateTaskDialog } from './dialogs/CreateTaskDialog';
|
import { CreateTaskDialog } from './dialogs/CreateTaskDialog';
|
||||||
import { EditTeamDialog } from './dialogs/EditTeamDialog';
|
import { EditTeamDialog } from './dialogs/EditTeamDialog';
|
||||||
import { LaunchTeamDialog } from './dialogs/LaunchTeamDialog';
|
import { LaunchTeamDialog } from './dialogs/LaunchTeamDialog';
|
||||||
|
|
@ -67,16 +66,18 @@ import { KanbanSearchInput } from './kanban/KanbanSearchInput';
|
||||||
import { TrashDialog } from './kanban/TrashDialog';
|
import { TrashDialog } from './kanban/TrashDialog';
|
||||||
import { MemberDetailDialog } from './members/MemberDetailDialog';
|
import { MemberDetailDialog } from './members/MemberDetailDialog';
|
||||||
|
|
||||||
|
import type { AddMemberEntry } from './dialogs/AddMemberDialog';
|
||||||
|
|
||||||
const ProjectEditorOverlay = lazy(() =>
|
const ProjectEditorOverlay = lazy(() =>
|
||||||
import('./editor/ProjectEditorOverlay').then((m) => ({ default: m.ProjectEditorOverlay }))
|
import('./editor/ProjectEditorOverlay').then((m) => ({ default: m.ProjectEditorOverlay }))
|
||||||
);
|
);
|
||||||
import { MemberList } from './members/MemberList';
|
import { MemberList } from './members/MemberList';
|
||||||
import { MessagesPanel } from './messages/MessagesPanel';
|
import { MessagesPanel } from './messages/MessagesPanel';
|
||||||
import { ChangeReviewDialog } from './review/ChangeReviewDialog';
|
import { ChangeReviewDialog } from './review/ChangeReviewDialog';
|
||||||
|
import { ScheduleSection } from './schedule/ScheduleSection';
|
||||||
import { ClaudeLogsSection } from './ClaudeLogsSection';
|
import { ClaudeLogsSection } from './ClaudeLogsSection';
|
||||||
import { CollapsibleTeamSection } from './CollapsibleTeamSection';
|
import { CollapsibleTeamSection } from './CollapsibleTeamSection';
|
||||||
import { ProcessesSection } from './ProcessesSection';
|
import { ProcessesSection } from './ProcessesSection';
|
||||||
import { ScheduleSection } from './schedule/ScheduleSection';
|
|
||||||
import { TeamProvisioningBanner } from './TeamProvisioningBanner';
|
import { TeamProvisioningBanner } from './TeamProvisioningBanner';
|
||||||
import { TeamSessionsSection } from './TeamSessionsSection';
|
import { TeamSessionsSection } from './TeamSessionsSection';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ export const TeamProvisioningBanner = ({
|
||||||
|
|
||||||
if (isReady) {
|
if (isReady) {
|
||||||
const readyMessage = allTeammatesOnline
|
const readyMessage = allTeammatesOnline
|
||||||
? `Team launched — all ${teamMembers!.length} teammates online`
|
? `Team launched — all ${teamMembers.length} teammates online`
|
||||||
: 'Team launched — teammates may still be starting';
|
: 'Team launched — teammates may still be starting';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ import { useTheme } from '@renderer/hooks/useTheme';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { FileText, Search, Terminal } from 'lucide-react';
|
import { FileText, Search, Terminal } from 'lucide-react';
|
||||||
|
|
||||||
import type { ToolApprovalRequest } from '@shared/types';
|
|
||||||
|
|
||||||
import { ToolApprovalSettingsPanel } from './dialogs/ToolApprovalSettingsPanel';
|
import { ToolApprovalSettingsPanel } from './dialogs/ToolApprovalSettingsPanel';
|
||||||
|
|
||||||
|
import type { ToolApprovalRequest } from '@shared/types';
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Tool icon mapping
|
// Tool icon mapping
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ export const ActiveTasksBlock = ({
|
||||||
className="size-5 rounded-full bg-[var(--color-surface-raised)]"
|
className="size-5 rounded-full bg-[var(--color-surface-raised)]"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
<span className="absolute -bottom-0.5 -right-0.5 flex h-2.5 w-2.5">
|
<span className="absolute -bottom-0.5 -right-0.5 flex size-2.5">
|
||||||
<span
|
<span
|
||||||
className={`absolute inline-flex size-full animate-ping rounded-full ${dotPing} opacity-70`}
|
className={`absolute inline-flex size-full animate-ping rounded-full ${dotPing} opacity-70`}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -99,13 +99,13 @@ export function getCrossTeamSentMemberName(value: string | undefined): string |
|
||||||
return parseQualifiedRecipient(value)?.memberName ?? null;
|
return parseQualifiedRecipient(value)?.memberName ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function CrossTeamTeamBadge({
|
const CrossTeamTeamBadge = ({
|
||||||
teamName,
|
teamName,
|
||||||
onClick,
|
onClick,
|
||||||
}: {
|
}: {
|
||||||
teamName: string;
|
teamName: string;
|
||||||
onClick?: (teamName: string) => void;
|
onClick?: (teamName: string) => void;
|
||||||
}): React.JSX.Element {
|
}): React.JSX.Element => {
|
||||||
if (onClick) {
|
if (onClick) {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
|
@ -135,7 +135,7 @@ function CrossTeamTeamBadge({
|
||||||
{teamName}
|
{teamName}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ActivityItemProps {
|
interface ActivityItemProps {
|
||||||
message: InboxMessage;
|
message: InboxMessage;
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,8 @@ import {
|
||||||
import { toMessageKey } from '@renderer/utils/teamMessageKey';
|
import { toMessageKey } from '@renderer/utils/teamMessageKey';
|
||||||
import { Layers } from 'lucide-react';
|
import { Layers } from 'lucide-react';
|
||||||
|
|
||||||
import { buildMessageContext, resolveMessageRenderProps } from './activityMessageContext';
|
|
||||||
|
|
||||||
import { ActivityItem, isNoiseMessage } from './ActivityItem';
|
import { ActivityItem, isNoiseMessage } from './ActivityItem';
|
||||||
|
import { buildMessageContext, resolveMessageRenderProps } from './activityMessageContext';
|
||||||
import { AnimatedHeightReveal } from './AnimatedHeightReveal';
|
import { AnimatedHeightReveal } from './AnimatedHeightReveal';
|
||||||
import { findNewestMessageIndex, resolveTimelineCollapseState } from './collapseState';
|
import { findNewestMessageIndex, resolveTimelineCollapseState } from './collapseState';
|
||||||
import {
|
import {
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,10 @@ import {
|
||||||
areThoughtMessagesEquivalentForRender,
|
areThoughtMessagesEquivalentForRender,
|
||||||
} from '@renderer/utils/messageRenderEquality';
|
} from '@renderer/utils/messageRenderEquality';
|
||||||
import { toMessageKey } from '@renderer/utils/teamMessageKey';
|
import { toMessageKey } from '@renderer/utils/teamMessageKey';
|
||||||
import { formatToolSummary, parseToolSummary } from '@shared/utils/toolSummary';
|
|
||||||
import { extractMarkdownPlainText } from '@shared/utils/markdownTextSearch';
|
import { extractMarkdownPlainText } from '@shared/utils/markdownTextSearch';
|
||||||
|
import { formatToolSummary, parseToolSummary } from '@shared/utils/toolSummary';
|
||||||
import { ChevronDown, ChevronRight, ChevronUp, Maximize2 } from 'lucide-react';
|
import { ChevronDown, ChevronRight, ChevronUp, Maximize2 } from 'lucide-react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AnimatedHeightReveal,
|
AnimatedHeightReveal,
|
||||||
ENTRY_REVEAL_ANIMATION_MS,
|
ENTRY_REVEAL_ANIMATION_MS,
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,13 @@ import { CARD_ICON_MUTED } from '@renderer/constants/cssVariables';
|
||||||
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
||||||
import { agentAvatarUrl } from '@renderer/utils/memberHelpers';
|
import { agentAvatarUrl } from '@renderer/utils/memberHelpers';
|
||||||
|
|
||||||
|
import { MemberBadge } from '../MemberBadge';
|
||||||
|
|
||||||
import { ActivityItem } from './ActivityItem';
|
import { ActivityItem } from './ActivityItem';
|
||||||
import { buildMessageContext, resolveMessageRenderProps } from './activityMessageContext';
|
import { buildMessageContext, resolveMessageRenderProps } from './activityMessageContext';
|
||||||
import { MemberBadge } from '../MemberBadge';
|
|
||||||
import { ThoughtBodyContent } from './ThoughtBodyContent';
|
import { ThoughtBodyContent } from './ThoughtBodyContent';
|
||||||
|
|
||||||
import type { TimelineItem, LeadThoughtGroup } from './LeadThoughtsGroup';
|
import type { LeadThoughtGroup, TimelineItem } from './LeadThoughtsGroup';
|
||||||
import type { InboxMessage, ResolvedTeamMember } from '@shared/types';
|
import type { InboxMessage, ResolvedTeamMember } from '@shared/types';
|
||||||
|
|
||||||
function formatTime(timestamp: string): string {
|
function formatTime(timestamp: string): string {
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ export const PendingRepliesBlock = ({
|
||||||
className="size-5 rounded-full bg-[var(--color-surface-raised)]"
|
className="size-5 rounded-full bg-[var(--color-surface-raised)]"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
<span className="absolute -bottom-0.5 -right-0.5 flex h-2.5 w-2.5">
|
<span className="absolute -bottom-0.5 -right-0.5 flex size-2.5">
|
||||||
<span className="absolute inline-flex size-full animate-ping rounded-full bg-emerald-400 opacity-70" />
|
<span className="absolute inline-flex size-full animate-ping rounded-full bg-emerald-400 opacity-70" />
|
||||||
<span className="relative inline-flex size-full rounded-full bg-emerald-500" />
|
<span className="relative inline-flex size-full rounded-full bg-emerald-500" />
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -151,7 +151,7 @@ export const PendingRepliesBlock = ({
|
||||||
<div className="flex items-center gap-2 px-3 py-2">
|
<div className="flex items-center gap-2 px-3 py-2">
|
||||||
<span className="relative inline-flex shrink-0 items-center justify-center rounded-full bg-[var(--color-surface-raised)] p-1">
|
<span className="relative inline-flex shrink-0 items-center justify-center rounded-full bg-[var(--color-surface-raised)] p-1">
|
||||||
<Users size={12} style={{ color: colors.border }} />
|
<Users size={12} style={{ color: colors.border }} />
|
||||||
<span className="absolute -bottom-0.5 -right-0.5 flex h-2.5 w-2.5">
|
<span className="absolute -bottom-0.5 -right-0.5 flex size-2.5">
|
||||||
<span className="absolute inline-flex size-full animate-ping rounded-full bg-emerald-400 opacity-70" />
|
<span className="absolute inline-flex size-full animate-ping rounded-full bg-emerald-400 opacity-70" />
|
||||||
<span className="relative inline-flex size-full rounded-full bg-emerald-500" />
|
<span className="relative inline-flex size-full rounded-full bg-emerald-500" />
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@ import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer
|
||||||
import { CopyButton } from '@renderer/components/common/CopyButton';
|
import { CopyButton } from '@renderer/components/common/CopyButton';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||||
import { CARD_ICON_MUTED, CARD_TEXT_LIGHT } from '@renderer/constants/cssVariables';
|
import { CARD_ICON_MUTED, CARD_TEXT_LIGHT } from '@renderer/constants/cssVariables';
|
||||||
|
import { linkifyAllMentionsInMarkdown } from '@renderer/utils/mentionLinkify';
|
||||||
import {
|
import {
|
||||||
areStringArraysEqual,
|
areStringArraysEqual,
|
||||||
areStringMapsEqual,
|
areStringMapsEqual,
|
||||||
areThoughtMessagesEquivalentForRender,
|
areThoughtMessagesEquivalentForRender,
|
||||||
} from '@renderer/utils/messageRenderEquality';
|
} from '@renderer/utils/messageRenderEquality';
|
||||||
import { linkifyAllMentionsInMarkdown } from '@renderer/utils/mentionLinkify';
|
|
||||||
import { linkifyTaskIdsInMarkdown, parseTaskLinkHref } from '@renderer/utils/taskReferenceUtils';
|
import { linkifyTaskIdsInMarkdown, parseTaskLinkHref } from '@renderer/utils/taskReferenceUtils';
|
||||||
import { Reply } from 'lucide-react';
|
import { Reply } from 'lucide-react';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import 'yet-another-react-lightbox/styles.css';
|
import 'yet-another-react-lightbox/styles.css';
|
||||||
import 'yet-another-react-lightbox/plugins/counter.css';
|
import 'yet-another-react-lightbox/plugins/counter.css';
|
||||||
|
|
||||||
import { createContext, useContext, useEffect, useRef, useMemo } from 'react';
|
import { createContext, useContext, useEffect, useMemo, useRef } from 'react';
|
||||||
|
|
||||||
import Lightbox from 'yet-another-react-lightbox';
|
import Lightbox from 'yet-another-react-lightbox';
|
||||||
import Counter from 'yet-another-react-lightbox/plugins/counter';
|
import Counter from 'yet-another-react-lightbox/plugins/counter';
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { Info } from 'lucide-react';
|
|
||||||
|
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||||
|
import { Info } from 'lucide-react';
|
||||||
|
|
||||||
import { AttachmentDisplay } from './AttachmentDisplay';
|
import { AttachmentDisplay } from './AttachmentDisplay';
|
||||||
|
|
||||||
|
|
@ -27,9 +26,7 @@ export const SourceMessageAttachments = ({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const truncatedText =
|
const truncatedText =
|
||||||
sourceMessage.text.length > 300
|
sourceMessage.text.length > 300 ? sourceMessage.text.slice(0, 297) + '...' : sourceMessage.text;
|
||||||
? sourceMessage.text.slice(0, 297) + '...'
|
|
||||||
: sourceMessage.text;
|
|
||||||
|
|
||||||
const formattedDate = (() => {
|
const formattedDate = (() => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { getNextSuggestedMemberName } from '@renderer/components/team/members/memberNameSets';
|
||||||
import {
|
import {
|
||||||
buildMembersFromDrafts,
|
buildMembersFromDrafts,
|
||||||
createMemberDraft,
|
createMemberDraft,
|
||||||
MembersEditorSection,
|
MembersEditorSection,
|
||||||
validateMemberNameInline,
|
validateMemberNameInline,
|
||||||
} from '@renderer/components/team/members/MembersEditorSection';
|
} from '@renderer/components/team/members/MembersEditorSection';
|
||||||
import { getNextSuggestedMemberName } from '@renderer/components/team/members/memberNameSets';
|
|
||||||
import { Button } from '@renderer/components/ui/button';
|
import { Button } from '@renderer/components/ui/button';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,8 @@ import { LimitContextCheckbox } from './LimitContextCheckbox';
|
||||||
import { OptionalSettingsSection } from './OptionalSettingsSection';
|
import { OptionalSettingsSection } from './OptionalSettingsSection';
|
||||||
import { ProjectPathSelector } from './ProjectPathSelector';
|
import { ProjectPathSelector } from './ProjectPathSelector';
|
||||||
import { SkipPermissionsCheckbox } from './SkipPermissionsCheckbox';
|
import { SkipPermissionsCheckbox } from './SkipPermissionsCheckbox';
|
||||||
import { getNextSuggestedTeamName } from './teamNameSets';
|
|
||||||
import { computeEffectiveTeamModel, TeamModelSelector } from './TeamModelSelector';
|
import { computeEffectiveTeamModel, TeamModelSelector } from './TeamModelSelector';
|
||||||
|
import { getNextSuggestedTeamName } from './teamNameSets';
|
||||||
|
|
||||||
import type { MemberDraft } from '@renderer/components/team/members/membersEditorTypes';
|
import type { MemberDraft } from '@renderer/components/team/members/membersEditorTypes';
|
||||||
|
|
||||||
|
|
@ -694,7 +694,7 @@ export const CreateTeamDialog = ({
|
||||||
if (!validation.valid) {
|
if (!validation.valid) {
|
||||||
const errors = validation.errors ?? {};
|
const errors = validation.errors ?? {};
|
||||||
setFieldErrors(errors);
|
setFieldErrors(errors);
|
||||||
const messages = Object.values(errors).filter(Boolean) as string[];
|
const messages = Object.values(errors).filter(Boolean);
|
||||||
setLocalError(messages.join(' · ') || 'Check form fields');
|
setLocalError(messages.join(' · ') || 'Check form fields');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -745,7 +745,7 @@ export const CreateTeamDialog = ({
|
||||||
if (!prev.teamName) return prev;
|
if (!prev.teamName) return prev;
|
||||||
// eslint-disable-next-line sonarjs/no-unused-vars -- destructured to omit teamName from rest
|
// eslint-disable-next-line sonarjs/no-unused-vars -- destructured to omit teamName from rest
|
||||||
const { teamName: _teamName, ...rest } = prev;
|
const { teamName: _teamName, ...rest } = prev;
|
||||||
const remaining = Object.values(rest).filter(Boolean) as string[];
|
const remaining = Object.values(rest).filter(Boolean);
|
||||||
if (remaining.length === 0) {
|
if (remaining.length === 0) {
|
||||||
setLocalError(null);
|
setLocalError(null);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,12 @@ import {
|
||||||
import { Input } from '@renderer/components/ui/input';
|
import { Input } from '@renderer/components/ui/input';
|
||||||
import { Label } from '@renderer/components/ui/label';
|
import { Label } from '@renderer/components/ui/label';
|
||||||
import { MentionableTextarea } from '@renderer/components/ui/MentionableTextarea';
|
import { MentionableTextarea } from '@renderer/components/ui/MentionableTextarea';
|
||||||
|
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
||||||
import { useChipDraftPersistence } from '@renderer/hooks/useChipDraftPersistence';
|
import { useChipDraftPersistence } from '@renderer/hooks/useChipDraftPersistence';
|
||||||
import { useDraftPersistence } from '@renderer/hooks/useDraftPersistence';
|
import { useDraftPersistence } from '@renderer/hooks/useDraftPersistence';
|
||||||
import { useFileListCacheWarmer } from '@renderer/hooks/useFileListCacheWarmer';
|
import { useFileListCacheWarmer } from '@renderer/hooks/useFileListCacheWarmer';
|
||||||
import { useTheme } from '@renderer/hooks/useTheme';
|
import { useTheme } from '@renderer/hooks/useTheme';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
|
||||||
import { formatAgentRole } from '@renderer/utils/formatAgentRole';
|
import { formatAgentRole } from '@renderer/utils/formatAgentRole';
|
||||||
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
||||||
import { normalizePath } from '@renderer/utils/pathNormalize';
|
import { normalizePath } from '@renderer/utils/pathNormalize';
|
||||||
|
|
@ -38,12 +38,13 @@ import {
|
||||||
X,
|
X,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
|
import { CronScheduleInput } from '../schedule/CronScheduleInput';
|
||||||
|
|
||||||
import { AdvancedCliSection } from './AdvancedCliSection';
|
import { AdvancedCliSection } from './AdvancedCliSection';
|
||||||
import { EffortLevelSelector } from './EffortLevelSelector';
|
import { EffortLevelSelector } from './EffortLevelSelector';
|
||||||
import { OptionalSettingsSection } from './OptionalSettingsSection';
|
import { OptionalSettingsSection } from './OptionalSettingsSection';
|
||||||
import { ProjectPathSelector } from './ProjectPathSelector';
|
import { ProjectPathSelector } from './ProjectPathSelector';
|
||||||
import { computeEffectiveTeamModel, TeamModelSelector } from './TeamModelSelector';
|
import { computeEffectiveTeamModel, TeamModelSelector } from './TeamModelSelector';
|
||||||
import { CronScheduleInput } from '../schedule/CronScheduleInput';
|
|
||||||
|
|
||||||
import type { ActiveTeamRef } from './CreateTeamDialog';
|
import type { ActiveTeamRef } from './CreateTeamDialog';
|
||||||
import type { MentionSuggestion } from '@renderer/types/mention';
|
import type { MentionSuggestion } from '@renderer/types/mention';
|
||||||
|
|
@ -112,7 +113,7 @@ export const LaunchTeamDialog = (props: LaunchTeamDialogProps): React.JSX.Elemen
|
||||||
const { isLight } = useTheme();
|
const { isLight } = useTheme();
|
||||||
const isLaunch = props.mode === 'launch';
|
const isLaunch = props.mode === 'launch';
|
||||||
const isSchedule = props.mode === 'schedule';
|
const isSchedule = props.mode === 'schedule';
|
||||||
const schedule = isSchedule ? ((props as LaunchDialogScheduleMode).schedule ?? null) : null;
|
const schedule = isSchedule ? (props.schedule ?? null) : null;
|
||||||
const isEditing = isSchedule && !!schedule;
|
const isEditing = isSchedule && !!schedule;
|
||||||
|
|
||||||
// Team name: always present for launch mode, may be absent in schedule mode (standalone page)
|
// Team name: always present for launch mode, may be absent in schedule mode (standalone page)
|
||||||
|
|
@ -338,7 +339,7 @@ export const LaunchTeamDialog = (props: LaunchTeamDialogProps): React.JSX.Elemen
|
||||||
// Clear stale provisioning error when dialog opens
|
// Clear stale provisioning error when dialog opens
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open || !isLaunch) return;
|
if (!open || !isLaunch) return;
|
||||||
(props as LaunchDialogLaunchMode).clearProvisioningError?.(effectiveTeamName);
|
props.clearProvisioningError?.(effectiveTeamName);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [open, isLaunch, effectiveTeamName]);
|
}, [open, isLaunch, effectiveTeamName]);
|
||||||
|
|
||||||
|
|
@ -443,9 +444,7 @@ export const LaunchTeamDialog = (props: LaunchTeamDialogProps): React.JSX.Elemen
|
||||||
}, [open, repositoryGroups]);
|
}, [open, repositoryGroups]);
|
||||||
|
|
||||||
// Pre-select defaultProjectPath (launch mode) or first project
|
// Pre-select defaultProjectPath (launch mode) or first project
|
||||||
const defaultProjectPath = isLaunch
|
const defaultProjectPath = isLaunch ? props.defaultProjectPath : undefined;
|
||||||
? (props as LaunchDialogLaunchMode).defaultProjectPath
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open || cwdMode !== 'project' || selectedProjectPath || projects.length === 0) return;
|
if (!open || cwdMode !== 'project' || selectedProjectPath || projects.length === 0) return;
|
||||||
|
|
@ -466,7 +465,7 @@ export const LaunchTeamDialog = (props: LaunchTeamDialogProps): React.JSX.Elemen
|
||||||
// Launch-only: conflict detection
|
// Launch-only: conflict detection
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const activeTeams = isLaunch ? (props as LaunchDialogLaunchMode).activeTeams : undefined;
|
const activeTeams = isLaunch ? props.activeTeams : undefined;
|
||||||
|
|
||||||
const conflictingTeam = useMemo(() => {
|
const conflictingTeam = useMemo(() => {
|
||||||
if (!isLaunch || !activeTeams?.length || !effectiveCwd) return null;
|
if (!isLaunch || !activeTeams?.length || !effectiveCwd) return null;
|
||||||
|
|
@ -487,7 +486,7 @@ export const LaunchTeamDialog = (props: LaunchTeamDialogProps): React.JSX.Elemen
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const storeMembers = useStore((s) => s.selectedTeamData?.members ?? []);
|
const storeMembers = useStore((s) => s.selectedTeamData?.members ?? []);
|
||||||
const members = isLaunch ? (props as LaunchDialogLaunchMode).members : storeMembers;
|
const members = isLaunch ? props.members : storeMembers;
|
||||||
|
|
||||||
const colorMap = useMemo(() => buildMemberColorMap(members), [members]);
|
const colorMap = useMemo(() => buildMemberColorMap(members), [members]);
|
||||||
const mentionSuggestions = useMemo<MentionSuggestion[]>(
|
const mentionSuggestions = useMemo<MentionSuggestion[]>(
|
||||||
|
|
@ -564,7 +563,7 @@ export const LaunchTeamDialog = (props: LaunchTeamDialogProps): React.JSX.Elemen
|
||||||
// Error
|
// Error
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const provisioningError = isLaunch ? (props as LaunchDialogLaunchMode).provisioningError : null;
|
const provisioningError = isLaunch ? props.provisioningError : null;
|
||||||
const activeError = localError ?? provisioningError;
|
const activeError = localError ?? provisioningError;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
@ -586,7 +585,7 @@ export const LaunchTeamDialog = (props: LaunchTeamDialogProps): React.JSX.Elemen
|
||||||
void (async () => {
|
void (async () => {
|
||||||
try {
|
try {
|
||||||
if (isLaunch) {
|
if (isLaunch) {
|
||||||
await (props as LaunchDialogLaunchMode).onLaunch({
|
await props.onLaunch({
|
||||||
teamName: effectiveTeamName,
|
teamName: effectiveTeamName,
|
||||||
cwd: effectiveCwd,
|
cwd: effectiveCwd,
|
||||||
prompt: promptDraft.value.trim() || undefined,
|
prompt: promptDraft.value.trim() || undefined,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Info } from 'lucide-react';
|
|
||||||
|
|
||||||
import { Checkbox } from '@renderer/components/ui/checkbox';
|
import { Checkbox } from '@renderer/components/ui/checkbox';
|
||||||
import { Label } from '@renderer/components/ui/label';
|
import { Label } from '@renderer/components/ui/label';
|
||||||
import {
|
import {
|
||||||
|
|
@ -10,6 +8,7 @@ import {
|
||||||
TooltipProvider,
|
TooltipProvider,
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from '@renderer/components/ui/tooltip';
|
} from '@renderer/components/ui/tooltip';
|
||||||
|
import { Info } from 'lucide-react';
|
||||||
|
|
||||||
interface LimitContextCheckboxProps {
|
interface LimitContextCheckboxProps {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ export const OptionalSettingsSection = ({
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="flex w-full items-start justify-between gap-3 px-3 py-3 text-left transition-colors hover:bg-[var(--color-surface-raised)]"
|
className="flex w-full items-start justify-between gap-3 p-3 text-left transition-colors hover:bg-[var(--color-surface-raised)]"
|
||||||
onClick={() => setIsOpen((prev) => !prev)}
|
onClick={() => setIsOpen((prev) => !prev)}
|
||||||
aria-expanded={isOpen}
|
aria-expanded={isOpen}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ export const WorkflowTimeline = ({ events, memberColorMap }: WorkflowTimelinePro
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<div className="flex w-full items-center gap-2 rounded px-1.5 py-1.5 text-xs text-[var(--color-text-secondary)]">
|
<div className="flex w-full items-center gap-2 rounded p-1.5 text-xs text-[var(--color-text-secondary)]">
|
||||||
<span className="shrink-0 font-mono text-[10px] text-[var(--color-text-muted)]">
|
<span className="shrink-0 font-mono text-[10px] text-[var(--color-text-muted)]">
|
||||||
{time}
|
{time}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer';
|
||||||
|
import { ImageLightbox } from '@renderer/components/team/attachments/ImageLightbox';
|
||||||
|
import { MemberBadge } from '@renderer/components/team/MemberBadge';
|
||||||
import { MentionableTextarea } from '@renderer/components/ui/MentionableTextarea';
|
import { MentionableTextarea } from '@renderer/components/ui/MentionableTextarea';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||||
import { useChipDraftPersistence } from '@renderer/hooks/useChipDraftPersistence';
|
import { useChipDraftPersistence } from '@renderer/hooks/useChipDraftPersistence';
|
||||||
|
|
@ -7,10 +10,10 @@ import { useDraftPersistence } from '@renderer/hooks/useDraftPersistence';
|
||||||
import { useTaskSuggestions } from '@renderer/hooks/useTaskSuggestions';
|
import { useTaskSuggestions } from '@renderer/hooks/useTaskSuggestions';
|
||||||
import { useTeamSuggestions } from '@renderer/hooks/useTeamSuggestions';
|
import { useTeamSuggestions } from '@renderer/hooks/useTeamSuggestions';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
|
import { serializeChipsWithText } from '@renderer/types/inlineChip';
|
||||||
import { buildReplyBlock } from '@renderer/utils/agentMessageFormatting';
|
import { buildReplyBlock } from '@renderer/utils/agentMessageFormatting';
|
||||||
import { formatAgentRole } from '@renderer/utils/formatAgentRole';
|
import { formatAgentRole } from '@renderer/utils/formatAgentRole';
|
||||||
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
||||||
import { serializeChipsWithText } from '@renderer/types/inlineChip';
|
|
||||||
import {
|
import {
|
||||||
extractTaskRefsFromText,
|
extractTaskRefsFromText,
|
||||||
stripEncodedTaskReferenceMetadata,
|
stripEncodedTaskReferenceMetadata,
|
||||||
|
|
@ -18,10 +21,6 @@ import {
|
||||||
import { MAX_TEXT_LENGTH } from '@shared/constants';
|
import { MAX_TEXT_LENGTH } from '@shared/constants';
|
||||||
import { ImagePlus, Mic, Send, Trash2, X } from 'lucide-react';
|
import { ImagePlus, Mic, Send, Trash2, X } from 'lucide-react';
|
||||||
|
|
||||||
import { MarkdownViewer } from '@renderer/components/chat/viewers/MarkdownViewer';
|
|
||||||
import { ImageLightbox } from '@renderer/components/team/attachments/ImageLightbox';
|
|
||||||
import { MemberBadge } from '@renderer/components/team/MemberBadge';
|
|
||||||
|
|
||||||
import type { MentionSuggestion } from '@renderer/types/mention';
|
import type { MentionSuggestion } from '@renderer/types/mention';
|
||||||
import type { CommentAttachmentPayload, ResolvedTeamMember } from '@shared/types';
|
import type { CommentAttachmentPayload, ResolvedTeamMember } from '@shared/types';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,6 @@ import {
|
||||||
ImageLightbox,
|
ImageLightbox,
|
||||||
LightboxLockProvider,
|
LightboxLockProvider,
|
||||||
} from '@renderer/components/team/attachments/ImageLightbox';
|
} from '@renderer/components/team/attachments/ImageLightbox';
|
||||||
import {
|
|
||||||
getTeamColorSet,
|
|
||||||
getThemedBadge,
|
|
||||||
getThemedBorder,
|
|
||||||
getThemedText,
|
|
||||||
} from '@renderer/constants/teamColors';
|
|
||||||
import { CollapsibleTeamSection } from '@renderer/components/team/CollapsibleTeamSection';
|
import { CollapsibleTeamSection } from '@renderer/components/team/CollapsibleTeamSection';
|
||||||
import { FileIcon } from '@renderer/components/team/editor/FileIcon';
|
import { FileIcon } from '@renderer/components/team/editor/FileIcon';
|
||||||
import { MemberBadge } from '@renderer/components/team/MemberBadge';
|
import { MemberBadge } from '@renderer/components/team/MemberBadge';
|
||||||
|
|
@ -30,19 +24,25 @@ import { Input } from '@renderer/components/ui/input';
|
||||||
import { MemberSelect } from '@renderer/components/ui/MemberSelect';
|
import { MemberSelect } from '@renderer/components/ui/MemberSelect';
|
||||||
import { TiptapEditor } from '@renderer/components/ui/tiptap';
|
import { TiptapEditor } from '@renderer/components/ui/tiptap';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||||
import { getLegacyCutoff, getReadCommentIds } from '@renderer/services/commentReadStorage';
|
import {
|
||||||
import { useStore } from '@renderer/store';
|
getTeamColorSet,
|
||||||
|
getThemedBadge,
|
||||||
|
getThemedBorder,
|
||||||
|
getThemedText,
|
||||||
|
} from '@renderer/constants/teamColors';
|
||||||
import { useTheme } from '@renderer/hooks/useTheme';
|
import { useTheme } from '@renderer/hooks/useTheme';
|
||||||
import { useViewportCommentRead } from '@renderer/hooks/useViewportCommentRead';
|
import { useViewportCommentRead } from '@renderer/hooks/useViewportCommentRead';
|
||||||
|
import { getLegacyCutoff, getReadCommentIds } from '@renderer/services/commentReadStorage';
|
||||||
|
import { useStore } from '@renderer/store';
|
||||||
import { isImageMimeType } from '@renderer/utils/attachmentUtils';
|
import { isImageMimeType } from '@renderer/utils/attachmentUtils';
|
||||||
import {
|
import {
|
||||||
|
agentAvatarUrl,
|
||||||
buildMemberColorMap,
|
buildMemberColorMap,
|
||||||
|
displayMemberName,
|
||||||
KANBAN_COLUMN_DISPLAY,
|
KANBAN_COLUMN_DISPLAY,
|
||||||
REVIEW_STATE_DISPLAY,
|
REVIEW_STATE_DISPLAY,
|
||||||
TASK_STATUS_LABELS,
|
TASK_STATUS_LABELS,
|
||||||
TASK_STATUS_STYLES,
|
TASK_STATUS_STYLES,
|
||||||
agentAvatarUrl,
|
|
||||||
displayMemberName,
|
|
||||||
} from '@renderer/utils/memberHelpers';
|
} from '@renderer/utils/memberHelpers';
|
||||||
import { buildTaskChangeRequestOptions, deriveTaskSince } from '@renderer/utils/taskChangeRequest';
|
import { buildTaskChangeRequestOptions, deriveTaskSince } from '@renderer/utils/taskChangeRequest';
|
||||||
import { linkifyTaskIdsInMarkdown, parseTaskLinkHref } from '@renderer/utils/taskReferenceUtils';
|
import { linkifyTaskIdsInMarkdown, parseTaskLinkHref } from '@renderer/utils/taskReferenceUtils';
|
||||||
|
|
@ -78,9 +78,10 @@ import {
|
||||||
X,
|
X,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
|
import { SourceMessageAttachments } from '../attachments/SourceMessageAttachments';
|
||||||
|
|
||||||
import { WorkflowTimeline } from './StatusHistoryTimeline';
|
import { WorkflowTimeline } from './StatusHistoryTimeline';
|
||||||
import { TaskAttachments } from './TaskAttachments';
|
import { TaskAttachments } from './TaskAttachments';
|
||||||
import { SourceMessageAttachments } from '../attachments/SourceMessageAttachments';
|
|
||||||
import { TaskCommentAwaitingReply } from './TaskCommentAwaitingReply';
|
import { TaskCommentAwaitingReply } from './TaskCommentAwaitingReply';
|
||||||
import { TaskCommentInput } from './TaskCommentInput';
|
import { TaskCommentInput } from './TaskCommentInput';
|
||||||
import { TaskCommentsSection } from './TaskCommentsSection';
|
import { TaskCommentsSection } from './TaskCommentsSection';
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,8 @@ import { Button } from '@renderer/components/ui/button';
|
||||||
import { Checkbox } from '@renderer/components/ui/checkbox';
|
import { Checkbox } from '@renderer/components/ui/checkbox';
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from '@renderer/components/ui/popover';
|
import { Popover, PopoverContent, PopoverTrigger } from '@renderer/components/ui/popover';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||||
import { Crown, Filter } from 'lucide-react';
|
|
||||||
|
|
||||||
import { displayMemberName } from '@renderer/utils/memberHelpers';
|
import { displayMemberName } from '@renderer/utils/memberHelpers';
|
||||||
|
import { Crown, Filter } from 'lucide-react';
|
||||||
|
|
||||||
import type { Session } from '@renderer/types/data';
|
import type { Session } from '@renderer/types/data';
|
||||||
import type { KanbanColumnId, ResolvedTeamMember } from '@shared/types';
|
import type { KanbanColumnId, ResolvedTeamMember } from '@shared/types';
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ export const KanbanSearchInput = ({
|
||||||
|
|
||||||
// Detect `#` trigger and extract filter text after it
|
// Detect `#` trigger and extract filter text after it
|
||||||
const hashMatch = useMemo(() => {
|
const hashMatch = useMemo(() => {
|
||||||
const match = value.match(/#(\S*)$/);
|
const match = /#(\S*)$/.exec(value);
|
||||||
return match ? match[1] : null;
|
return match ? match[1] : null;
|
||||||
}, [value]);
|
}, [value]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '@renderer/components/ui
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||||
import { useUnreadCommentCount } from '@renderer/hooks/useUnreadCommentCount';
|
import { useUnreadCommentCount } from '@renderer/hooks/useUnreadCommentCount';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { REVIEW_STATE_DISPLAY, buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
import { buildMemberColorMap, REVIEW_STATE_DISPLAY } from '@renderer/utils/memberHelpers';
|
||||||
import {
|
import {
|
||||||
buildTaskChangePresenceKey,
|
buildTaskChangePresenceKey,
|
||||||
buildTaskChangeRequestOptions,
|
buildTaskChangeRequestOptions,
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,7 @@ export const MemberDraftRow = ({
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="h-8 w-8 shrink-0 border-red-500/40 px-0 text-red-300 hover:bg-red-500/10 hover:text-red-200"
|
className="size-8 shrink-0 border-red-500/40 px-0 text-red-300 hover:bg-red-500/10 hover:text-red-200"
|
||||||
aria-label={`Remove ${member.name || `member ${index + 1}`}`}
|
aria-label={`Remove ${member.name || `member ${index + 1}`}`}
|
||||||
title="Remove member"
|
title="Remove member"
|
||||||
onClick={() => onRemove(member.id)}
|
onClick={() => onRemove(member.id)}
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ import {
|
||||||
SubagentRecentMessagesPreview,
|
SubagentRecentMessagesPreview,
|
||||||
} from '@renderer/components/team/members/SubagentRecentMessagesPreview';
|
} from '@renderer/components/team/members/SubagentRecentMessagesPreview';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||||
|
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
||||||
import { asEnhancedChunkArray } from '@renderer/types/data';
|
import { asEnhancedChunkArray } from '@renderer/types/data';
|
||||||
import { enhanceAIGroup } from '@renderer/utils/aiGroupEnhancer';
|
import { enhanceAIGroup } from '@renderer/utils/aiGroupEnhancer';
|
||||||
import { formatDuration } from '@renderer/utils/formatters';
|
import { formatDuration } from '@renderer/utils/formatters';
|
||||||
import { transformChunksToConversation } from '@renderer/utils/groupTransformer';
|
import { transformChunksToConversation } from '@renderer/utils/groupTransformer';
|
||||||
import { getMemberColorByName } from '@shared/constants/memberColors';
|
import { getMemberColorByName } from '@shared/constants/memberColors';
|
||||||
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
|
||||||
import {
|
import {
|
||||||
AlertCircle,
|
AlertCircle,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
|
|
@ -271,7 +271,12 @@ export const MemberLogsTab = ({
|
||||||
if (!previewLog) return [];
|
if (!previewLog) return [];
|
||||||
|
|
||||||
// Use task-scoped recentPreviews when available
|
// Use task-scoped recentPreviews when available
|
||||||
if (previewLog.recentPreviews && previewLog.recentPreviews.length > 0 && taskWorkIntervals && taskWorkIntervals.length > 0) {
|
if (
|
||||||
|
previewLog.recentPreviews &&
|
||||||
|
previewLog.recentPreviews.length > 0 &&
|
||||||
|
taskWorkIntervals &&
|
||||||
|
taskWorkIntervals.length > 0
|
||||||
|
) {
|
||||||
const GRACE_BEFORE = 30_000;
|
const GRACE_BEFORE = 30_000;
|
||||||
const GRACE_AFTER = 15_000;
|
const GRACE_AFTER = 15_000;
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
@ -280,7 +285,10 @@ export const MemberLogsTab = ({
|
||||||
const s = Date.parse(i.startedAt);
|
const s = Date.parse(i.startedAt);
|
||||||
if (!Number.isFinite(s)) return null;
|
if (!Number.isFinite(s)) return null;
|
||||||
const e = typeof i.completedAt === 'string' ? Date.parse(i.completedAt) : null;
|
const e = typeof i.completedAt === 'string' ? Date.parse(i.completedAt) : null;
|
||||||
return { startMs: s - GRACE_BEFORE, endMs: e != null && Number.isFinite(e) ? e + GRACE_AFTER : now + GRACE_AFTER };
|
return {
|
||||||
|
startMs: s - GRACE_BEFORE,
|
||||||
|
endMs: e != null && Number.isFinite(e) ? e + GRACE_AFTER : now + GRACE_AFTER,
|
||||||
|
};
|
||||||
})
|
})
|
||||||
.filter((v): v is { startMs: number; endMs: number } => v !== null);
|
.filter((v): v is { startMs: number; endMs: number } => v !== null);
|
||||||
|
|
||||||
|
|
@ -291,9 +299,7 @@ export const MemberLogsTab = ({
|
||||||
return intervals.some((i) => ms >= i.startMs && ms <= i.endMs);
|
return intervals.some((i) => ms >= i.startMs && ms <= i.endMs);
|
||||||
});
|
});
|
||||||
if (scoped.length > 0) {
|
if (scoped.length > 0) {
|
||||||
return scoped
|
return scoped.reverse().map((p, idx) => ({
|
||||||
.reverse()
|
|
||||||
.map((p, idx) => ({
|
|
||||||
id: `${previewLog.sessionId}:recent:${idx}`,
|
id: `${previewLog.sessionId}:recent:${idx}`,
|
||||||
timestamp: new Date(p.timestamp),
|
timestamp: new Date(p.timestamp),
|
||||||
kind: 'output' as const,
|
kind: 'output' as const,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { CUSTOM_ROLE, NO_ROLE } from '@renderer/constants/teamRoles';
|
import { CUSTOM_ROLE, NO_ROLE } from '@renderer/constants/teamRoles';
|
||||||
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
|
||||||
import { serializeChipsWithText } from '@renderer/types/inlineChip';
|
import { serializeChipsWithText } from '@renderer/types/inlineChip';
|
||||||
|
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
||||||
|
|
||||||
import type { MemberDraft } from './membersEditorTypes';
|
import type { MemberDraft } from './membersEditorTypes';
|
||||||
import type { MentionSuggestion } from '@renderer/types/mention';
|
import type { MentionSuggestion } from '@renderer/types/mention';
|
||||||
|
|
@ -37,7 +37,7 @@ export function createMemberDraft(initial?: Partial<MemberDraft>): MemberDraft {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildMemberDraftColorMap(
|
export function buildMemberDraftColorMap(
|
||||||
members: ReadonlyArray<Pick<MemberDraft, 'name'>>
|
members: readonly Pick<MemberDraft, 'name'>[]
|
||||||
): Map<string, string> {
|
): Map<string, string> {
|
||||||
return buildMemberColorMap(
|
return buildMemberColorMap(
|
||||||
members
|
members
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import { cn } from '@renderer/lib/utils';
|
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
TooltipProvider,
|
TooltipProvider,
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from '@renderer/components/ui/tooltip';
|
} from '@renderer/components/ui/tooltip';
|
||||||
|
import { cn } from '@renderer/lib/utils';
|
||||||
|
|
||||||
import type { AgentActionMode } from '@shared/types';
|
import type { AgentActionMode } from '@shared/types';
|
||||||
|
|
||||||
export type ActionMode = AgentActionMode;
|
export type ActionMode = AgentActionMode;
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,12 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { api } from '@renderer/api';
|
import { api } from '@renderer/api';
|
||||||
import { AttachmentPreviewList } from '@renderer/components/team/attachments/AttachmentPreviewList';
|
import { AttachmentPreviewList } from '@renderer/components/team/attachments/AttachmentPreviewList';
|
||||||
import { DropZoneOverlay } from '@renderer/components/team/attachments/DropZoneOverlay';
|
import { DropZoneOverlay } from '@renderer/components/team/attachments/DropZoneOverlay';
|
||||||
import { ActionModeSelector } from '@renderer/components/team/messages/ActionModeSelector';
|
|
||||||
import { MemberBadge } from '@renderer/components/team/MemberBadge';
|
import { MemberBadge } from '@renderer/components/team/MemberBadge';
|
||||||
|
import { ActionModeSelector } from '@renderer/components/team/messages/ActionModeSelector';
|
||||||
import { MentionableTextarea } from '@renderer/components/ui/MentionableTextarea';
|
import { MentionableTextarea } from '@renderer/components/ui/MentionableTextarea';
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from '@renderer/components/ui/popover';
|
import { Popover, PopoverContent, PopoverTrigger } from '@renderer/components/ui/popover';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||||
|
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
||||||
import { useComposerDraft } from '@renderer/hooks/useComposerDraft';
|
import { useComposerDraft } from '@renderer/hooks/useComposerDraft';
|
||||||
import { useTaskSuggestions } from '@renderer/hooks/useTaskSuggestions';
|
import { useTaskSuggestions } from '@renderer/hooks/useTaskSuggestions';
|
||||||
import { useTeamSuggestions } from '@renderer/hooks/useTeamSuggestions';
|
import { useTeamSuggestions } from '@renderer/hooks/useTeamSuggestions';
|
||||||
|
|
@ -17,7 +18,6 @@ import { isTeamProvisioningActive } from '@renderer/store/slices/teamSlice';
|
||||||
import { serializeChipsWithText } from '@renderer/types/inlineChip';
|
import { serializeChipsWithText } from '@renderer/types/inlineChip';
|
||||||
import { formatAgentRole } from '@renderer/utils/formatAgentRole';
|
import { formatAgentRole } from '@renderer/utils/formatAgentRole';
|
||||||
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
||||||
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
|
||||||
import { nameColorSet } from '@renderer/utils/projectColor';
|
import { nameColorSet } from '@renderer/utils/projectColor';
|
||||||
import {
|
import {
|
||||||
extractTaskRefsFromText,
|
extractTaskRefsFromText,
|
||||||
|
|
@ -27,8 +27,8 @@ import { MAX_TEXT_LENGTH } from '@shared/constants';
|
||||||
import { isLeadMember } from '@shared/utils/leadDetection';
|
import { isLeadMember } from '@shared/utils/leadDetection';
|
||||||
import { AlertCircle, Check, ChevronDown, ImagePlus, Mic, Search, Send } from 'lucide-react';
|
import { AlertCircle, Check, ChevronDown, ImagePlus, Mic, Search, Send } from 'lucide-react';
|
||||||
|
|
||||||
import type { MentionSuggestion } from '@renderer/types/mention';
|
|
||||||
import type { ActionMode } from '@renderer/components/team/messages/ActionModeSelector';
|
import type { ActionMode } from '@renderer/components/team/messages/ActionModeSelector';
|
||||||
|
import type { MentionSuggestion } from '@renderer/types/mention';
|
||||||
import type {
|
import type {
|
||||||
AttachmentPayload,
|
AttachmentPayload,
|
||||||
ResolvedTeamMember,
|
ResolvedTeamMember,
|
||||||
|
|
@ -84,7 +84,6 @@ export const MessageComposer = ({
|
||||||
(externalTextareaRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;
|
(externalTextareaRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [externalTextareaRef]);
|
}, [externalTextareaRef]);
|
||||||
const [recipient, setRecipient] = useState<string>(() => {
|
const [recipient, setRecipient] = useState<string>(() => {
|
||||||
const lead = members.find((m) => isLeadMember(m));
|
const lead = members.find((m) => isLeadMember(m));
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Badge } from '@renderer/components/ui/badge';
|
import { Badge } from '@renderer/components/ui/badge';
|
||||||
import { Button } from '@renderer/components/ui/button';
|
import { Button } from '@renderer/components/ui/button';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
||||||
|
import { useStableTeamMentionMeta } from '@renderer/hooks/useStableTeamMentionMeta';
|
||||||
import { useTeamMessagesExpanded } from '@renderer/hooks/useTeamMessagesExpanded';
|
import { useTeamMessagesExpanded } from '@renderer/hooks/useTeamMessagesExpanded';
|
||||||
import { useTeamMessagesRead } from '@renderer/hooks/useTeamMessagesRead';
|
import { useTeamMessagesRead } from '@renderer/hooks/useTeamMessagesRead';
|
||||||
import { useStableTeamMentionMeta } from '@renderer/hooks/useStableTeamMentionMeta';
|
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { filterTeamMessages } from '@renderer/utils/teamMessageFiltering';
|
import { filterTeamMessages } from '@renderer/utils/teamMessageFiltering';
|
||||||
import { toMessageKey } from '@renderer/utils/teamMessageKey';
|
import { toMessageKey } from '@renderer/utils/teamMessageKey';
|
||||||
|
|
@ -16,8 +16,8 @@ import {
|
||||||
ChevronsDownUp,
|
ChevronsDownUp,
|
||||||
ChevronsUpDown,
|
ChevronsUpDown,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
PanelLeftClose,
|
|
||||||
PanelLeft,
|
PanelLeft,
|
||||||
|
PanelLeftClose,
|
||||||
Search,
|
Search,
|
||||||
X,
|
X,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
@ -26,13 +26,14 @@ import { ActivityTimeline } from '../activity/ActivityTimeline';
|
||||||
import { getThoughtGroupKey, groupTimelineItems } from '../activity/LeadThoughtsGroup';
|
import { getThoughtGroupKey, groupTimelineItems } from '../activity/LeadThoughtsGroup';
|
||||||
import { MessageExpandDialog } from '../activity/MessageExpandDialog';
|
import { MessageExpandDialog } from '../activity/MessageExpandDialog';
|
||||||
import { CollapsibleTeamSection } from '../CollapsibleTeamSection';
|
import { CollapsibleTeamSection } from '../CollapsibleTeamSection';
|
||||||
|
|
||||||
import { MessageComposer } from './MessageComposer';
|
import { MessageComposer } from './MessageComposer';
|
||||||
import { MessagesFilterPopover } from './MessagesFilterPopover';
|
import { MessagesFilterPopover } from './MessagesFilterPopover';
|
||||||
import { StatusBlock } from './StatusBlock';
|
import { StatusBlock } from './StatusBlock';
|
||||||
|
|
||||||
import type { MessagesFilterState } from './MessagesFilterPopover';
|
|
||||||
import type { ActionMode } from './ActionModeSelector';
|
|
||||||
import type { TimelineItem } from '../activity/LeadThoughtsGroup';
|
import type { TimelineItem } from '../activity/LeadThoughtsGroup';
|
||||||
|
import type { ActionMode } from './ActionModeSelector';
|
||||||
|
import type { MessagesFilterState } from './MessagesFilterPopover';
|
||||||
import type { InboxMessage, ResolvedTeamMember, TaskRef, TeamTaskWithKanban } from '@shared/types';
|
import type { InboxMessage, ResolvedTeamMember, TaskRef, TeamTaskWithKanban } from '@shared/types';
|
||||||
|
|
||||||
interface TimeWindow {
|
interface TimeWindow {
|
||||||
|
|
@ -631,7 +632,7 @@ export const MessagesPanel = memo(function MessagesPanel({
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
defaultOpen
|
defaultOpen
|
||||||
action={<div className="flex items-center gap-2 pl-2 pr-2">{searchAndFilterBar}</div>}
|
action={<div className="flex items-center gap-2 px-2">{searchAndFilterBar}</div>}
|
||||||
>
|
>
|
||||||
{messagesContent}
|
{messagesContent}
|
||||||
</CollapsibleTeamSection>
|
</CollapsibleTeamSection>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { Check, FileCode, FileDiff, FileText, GitBranch, GitCommit, Search } from 'lucide-react';
|
import { Check, FileCode, FileDiff, FileText, GitBranch, GitCommit, Search } from 'lucide-react';
|
||||||
|
|
||||||
/* ── Fake diff lines for the mini-terminal ─────────────────────────── */
|
/* ── Fake diff lines for the mini-terminal ─────────────────────────── */
|
||||||
|
|
@ -156,10 +157,7 @@ export const ChangesLoadingAnimation = (): React.JSX.Element => {
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
{/* Rotating dashed orbit ring */}
|
{/* Rotating dashed orbit ring */}
|
||||||
<svg
|
<svg className="clda-orbit-ring pointer-events-none absolute size-40" viewBox="0 0 160 160">
|
||||||
className="clda-orbit-ring pointer-events-none absolute h-40 w-40"
|
|
||||||
viewBox="0 0 160 160"
|
|
||||||
>
|
|
||||||
<circle
|
<circle
|
||||||
cx="80"
|
cx="80"
|
||||||
cy="80"
|
cy="80"
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ export const FullDiffLoadingBanner = ({
|
||||||
return (
|
return (
|
||||||
<div className="bg-surface/95 border-b border-border px-4 py-3">
|
<div className="bg-surface/95 border-b border-border px-4 py-3">
|
||||||
<div className="bg-surface-raised/80 rounded-xl border border-border shadow-sm">
|
<div className="bg-surface-raised/80 rounded-xl border border-border shadow-sm">
|
||||||
<div className="flex items-start gap-3 px-3 py-3">
|
<div className="flex items-start gap-3 p-3">
|
||||||
<div className="relative mt-0.5 flex size-9 shrink-0 items-center justify-center rounded-xl border border-border bg-surface-sidebar">
|
<div className="relative mt-0.5 flex size-9 shrink-0 items-center justify-center rounded-xl border border-border bg-surface-sidebar">
|
||||||
<div className="absolute inset-1 rounded-lg bg-emerald-500/10 blur-sm" />
|
<div className="absolute inset-1 rounded-lg bg-emerald-500/10 blur-sm" />
|
||||||
<LoaderCircle
|
<LoaderCircle
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import { useStore } from '@renderer/store';
|
||||||
import { AlertTriangle, Clock, Loader2, Terminal } from 'lucide-react';
|
import { AlertTriangle, Clock, Loader2, Terminal } from 'lucide-react';
|
||||||
|
|
||||||
import { CliLogsRichView } from '../CliLogsRichView';
|
import { CliLogsRichView } from '../CliLogsRichView';
|
||||||
|
|
||||||
import { RunStatusBadge } from './ScheduleStatusBadge';
|
import { RunStatusBadge } from './ScheduleStatusBadge';
|
||||||
|
|
||||||
import type { ScheduleRun } from '@shared/types';
|
import type { ScheduleRun } from '@shared/types';
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import {
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
import { LaunchTeamDialog } from '../dialogs/LaunchTeamDialog';
|
import { LaunchTeamDialog } from '../dialogs/LaunchTeamDialog';
|
||||||
|
|
||||||
import { ScheduleEmptyState } from './ScheduleEmptyState';
|
import { ScheduleEmptyState } from './ScheduleEmptyState';
|
||||||
import { ScheduleRunLogDialog } from './ScheduleRunLogDialog';
|
import { ScheduleRunLogDialog } from './ScheduleRunLogDialog';
|
||||||
import { ScheduleRunRow } from './ScheduleRunRow';
|
import { ScheduleRunRow } from './ScheduleRunRow';
|
||||||
|
|
@ -241,7 +242,6 @@ export const ScheduleSection = ({ teamName }: ScheduleSectionProps): React.JSX.E
|
||||||
try {
|
try {
|
||||||
await deleteSchedule(id);
|
await deleteSchedule(id);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error('Failed to delete schedule:', err);
|
console.error('Failed to delete schedule:', err);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -253,7 +253,6 @@ export const ScheduleSection = ({ teamName }: ScheduleSectionProps): React.JSX.E
|
||||||
try {
|
try {
|
||||||
await triggerNow(id);
|
await triggerNow(id);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error('Failed to trigger schedule:', err);
|
console.error('Failed to trigger schedule:', err);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue