From ad36305765aa4113ff2cb7284944e3ce3a37d095 Mon Sep 17 00:00:00 2001
From: Eric Amodio <eamodio@gmail.com>
Date: Mon, 10 Apr 2023 01:19:23 -0400
Subject: [PATCH] Moves storage & context into system Moves storage types into
 constants

---
 src/annotations/annotationProvider.ts              |   2 +-
 src/annotations/fileAnnotationController.ts        |   2 +-
 src/api/actionRunners.ts                           |   2 +-
 src/avatars.ts                                     |   4 +-
 src/codelens/codeLensController.ts                 |   2 +-
 src/commands/generateCommitMessage.ts              |   2 +-
 src/commands/git/search.ts                         |   2 +-
 src/commands/git/stash.ts                          |   2 +-
 src/commands/gitCommands.utils.ts                  |   4 +-
 src/constants.ts                                   | 182 +++++++++++++
 src/container.ts                                   |   2 +-
 src/context.ts                                     |  20 --
 src/extension.ts                                   |   6 +-
 src/git/gitProviderService.ts                      |   2 +-
 src/plus/github/githubGitProvider.ts               |   2 +-
 src/plus/subscription/subscriptionService.ts       |   2 +-
 src/plus/subscription/utils.ts                     |   2 +-
 src/plus/webviews/focus/focusWebview.ts            |   2 +-
 src/plus/webviews/graph/graphWebview.ts            |   4 +-
 src/plus/webviews/graph/statusbar.ts               |   2 +-
 src/storage.ts                                     | 282 ---------------------
 src/system/context.ts                              |  20 ++
 src/system/decorators/resolver.ts                  |   8 +-
 src/system/keyboard.ts                             |   2 +-
 src/system/storage.ts                              | 111 ++++++++
 src/telemetry/usageTracker.ts                      |   2 +-
 src/trackers/documentTracker.ts                    |   2 +-
 src/trackers/trackedDocument.ts                    |   2 +-
 src/uris/deepLinks/deepLinkService.ts              |   2 +-
 src/views/commitsView.ts                           |   2 +-
 src/views/fileHistoryView.ts                       |   2 +-
 src/views/lineHistoryView.ts                       |   2 +-
 src/views/nodes/branchNode.ts                      |   4 +-
 src/views/nodes/commitNode.ts                      |   4 +-
 src/views/nodes/compareBranchNode.ts               |   2 +-
 src/views/nodes/comparePickerNode.ts               |   2 +-
 src/views/nodes/compareResultsNode.ts              |   2 +-
 src/views/nodes/fileHistoryTrackerNode.ts          |   2 +-
 src/views/nodes/lineHistoryTrackerNode.ts          |   2 +-
 src/views/nodes/worktreeNode.ts                    |   2 +-
 src/views/repositoriesView.ts                      |   2 +-
 src/views/searchAndCompareView.ts                  |   4 +-
 src/views/viewCommands.ts                          |   2 +-
 src/vsls/vsls.ts                                   |   2 +-
 src/webviews/commitDetails/commitDetailsWebview.ts |   2 +-
 src/webviews/home/homeWebview.ts                   |   4 +-
 src/webviews/webviewController.ts                  |   2 +-
 47 files changed, 365 insertions(+), 358 deletions(-)
 delete mode 100644 src/context.ts
 delete mode 100644 src/storage.ts
 create mode 100644 src/system/context.ts
 create mode 100644 src/system/storage.ts

diff --git a/src/annotations/annotationProvider.ts b/src/annotations/annotationProvider.ts
index 24c609c..598bc6b 100644
--- a/src/annotations/annotationProvider.ts
+++ b/src/annotations/annotationProvider.ts
@@ -9,7 +9,7 @@ import type {
 } from 'vscode';
 import { Disposable, window } from 'vscode';
 import type { FileAnnotationType } from '../config';
-import { setContext } from '../context';
+import { setContext } from '../system/context';
 import { Logger } from '../system/logger';
 import type { GitDocumentState, TrackedDocument } from '../trackers/gitDocumentTracker';
 
diff --git a/src/annotations/fileAnnotationController.ts b/src/annotations/fileAnnotationController.ts
index 387b425..1581c4a 100644
--- a/src/annotations/fileAnnotationController.ts
+++ b/src/annotations/fileAnnotationController.ts
@@ -23,8 +23,8 @@ import {
 import { AnnotationsToggleMode, BlameHighlightLocations, ChangesLocations, FileAnnotationType } from '../config';
 import type { Colors, CoreColors } from '../constants';
 import type { Container } from '../container';
-import { setContext } from '../context';
 import { configuration } from '../system/configuration';
+import { setContext } from '../system/context';
 import { once } from '../system/event';
 import { debounce } from '../system/function';
 import { find } from '../system/iterable';
diff --git a/src/api/actionRunners.ts b/src/api/actionRunners.ts
index bc8f76e..8930a93 100644
--- a/src/api/actionRunners.ts
+++ b/src/api/actionRunners.ts
@@ -3,9 +3,9 @@ import { Disposable, EventEmitter, window } from 'vscode';
 import type { Config } from '../config';
 import { Commands } from '../constants';
 import type { Container } from '../container';
-import { setContext } from '../context';
 import { registerCommand } from '../system/command';
 import { configuration } from '../system/configuration';
+import { setContext } from '../system/context';
 import { sortCompare } from '../system/string';
 import { getQuickPickIgnoreFocusOut } from '../system/utils';
 import type { Action, ActionContext, ActionRunner } from './gitlens';
diff --git a/src/avatars.ts b/src/avatars.ts
index 4509082..2bbe173 100644
--- a/src/avatars.ts
+++ b/src/avatars.ts
@@ -1,11 +1,11 @@
 import { EventEmitter, Uri } from 'vscode';
 import { md5 } from '@env/crypto';
 import { GravatarDefaultStyle } from './config';
+import type { StoredAvatar } from './constants';
 import { Container } from './container';
-import { getContext } from './context';
 import { getGitHubNoReplyAddressParts } from './git/remotes/github';
-import type { StoredAvatar } from './storage';
 import { configuration } from './system/configuration';
+import { getContext } from './system/context';
 import { debounce } from './system/function';
 import { filterMap } from './system/iterable';
 import { base64, equalsIgnoreCase } from './system/string';
diff --git a/src/codelens/codeLensController.ts b/src/codelens/codeLensController.ts
index 25450af..1208362 100644
--- a/src/codelens/codeLensController.ts
+++ b/src/codelens/codeLensController.ts
@@ -1,8 +1,8 @@
 import type { ConfigurationChangeEvent } from 'vscode';
 import { Disposable, languages } from 'vscode';
 import type { Container } from '../container';
-import { setContext } from '../context';
 import { configuration } from '../system/configuration';
+import { setContext } from '../system/context';
 import { once } from '../system/event';
 import { Logger } from '../system/logger';
 import type {
diff --git a/src/commands/generateCommitMessage.ts b/src/commands/generateCommitMessage.ts
index b645d47..09752a0 100644
--- a/src/commands/generateCommitMessage.ts
+++ b/src/commands/generateCommitMessage.ts
@@ -7,10 +7,10 @@ import { GitUri } from '../git/gitUri';
 import { uncommittedStaged } from '../git/models/constants';
 import { showGenericErrorMessage } from '../messages';
 import { getBestRepositoryOrShowPicker } from '../quickpicks/repositoryPicker';
-import type { Storage } from '../storage';
 import { command, executeCoreCommand } from '../system/command';
 import { configuration } from '../system/configuration';
 import { Logger } from '../system/logger';
+import type { Storage } from '../system/storage';
 import { supportedInVSCodeVersion } from '../system/utils';
 import { ActiveEditorCommand, Command, getCommandUri } from './base';
 
diff --git a/src/commands/git/search.ts b/src/commands/git/search.ts
index d79656e..60690fb 100644
--- a/src/commands/git/search.ts
+++ b/src/commands/git/search.ts
@@ -1,6 +1,5 @@
 import { GlyphChars } from '../../constants';
 import type { Container } from '../../container';
-import { getContext } from '../../context';
 import { showDetailsView } from '../../git/actions/commit';
 import type { GitCommit } from '../../git/models/commit';
 import type { GitLog } from '../../git/models/log';
@@ -10,6 +9,7 @@ import { getSearchQueryComparisonKey, parseSearchQuery, searchOperators } from '
 import type { QuickPickItemOfT } from '../../quickpicks/items/common';
 import { ActionQuickPickItem } from '../../quickpicks/items/common';
 import { configuration } from '../../system/configuration';
+import { getContext } from '../../system/context';
 import { pluralize } from '../../system/string';
 import { SearchResultsNode } from '../../views/nodes/searchResultsNode';
 import type { ViewsWithRepositoryFolders } from '../../views/viewBase';
diff --git a/src/commands/git/stash.ts b/src/commands/git/stash.ts
index fd56e62..1d8d5ca 100644
--- a/src/commands/git/stash.ts
+++ b/src/commands/git/stash.ts
@@ -2,7 +2,6 @@ import type { QuickPickItem, Uri } from 'vscode';
 import { QuickInputButtons, window } from 'vscode';
 import { GlyphChars } from '../../constants';
 import type { Container } from '../../container';
-import { getContext } from '../../context';
 import { reveal, showDetailsView } from '../../git/actions/stash';
 import { StashApplyError, StashApplyErrorReason, StashPushError, StashPushErrorReason } from '../../git/errors';
 import type { GitStashCommit } from '../../git/models/commit';
@@ -13,6 +12,7 @@ import { showGenericErrorMessage } from '../../messages';
 import type { QuickPickItemOfT } from '../../quickpicks/items/common';
 import type { FlagsQuickPickItem } from '../../quickpicks/items/flags';
 import { createFlagsQuickPickItem } from '../../quickpicks/items/flags';
+import { getContext } from '../../system/context';
 import { formatPath } from '../../system/formatPath';
 import { Logger } from '../../system/logger';
 import { pad } from '../../system/string';
diff --git a/src/commands/gitCommands.utils.ts b/src/commands/gitCommands.utils.ts
index dd5191d..0d8ee57 100644
--- a/src/commands/gitCommands.utils.ts
+++ b/src/commands/gitCommands.utils.ts
@@ -1,8 +1,8 @@
 import { GitCommandSorting } from '../config';
+import type { RecentUsage } from '../constants';
 import type { Container } from '../container';
-import { getContext } from '../context';
-import type { RecentUsage } from '../storage';
 import { configuration } from '../system/configuration';
+import { getContext } from '../system/context';
 import { BranchGitCommand } from './git/branch';
 import { CherryPickGitCommand } from './git/cherry-pick';
 import { CoAuthorsGitCommand } from './git/coauthors';
diff --git a/src/constants.ts b/src/constants.ts
index 4a07ce4..31d919e 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -1,3 +1,11 @@
+import type { ViewShowBranchComparison } from './config';
+import type { Environment } from './container';
+import type { StoredSearchQuery } from './git/search';
+import type { Subscription } from './subscription';
+import type { TrackedUsage, TrackedUsageKeys } from './telemetry/usageTracker';
+import type { CommitDetailsDismissed } from './webviews/commitDetails/protocol';
+import type { CompletedActions } from './webviews/home/protocol';
+
 export const extensionPrefix = 'gitlens';
 export const quickPickTitleMaxChars = 80;
 
@@ -488,3 +496,177 @@ export type TelemetryEvents =
 	| 'subscription'
 	| 'subscription/changed'
 	| 'usage/track';
+
+export type SecretKeys =
+	| `gitlens.integration.auth:${string}`
+	| 'gitlens.openai.key'
+	| `gitlens.plus.auth:${Environment}`;
+
+export const enum SyncedStorageKeys {
+	Version = 'gitlens:synced:version',
+	PreReleaseVersion = 'gitlens:synced:preVersion',
+	HomeViewWelcomeVisible = 'gitlens:views:welcome:visible',
+}
+
+export type DeprecatedGlobalStorage = {
+	/** @deprecated */
+	[key in `disallow:connection:${string}`]: any;
+};
+
+export type GlobalStorage = {
+	avatars: [string, StoredAvatar][];
+	repoVisibility: [string, StoredRepoVisibilityInfo][];
+	'confirm:sendToOpenAI': boolean;
+	'deepLinks:pending': StoredDeepLinkContext;
+	'home:actions:completed': CompletedActions[];
+	'home:steps:completed': string[];
+	'home:sections:dismissed': string[];
+	'home:status:pinned': boolean;
+	'home:banners:dismissed': string[];
+	pendingWelcomeOnFocus: boolean;
+	pendingWhatsNewOnFocus: boolean;
+	'plus:migratedAuthentication': boolean;
+	'plus:discountNotificationShown': boolean;
+	'plus:renewalDiscountNotificationShown': boolean;
+	// Don't change this key name ('premium`) as its the stored subscription
+	'premium:subscription': Stored<Subscription>;
+	'synced:version': string;
+	// Keep the pre-release version separate from the released version
+	'synced:preVersion': string;
+	usages: Record<TrackedUsageKeys, TrackedUsage>;
+	version: string;
+	// Keep the pre-release version separate from the released version
+	preVersion: string;
+	'views:layout': StoredViewsLayout;
+	'views:welcome:visible': boolean;
+	'views:commitDetails:dismissed': CommitDetailsDismissed[];
+} & { [key in `provider:authentication:skip:${string}`]: boolean };
+
+export type DeprecatedWorkspaceStorage = {
+	/** @deprecated use `graph:filtersByRepo.excludeRefs` */
+	'graph:hiddenRefs': Record<string, StoredGraphExcludedRef>;
+	/** @deprecated use `views:searchAndCompare:pinned` */
+	'pinned:comparisons': Record<string, DeprecatedPinnedComparison>;
+};
+
+export type WorkspaceStorage = {
+	assumeRepositoriesOnStartup?: boolean;
+	'branch:comparisons': StoredBranchComparisons;
+	'confirm:sendToOpenAI': boolean;
+	'gitComandPalette:usage': RecentUsage;
+	gitPath: string;
+	'graph:banners:dismissed': Record<string, boolean>;
+	'graph:columns': Record<string, StoredGraphColumn>;
+	'graph:filtersByRepo': Record<string, StoredGraphFilters>;
+	'remote:default': string;
+	'starred:branches': StoredStarred;
+	'starred:repositories': StoredStarred;
+	'views:repositories:autoRefresh': boolean;
+	'views:searchAndCompare:keepResults': boolean;
+	'views:searchAndCompare:pinned': StoredPinnedItems;
+	'views:commitDetails:autolinksExpanded': boolean;
+} & { [key in `connected:${string}`]: boolean };
+
+export type StoredViewsLayout = 'gitlens' | 'scm';
+export interface Stored<T, SchemaVersion extends number = 1> {
+	v: SchemaVersion;
+	data: T;
+}
+
+export interface StoredAvatar {
+	uri: string;
+	timestamp: number;
+}
+
+export type StoredRepositoryVisibility = 'private' | 'public' | 'local';
+
+export interface StoredRepoVisibilityInfo {
+	visibility: StoredRepositoryVisibility;
+	timestamp: number;
+	remotesHash?: string;
+}
+
+export interface StoredBranchComparison {
+	ref: string;
+	notation: '..' | '...' | undefined;
+	type: Exclude<ViewShowBranchComparison, false> | undefined;
+}
+
+export interface StoredBranchComparisons {
+	[id: string]: string | StoredBranchComparison;
+}
+
+export interface StoredDeepLinkContext {
+	url?: string | undefined;
+	repoPath?: string | undefined;
+}
+
+export interface StoredGraphColumn {
+	isHidden?: boolean;
+	mode?: string;
+	width?: number;
+}
+
+export interface StoredGraphFilters {
+	includeOnlyRefs?: Record<string, StoredGraphIncludeOnlyRef>;
+	excludeRefs?: Record<string, StoredGraphExcludedRef>;
+	excludeTypes?: Record<string, boolean>;
+}
+
+export type StoredGraphRefType = 'head' | 'remote' | 'tag';
+
+export interface StoredGraphExcludedRef {
+	id: string;
+	type: StoredGraphRefType;
+	name: string;
+	owner?: string;
+}
+
+export interface StoredGraphIncludeOnlyRef {
+	id: string;
+	type: StoredGraphRefType;
+	name: string;
+	owner?: string;
+}
+
+export interface StoredNamedRef {
+	label?: string;
+	ref: string;
+}
+
+export interface StoredPinnedComparison {
+	type: 'comparison';
+	timestamp: number;
+	path: string;
+	ref1: StoredNamedRef;
+	ref2: StoredNamedRef;
+	notation?: '..' | '...';
+}
+
+export interface StoredPinnedSearch {
+	type: 'search';
+	timestamp: number;
+	path: string;
+	labels: {
+		label: string;
+		queryLabel:
+			| string
+			| {
+					label: string;
+					resultsType?: { singular: string; plural: string };
+			  };
+	};
+	search: StoredSearchQuery;
+}
+
+export type StoredPinnedItem = StoredPinnedComparison | StoredPinnedSearch;
+export type StoredPinnedItems = Record<string, StoredPinnedItem>;
+export type StoredStarred = Record<string, boolean>;
+export type RecentUsage = Record<string, number>;
+
+interface DeprecatedPinnedComparison {
+	path: string;
+	ref1: StoredNamedRef;
+	ref2: StoredNamedRef;
+	notation?: '..' | '...';
+}
diff --git a/src/container.ts b/src/container.ts
index 38570fe..869c217 100644
--- a/src/container.ts
+++ b/src/container.ts
@@ -31,13 +31,13 @@ import {
 import { GraphStatusBarController } from './plus/webviews/graph/statusbar';
 import { registerTimelineWebviewPanel, registerTimelineWebviewView } from './plus/webviews/timeline/registration';
 import { StatusBarController } from './statusbar/statusBarController';
-import type { Storage } from './storage';
 import { executeCommand } from './system/command';
 import { configuration } from './system/configuration';
 import { log } from './system/decorators/log';
 import { memoize } from './system/decorators/memoize';
 import { Keyboard } from './system/keyboard';
 import { Logger } from './system/logger';
+import type { Storage } from './system/storage';
 import { TelemetryService } from './telemetry/telemetry';
 import { UsageTracker } from './telemetry/usageTracker';
 import { GitTerminalLinkProvider } from './terminal/linkProvider';
diff --git a/src/context.ts b/src/context.ts
deleted file mode 100644
index e8952e6..0000000
--- a/src/context.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { EventEmitter } from 'vscode';
-import type { ContextKeys } from './constants';
-import { executeCoreCommand } from './system/command';
-
-const contextStorage = new Map<string, unknown>();
-
-const _onDidChangeContext = new EventEmitter<ContextKeys>();
-export const onDidChangeContext = _onDidChangeContext.event;
-
-export function getContext<T>(key: ContextKeys): T | undefined;
-export function getContext<T>(key: ContextKeys, defaultValue: T): T;
-export function getContext<T>(key: ContextKeys, defaultValue?: T): T | undefined {
-	return (contextStorage.get(key) as T | undefined) ?? defaultValue;
-}
-
-export async function setContext(key: ContextKeys, value: unknown): Promise<void> {
-	contextStorage.set(key, value);
-	void (await executeCoreCommand('setContext', key, value));
-	_onDidChangeContext.fire(key);
-}
diff --git a/src/extension.ts b/src/extension.ts
index 6ee3776..e4a45b6 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -6,9 +6,8 @@ import { Api } from './api/api';
 import type { CreatePullRequestActionContext, GitLensApi, OpenPullRequestActionContext } from './api/gitlens';
 import type { CreatePullRequestOnRemoteCommandArgs, OpenPullRequestOnRemoteCommandArgs } from './commands';
 import { fromOutputLevel, OutputLevel } from './config';
-import { Commands } from './constants';
+import { Commands, SyncedStorageKeys } from './constants';
 import { Container } from './container';
-import { setContext } from './context';
 import { isGitUri } from './git/gitUri';
 import { getBranchNameWithoutRemote, isBranch } from './git/models/branch';
 import { isCommit } from './git/models/commit';
@@ -16,15 +15,16 @@ import { isRepository } from './git/models/repository';
 import { isTag } from './git/models/tag';
 import { showDebugLoggingWarningMessage, showPreReleaseExpiredErrorMessage, showWhatsNewMessage } from './messages';
 import { registerPartnerActionRunners } from './partners';
-import { Storage, SyncedStorageKeys } from './storage';
 import { executeCommand, executeCoreCommand, registerCommands } from './system/command';
 import { configuration, Configuration } from './system/configuration';
+import { setContext } from './system/context';
 import { setDefaultDateLocales } from './system/date';
 import { once } from './system/event';
 import { Logger } from './system/logger';
 import { LogLevel } from './system/logger.constants';
 import { flatten } from './system/object';
 import { Stopwatch } from './system/stopwatch';
+import { Storage } from './system/storage';
 import { compare, fromString, satisfies } from './system/version';
 import { isViewNode } from './views/nodes/viewNode';
 
diff --git a/src/git/gitProviderService.ts b/src/git/gitProviderService.ts
index e762d41..3f949c0 100644
--- a/src/git/gitProviderService.ts
+++ b/src/git/gitProviderService.ts
@@ -14,7 +14,6 @@ import { resetAvatarCache } from '../avatars';
 import type { CoreGitConfiguration } from '../constants';
 import { GlyphChars, Schemes } from '../constants';
 import type { Container } from '../container';
-import { setContext } from '../context';
 import { AccessDeniedError, ProviderNotFoundError } from '../errors';
 import type { FeatureAccess, Features, PlusFeatures, RepoFeatureAccess } from '../features';
 import type { SubscriptionChangeEvent } from '../plus/subscription/subscriptionService';
@@ -25,6 +24,7 @@ import { isSubscriptionPaidPlan, SubscriptionPlanId } from '../subscription';
 import { groupByFilterMap, groupByMap, joinUnique } from '../system/array';
 import { registerCommand } from '../system/command';
 import { configuration } from '../system/configuration';
+import { setContext } from '../system/context';
 import { gate } from '../system/decorators/gate';
 import { debug, log } from '../system/decorators/log';
 import type { Deferrable } from '../system/function';
diff --git a/src/plus/github/githubGitProvider.ts b/src/plus/github/githubGitProvider.ts
index b545eda..eeefd18 100644
--- a/src/plus/github/githubGitProvider.ts
+++ b/src/plus/github/githubGitProvider.ts
@@ -13,7 +13,6 @@ import { authentication, EventEmitter, FileType, Uri, window, workspace } from '
 import { encodeUtf8Hex } from '@env/hex';
 import { CharCode, Schemes } from '../../constants';
 import type { Container } from '../../container';
-import { setContext } from '../../context';
 import { emojify } from '../../emojis';
 import {
 	AuthenticationError,
@@ -77,6 +76,7 @@ import { getRemoteProviderMatcher, loadRemoteProviders } from '../../git/remotes
 import type { GitSearch, GitSearchResultData, GitSearchResults, SearchQuery } from '../../git/search';
 import { getSearchQueryComparisonKey, parseSearchQuery } from '../../git/search';
 import { configuration } from '../../system/configuration';
+import { setContext } from '../../system/context';
 import { gate } from '../../system/decorators/gate';
 import { debug, log } from '../../system/decorators/log';
 import { filterMap, first, last, some } from '../../system/iterable';
diff --git a/src/plus/subscription/subscriptionService.ts b/src/plus/subscription/subscriptionService.ts
index 5b1d902..fa7e753 100644
--- a/src/plus/subscription/subscriptionService.ts
+++ b/src/plus/subscription/subscriptionService.ts
@@ -25,7 +25,6 @@ import { getPlatform } from '@env/platform';
 import type { CoreColors } from '../../constants';
 import { Commands } from '../../constants';
 import type { Container } from '../../container';
-import { setContext } from '../../context';
 import { AccountValidationError } from '../../errors';
 import type { RepositoriesChangeEvent } from '../../git/gitProviderService';
 import { showMessage } from '../../messages';
@@ -45,6 +44,7 @@ import {
 } from '../../subscription';
 import { executeCommand, registerCommand } from '../../system/command';
 import { configuration } from '../../system/configuration';
+import { setContext } from '../../system/context';
 import { createFromDateDelta } from '../../system/date';
 import { gate } from '../../system/decorators/gate';
 import { debug, log } from '../../system/decorators/log';
diff --git a/src/plus/subscription/utils.ts b/src/plus/subscription/utils.ts
index 7d5e7bd..148d684 100644
--- a/src/plus/subscription/utils.ts
+++ b/src/plus/subscription/utils.ts
@@ -1,7 +1,7 @@
 import type { MessageItem } from 'vscode';
 import { window } from 'vscode';
-import { getContext } from '../../context';
 import { configuration } from '../../system/configuration';
+import { getContext } from '../../system/context';
 
 export function arePlusFeaturesEnabled(): boolean {
 	return getContext('gitlens:plus:enabled', configuration.get('plusFeatures.enabled', undefined, true));
diff --git a/src/plus/webviews/focus/focusWebview.ts b/src/plus/webviews/focus/focusWebview.ts
index 131764a..fd00355 100644
--- a/src/plus/webviews/focus/focusWebview.ts
+++ b/src/plus/webviews/focus/focusWebview.ts
@@ -2,7 +2,6 @@ import { Disposable, Uri, window } from 'vscode';
 import type { GHPRPullRequest } from '../../../commands';
 import { Commands } from '../../../constants';
 import type { Container } from '../../../container';
-import { setContext } from '../../../context';
 import { PlusFeatures } from '../../../features';
 import { add as addRemote } from '../../../git/actions/remote';
 import * as RepoActions from '../../../git/actions/repository';
@@ -26,6 +25,7 @@ import type { RichRemoteProvider } from '../../../git/remotes/richRemoteProvider
 import type { Subscription } from '../../../subscription';
 import { SubscriptionState } from '../../../subscription';
 import { executeCommand, registerCommand } from '../../../system/command';
+import { setContext } from '../../../system/context';
 import type { IpcMessage } from '../../../webviews/protocol';
 import { onIpc } from '../../../webviews/protocol';
 import type { WebviewController, WebviewProvider } from '../../../webviews/webviewController';
diff --git a/src/plus/webviews/graph/graphWebview.ts b/src/plus/webviews/graph/graphWebview.ts
index a52dd64..23ee81c 100644
--- a/src/plus/webviews/graph/graphWebview.ts
+++ b/src/plus/webviews/graph/graphWebview.ts
@@ -12,9 +12,9 @@ import type {
 } from '../../../commands';
 import { parseCommandContext } from '../../../commands/base';
 import type { Config } from '../../../config';
+import type { StoredGraphFilters, StoredGraphIncludeOnlyRef, StoredGraphRefType } from '../../../constants';
 import { Commands, GlyphChars } from '../../../constants';
 import type { Container } from '../../../container';
-import { getContext, onDidChangeContext } from '../../../context';
 import type { CommitSelectedEvent } from '../../../eventBus';
 import { PlusFeatures } from '../../../features';
 import * as BranchActions from '../../../git/actions/branch';
@@ -57,7 +57,6 @@ import {
 import type { GitSearch } from '../../../git/search';
 import { getSearchQueryComparisonKey } from '../../../git/search';
 import { showRepositoryPicker } from '../../../quickpicks/repositoryPicker';
-import type { StoredGraphFilters, StoredGraphIncludeOnlyRef, StoredGraphRefType } from '../../../storage';
 import {
 	executeActionCommand,
 	executeCommand,
@@ -66,6 +65,7 @@ import {
 	registerCommand,
 } from '../../../system/command';
 import { configuration } from '../../../system/configuration';
+import { getContext, onDidChangeContext } from '../../../system/context';
 import { gate } from '../../../system/decorators/gate';
 import { debug } from '../../../system/decorators/log';
 import type { Deferrable } from '../../../system/function';
diff --git a/src/plus/webviews/graph/statusbar.ts b/src/plus/webviews/graph/statusbar.ts
index 00a2a57..91e38ec 100644
--- a/src/plus/webviews/graph/statusbar.ts
+++ b/src/plus/webviews/graph/statusbar.ts
@@ -2,8 +2,8 @@ import type { ConfigurationChangeEvent, StatusBarItem } from 'vscode';
 import { Disposable, MarkdownString, StatusBarAlignment, window } from 'vscode';
 import { Commands } from '../../../constants';
 import type { Container } from '../../../container';
-import { getContext, onDidChangeContext } from '../../../context';
 import { configuration } from '../../../system/configuration';
+import { getContext, onDidChangeContext } from '../../../system/context';
 import { once } from '../../../system/function';
 import type { SubscriptionChangeEvent } from '../../subscription/subscriptionService';
 import { arePlusFeaturesEnabled } from '../../subscription/utils';
diff --git a/src/storage.ts b/src/storage.ts
deleted file mode 100644
index 13fd1cc..0000000
--- a/src/storage.ts
+++ /dev/null
@@ -1,282 +0,0 @@
-import type { Disposable, Event, ExtensionContext, SecretStorageChangeEvent } from 'vscode';
-import { EventEmitter } from 'vscode';
-import type { ViewShowBranchComparison } from './config';
-import { extensionPrefix } from './constants';
-import type { Environment } from './container';
-import type { StoredSearchQuery } from './git/search';
-import type { Subscription } from './subscription';
-import { debug } from './system/decorators/log';
-import type { TrackedUsage, TrackedUsageKeys } from './telemetry/usageTracker';
-import type { CommitDetailsDismissed } from './webviews/commitDetails/protocol';
-import type { CompletedActions } from './webviews/home/protocol';
-
-export type StorageChangeEvent =
-	| {
-			/**
-			 * The key of the stored value that has changed.
-			 */
-			readonly key: keyof (GlobalStorage & DeprecatedGlobalStorage);
-			readonly workspace: false;
-	  }
-	| {
-			/**
-			 * The key of the stored value that has changed.
-			 */
-			readonly key: keyof (WorkspaceStorage & DeprecatedWorkspaceStorage);
-			readonly workspace: true;
-	  };
-
-export class Storage implements Disposable {
-	private _onDidChange = new EventEmitter<StorageChangeEvent>();
-	get onDidChange(): Event<StorageChangeEvent> {
-		return this._onDidChange.event;
-	}
-
-	private _onDidChangeSecrets = new EventEmitter<SecretStorageChangeEvent>();
-	get onDidChangeSecrets(): Event<SecretStorageChangeEvent> {
-		return this._onDidChangeSecrets.event;
-	}
-
-	private readonly _disposable: Disposable;
-	constructor(private readonly context: ExtensionContext) {
-		this._disposable = this.context.secrets.onDidChange(e => this._onDidChangeSecrets.fire(e));
-	}
-
-	dispose(): void {
-		this._disposable.dispose();
-	}
-
-	get<T extends keyof GlobalStorage>(key: T): GlobalStorage[T] | undefined;
-	/** @deprecated */
-	get<T extends keyof DeprecatedGlobalStorage>(key: T): DeprecatedGlobalStorage[T] | undefined;
-	get<T extends keyof GlobalStorage>(key: T, defaultValue: GlobalStorage[T]): GlobalStorage[T];
-	@debug({ logThreshold: 50 })
-	get(key: keyof (GlobalStorage & DeprecatedGlobalStorage), defaultValue?: unknown): unknown | undefined {
-		return this.context.globalState.get(`${extensionPrefix}:${key}`, defaultValue);
-	}
-
-	@debug({ logThreshold: 250 })
-	async delete(key: keyof (GlobalStorage & DeprecatedGlobalStorage)): Promise<void> {
-		await this.context.globalState.update(`${extensionPrefix}:${key}`, undefined);
-		this._onDidChange.fire({ key: key, workspace: false });
-	}
-
-	@debug({ args: { 1: false }, logThreshold: 250 })
-	async store<T extends keyof GlobalStorage>(key: T, value: GlobalStorage[T] | undefined): Promise<void> {
-		await this.context.globalState.update(`${extensionPrefix}:${key}`, value);
-		this._onDidChange.fire({ key: key, workspace: false });
-	}
-
-	@debug({ args: false, logThreshold: 250 })
-	async getSecret(key: SecretKeys): Promise<string | undefined> {
-		return this.context.secrets.get(key);
-	}
-
-	@debug({ args: false, logThreshold: 250 })
-	async deleteSecret(key: SecretKeys): Promise<void> {
-		return this.context.secrets.delete(key);
-	}
-
-	@debug({ args: false, logThreshold: 250 })
-	async storeSecret(key: SecretKeys, value: string): Promise<void> {
-		return this.context.secrets.store(key, value);
-	}
-
-	getWorkspace<T extends keyof WorkspaceStorage>(key: T): WorkspaceStorage[T] | undefined;
-	/** @deprecated */
-	getWorkspace<T extends keyof DeprecatedWorkspaceStorage>(key: T): DeprecatedWorkspaceStorage[T] | undefined;
-	getWorkspace<T extends keyof WorkspaceStorage>(key: T, defaultValue: WorkspaceStorage[T]): WorkspaceStorage[T];
-	@debug({ logThreshold: 25 })
-	getWorkspace(
-		key: keyof (WorkspaceStorage & DeprecatedWorkspaceStorage),
-		defaultValue?: unknown,
-	): unknown | undefined {
-		return this.context.workspaceState.get(`${extensionPrefix}:${key}`, defaultValue);
-	}
-
-	@debug({ logThreshold: 250 })
-	async deleteWorkspace(key: keyof (WorkspaceStorage & DeprecatedWorkspaceStorage)): Promise<void> {
-		await this.context.workspaceState.update(`${extensionPrefix}:${key}`, undefined);
-		this._onDidChange.fire({ key: key, workspace: true });
-	}
-
-	@debug({ args: { 1: false }, logThreshold: 250 })
-	async storeWorkspace(key: keyof WorkspaceStorage, value: unknown | undefined): Promise<void> {
-		await this.context.workspaceState.update(`${extensionPrefix}:${key}`, value);
-		this._onDidChange.fire({ key: key, workspace: true });
-	}
-}
-
-export type SecretKeys =
-	| `gitlens.integration.auth:${string}`
-	| 'gitlens.openai.key'
-	| `gitlens.plus.auth:${Environment}`;
-
-export const enum SyncedStorageKeys {
-	Version = 'gitlens:synced:version',
-	PreReleaseVersion = 'gitlens:synced:preVersion',
-	HomeViewWelcomeVisible = 'gitlens:views:welcome:visible',
-}
-
-export type DeprecatedGlobalStorage = {
-	/** @deprecated */
-	[key in `disallow:connection:${string}`]: any;
-};
-
-export type GlobalStorage = {
-	avatars: [string, StoredAvatar][];
-	repoVisibility: [string, StoredRepoVisibilityInfo][];
-	'confirm:sendToOpenAI': boolean;
-	'deepLinks:pending': StoredDeepLinkContext;
-	'home:actions:completed': CompletedActions[];
-	'home:steps:completed': string[];
-	'home:sections:dismissed': string[];
-	'home:status:pinned': boolean;
-	'home:banners:dismissed': string[];
-	pendingWelcomeOnFocus: boolean;
-	pendingWhatsNewOnFocus: boolean;
-	'plus:migratedAuthentication': boolean;
-	'plus:discountNotificationShown': boolean;
-	'plus:renewalDiscountNotificationShown': boolean;
-	// Don't change this key name ('premium`) as its the stored subscription
-	'premium:subscription': Stored<Subscription>;
-	'synced:version': string;
-	// Keep the pre-release version separate from the released version
-	'synced:preVersion': string;
-	usages: Record<TrackedUsageKeys, TrackedUsage>;
-	version: string;
-	// Keep the pre-release version separate from the released version
-	preVersion: string;
-	'views:layout': StoredViewsLayout;
-	'views:welcome:visible': boolean;
-	'views:commitDetails:dismissed': CommitDetailsDismissed[];
-} & { [key in `provider:authentication:skip:${string}`]: boolean };
-
-export type DeprecatedWorkspaceStorage = {
-	/** @deprecated use `graph:filtersByRepo.excludeRefs` */
-	'graph:hiddenRefs': Record<string, StoredGraphExcludedRef>;
-	/** @deprecated use `views:searchAndCompare:pinned` */
-	'pinned:comparisons': Record<string, DeprecatedPinnedComparison>;
-};
-
-export type WorkspaceStorage = {
-	assumeRepositoriesOnStartup?: boolean;
-	'branch:comparisons': StoredBranchComparisons;
-	'confirm:sendToOpenAI': boolean;
-	'gitComandPalette:usage': RecentUsage;
-	gitPath: string;
-	'graph:banners:dismissed': Record<string, boolean>;
-	'graph:columns': Record<string, StoredGraphColumn>;
-	'graph:filtersByRepo': Record<string, StoredGraphFilters>;
-	'remote:default': string;
-	'starred:branches': StoredStarred;
-	'starred:repositories': StoredStarred;
-	'views:repositories:autoRefresh': boolean;
-	'views:searchAndCompare:keepResults': boolean;
-	'views:searchAndCompare:pinned': StoredPinnedItems;
-	'views:commitDetails:autolinksExpanded': boolean;
-} & { [key in `connected:${string}`]: boolean };
-
-export type StoredViewsLayout = 'gitlens' | 'scm';
-export interface Stored<T, SchemaVersion extends number = 1> {
-	v: SchemaVersion;
-	data: T;
-}
-
-export interface StoredAvatar {
-	uri: string;
-	timestamp: number;
-}
-
-export type StoredRepositoryVisibility = 'private' | 'public' | 'local';
-
-export interface StoredRepoVisibilityInfo {
-	visibility: StoredRepositoryVisibility;
-	timestamp: number;
-	remotesHash?: string;
-}
-
-export interface StoredBranchComparison {
-	ref: string;
-	notation: '..' | '...' | undefined;
-	type: Exclude<ViewShowBranchComparison, false> | undefined;
-}
-
-export interface StoredBranchComparisons {
-	[id: string]: string | StoredBranchComparison;
-}
-
-export interface StoredDeepLinkContext {
-	url?: string | undefined;
-	repoPath?: string | undefined;
-}
-
-export interface StoredGraphColumn {
-	isHidden?: boolean;
-	mode?: string;
-	width?: number;
-}
-
-export interface StoredGraphFilters {
-	includeOnlyRefs?: Record<string, StoredGraphIncludeOnlyRef>;
-	excludeRefs?: Record<string, StoredGraphExcludedRef>;
-	excludeTypes?: Record<string, boolean>;
-}
-
-export type StoredGraphRefType = 'head' | 'remote' | 'tag';
-
-export interface StoredGraphExcludedRef {
-	id: string;
-	type: StoredGraphRefType;
-	name: string;
-	owner?: string;
-}
-
-export interface StoredGraphIncludeOnlyRef {
-	id: string;
-	type: StoredGraphRefType;
-	name: string;
-	owner?: string;
-}
-
-export interface StoredNamedRef {
-	label?: string;
-	ref: string;
-}
-
-export interface StoredPinnedComparison {
-	type: 'comparison';
-	timestamp: number;
-	path: string;
-	ref1: StoredNamedRef;
-	ref2: StoredNamedRef;
-	notation?: '..' | '...';
-}
-
-export interface StoredPinnedSearch {
-	type: 'search';
-	timestamp: number;
-	path: string;
-	labels: {
-		label: string;
-		queryLabel:
-			| string
-			| {
-					label: string;
-					resultsType?: { singular: string; plural: string };
-			  };
-	};
-	search: StoredSearchQuery;
-}
-
-export type StoredPinnedItem = StoredPinnedComparison | StoredPinnedSearch;
-export type StoredPinnedItems = Record<string, StoredPinnedItem>;
-export type StoredStarred = Record<string, boolean>;
-export type RecentUsage = Record<string, number>;
-
-interface DeprecatedPinnedComparison {
-	path: string;
-	ref1: StoredNamedRef;
-	ref2: StoredNamedRef;
-	notation?: '..' | '...';
-}
diff --git a/src/system/context.ts b/src/system/context.ts
new file mode 100644
index 0000000..e217cc4
--- /dev/null
+++ b/src/system/context.ts
@@ -0,0 +1,20 @@
+import { EventEmitter } from 'vscode';
+import type { ContextKeys } from '../constants';
+import { executeCoreCommand } from './command';
+
+const contextStorage = new Map<string, unknown>();
+
+const _onDidChangeContext = new EventEmitter<ContextKeys>();
+export const onDidChangeContext = _onDidChangeContext.event;
+
+export function getContext<T>(key: ContextKeys): T | undefined;
+export function getContext<T>(key: ContextKeys, defaultValue: T): T;
+export function getContext<T>(key: ContextKeys, defaultValue?: T): T | undefined {
+	return (contextStorage.get(key) as T | undefined) ?? defaultValue;
+}
+
+export async function setContext(key: ContextKeys, value: unknown): Promise<void> {
+	contextStorage.set(key, value);
+	void (await executeCoreCommand('setContext', key, value));
+	_onDidChangeContext.fire(key);
+}
diff --git a/src/system/decorators/resolver.ts b/src/system/decorators/resolver.ts
index 5eb0221..21969e8 100644
--- a/src/system/decorators/resolver.ts
+++ b/src/system/decorators/resolver.ts
@@ -21,9 +21,7 @@ function replacer(key: string, value: any): any {
 	if (isBranch(value) || isCommit(value) || isTag(value) || isViewNode(value)) {
 		return value.toString();
 	}
-	if (isContainer(value)) {
-		return '<container>';
-	}
+	if (isContainer(value)) return '<container>';
 
 	return value;
 }
@@ -58,9 +56,7 @@ export function defaultResolver(...args: any[]): string {
 			if (isBranch(arg0) || isCommit(arg0) || isTag(arg0) || isViewNode(arg0)) {
 				return arg0.toString();
 			}
-			if (isContainer(arg0)) {
-				return '<container>';
-			}
+			if (isContainer(arg0)) return '<container>';
 
 			return JSON.stringify(arg0, replacer);
 	}
diff --git a/src/system/keyboard.ts b/src/system/keyboard.ts
index f13b1a8..ca3eeff 100644
--- a/src/system/keyboard.ts
+++ b/src/system/keyboard.ts
@@ -1,8 +1,8 @@
 import { Disposable } from 'vscode';
 import type { Keys } from '../constants';
 import { extensionPrefix, keys } from '../constants';
-import { setContext } from '../context';
 import { registerCommand } from './command';
+import { setContext } from './context';
 import { log } from './decorators/log';
 import { Logger } from './logger';
 import { getLogScope } from './logger.scope';
diff --git a/src/system/storage.ts b/src/system/storage.ts
new file mode 100644
index 0000000..97f439a
--- /dev/null
+++ b/src/system/storage.ts
@@ -0,0 +1,111 @@
+import type { Disposable, Event, ExtensionContext, SecretStorageChangeEvent } from 'vscode';
+import { EventEmitter } from 'vscode';
+import type {
+	DeprecatedGlobalStorage,
+	DeprecatedWorkspaceStorage,
+	GlobalStorage,
+	SecretKeys,
+	WorkspaceStorage,
+} from '../constants';
+import { extensionPrefix } from '../constants';
+import { debug } from './decorators/log';
+
+export type StorageChangeEvent =
+	| {
+			/**
+			 * The key of the stored value that has changed.
+			 */
+			readonly key: keyof (GlobalStorage & DeprecatedGlobalStorage);
+			readonly workspace: false;
+	  }
+	| {
+			/**
+			 * The key of the stored value that has changed.
+			 */
+			readonly key: keyof (WorkspaceStorage & DeprecatedWorkspaceStorage);
+			readonly workspace: true;
+	  };
+
+export class Storage implements Disposable {
+	private _onDidChange = new EventEmitter<StorageChangeEvent>();
+	get onDidChange(): Event<StorageChangeEvent> {
+		return this._onDidChange.event;
+	}
+
+	private _onDidChangeSecrets = new EventEmitter<SecretStorageChangeEvent>();
+	get onDidChangeSecrets(): Event<SecretStorageChangeEvent> {
+		return this._onDidChangeSecrets.event;
+	}
+
+	private readonly _disposable: Disposable;
+	constructor(private readonly context: ExtensionContext) {
+		this._disposable = this.context.secrets.onDidChange(e => this._onDidChangeSecrets.fire(e));
+	}
+
+	dispose(): void {
+		this._disposable.dispose();
+	}
+
+	get<T extends keyof GlobalStorage>(key: T): GlobalStorage[T] | undefined;
+	/** @deprecated */
+	get<T extends keyof DeprecatedGlobalStorage>(key: T): DeprecatedGlobalStorage[T] | undefined;
+	get<T extends keyof GlobalStorage>(key: T, defaultValue: GlobalStorage[T]): GlobalStorage[T];
+	@debug({ logThreshold: 50 })
+	get(key: keyof (GlobalStorage & DeprecatedGlobalStorage), defaultValue?: unknown): unknown | undefined {
+		return this.context.globalState.get(`${extensionPrefix}:${key}`, defaultValue);
+	}
+
+	@debug({ logThreshold: 250 })
+	async delete(key: keyof (GlobalStorage & DeprecatedGlobalStorage)): Promise<void> {
+		await this.context.globalState.update(`${extensionPrefix}:${key}`, undefined);
+		this._onDidChange.fire({ key: key, workspace: false });
+	}
+
+	@debug({ args: { 1: false }, logThreshold: 250 })
+	async store<T extends keyof GlobalStorage>(key: T, value: GlobalStorage[T] | undefined): Promise<void> {
+		await this.context.globalState.update(`${extensionPrefix}:${key}`, value);
+		this._onDidChange.fire({ key: key, workspace: false });
+	}
+
+	@debug({ args: false, logThreshold: 250 })
+	async getSecret(key: SecretKeys): Promise<string | undefined> {
+		return this.context.secrets.get(key);
+	}
+
+	@debug({ args: false, logThreshold: 250 })
+	async deleteSecret(key: SecretKeys): Promise<void> {
+		return this.context.secrets.delete(key);
+	}
+
+	@debug({ args: false, logThreshold: 250 })
+	async storeSecret(key: SecretKeys, value: string): Promise<void> {
+		return this.context.secrets.store(key, value);
+	}
+
+	getWorkspace<T extends keyof WorkspaceStorage>(key: T): WorkspaceStorage[T] | undefined;
+	/** @deprecated */
+	getWorkspace<T extends keyof DeprecatedWorkspaceStorage>(key: T): DeprecatedWorkspaceStorage[T] | undefined;
+	getWorkspace<T extends keyof WorkspaceStorage>(key: T, defaultValue: WorkspaceStorage[T]): WorkspaceStorage[T];
+	@debug({ logThreshold: 25 })
+	getWorkspace(
+		key: keyof (WorkspaceStorage & DeprecatedWorkspaceStorage),
+		defaultValue?: unknown,
+	): unknown | undefined {
+		return this.context.workspaceState.get(`${extensionPrefix}:${key}`, defaultValue);
+	}
+
+	@debug({ logThreshold: 250 })
+	async deleteWorkspace(key: keyof (WorkspaceStorage & DeprecatedWorkspaceStorage)): Promise<void> {
+		await this.context.workspaceState.update(`${extensionPrefix}:${key}`, undefined);
+		this._onDidChange.fire({ key: key, workspace: true });
+	}
+
+	@debug({ args: { 1: false }, logThreshold: 250 })
+	async storeWorkspace<T extends keyof WorkspaceStorage>(
+		key: T,
+		value: WorkspaceStorage[T] | undefined,
+	): Promise<void> {
+		await this.context.workspaceState.update(`${extensionPrefix}:${key}`, value);
+		this._onDidChange.fire({ key: key, workspace: true });
+	}
+}
diff --git a/src/telemetry/usageTracker.ts b/src/telemetry/usageTracker.ts
index 3c6bda4..49b2674 100644
--- a/src/telemetry/usageTracker.ts
+++ b/src/telemetry/usageTracker.ts
@@ -1,8 +1,8 @@
 import type { Disposable, Event } from 'vscode';
 import { EventEmitter } from 'vscode';
 import type { Container } from '../container';
-import type { Storage } from '../storage';
 import { updateRecordValue } from '../system/object';
+import type { Storage } from '../system/storage';
 
 export interface TrackedUsage {
 	count: number;
diff --git a/src/trackers/documentTracker.ts b/src/trackers/documentTracker.ts
index 8890634..662b6eb 100644
--- a/src/trackers/documentTracker.ts
+++ b/src/trackers/documentTracker.ts
@@ -11,13 +11,13 @@ import type {
 } from 'vscode';
 import { Disposable, EndOfLine, env, EventEmitter, Uri, window, workspace } from 'vscode';
 import type { Container } from '../container';
-import { setContext } from '../context';
 import type { RepositoriesChangeEvent } from '../git/gitProviderService';
 import type { GitUri } from '../git/gitUri';
 import { isGitUri } from '../git/gitUri';
 import type { RepositoryChangeEvent } from '../git/models/repository';
 import { RepositoryChange, RepositoryChangeComparisonMode } from '../git/models/repository';
 import { configuration } from '../system/configuration';
+import { setContext } from '../system/context';
 import { debug } from '../system/decorators/log';
 import { once } from '../system/event';
 import type { Deferrable } from '../system/function';
diff --git a/src/trackers/trackedDocument.ts b/src/trackers/trackedDocument.ts
index 1c49722..3a99052 100644
--- a/src/trackers/trackedDocument.ts
+++ b/src/trackers/trackedDocument.ts
@@ -1,9 +1,9 @@
 import type { Disposable, Event, TextDocument, TextEditor } from 'vscode';
 import { EventEmitter } from 'vscode';
 import type { Container } from '../container';
-import { setContext } from '../context';
 import { GitUri } from '../git/gitUri';
 import { deletedOrMissing } from '../git/models/constants';
+import { setContext } from '../system/context';
 import type { Deferrable } from '../system/function';
 import { debounce } from '../system/function';
 import { Logger } from '../system/logger';
diff --git a/src/uris/deepLinks/deepLinkService.ts b/src/uris/deepLinks/deepLinkService.ts
index 112fe1a..048c227 100644
--- a/src/uris/deepLinks/deepLinkService.ts
+++ b/src/uris/deepLinks/deepLinkService.ts
@@ -1,4 +1,5 @@
 import { Disposable, env, ProgressLocation, Uri, window, workspace } from 'vscode';
+import type { StoredDeepLinkContext } from '../../constants';
 import { Commands } from '../../constants';
 import type { Container } from '../../container';
 import { getBranchNameWithoutRemote } from '../../git/models/branch';
@@ -7,7 +8,6 @@ import { createReference } from '../../git/models/reference';
 import type { GitRemote } from '../../git/models/remote';
 import { parseGitRemoteUrl } from '../../git/parsers/remoteParser';
 import type { ShowInCommitGraphCommandArgs } from '../../plus/webviews/graph/protocol';
-import type { StoredDeepLinkContext } from '../../storage';
 import { executeCommand } from '../../system/command';
 import { configuration } from '../../system/configuration';
 import { once } from '../../system/event';
diff --git a/src/views/commitsView.ts b/src/views/commitsView.ts
index d30ab99..ed6f26d 100644
--- a/src/views/commitsView.ts
+++ b/src/views/commitsView.ts
@@ -9,7 +9,6 @@ import type { CommitsViewConfig } from '../config';
 import { ViewFilesLayout, ViewShowBranchComparison } from '../config';
 import { Commands, GlyphChars } from '../constants';
 import type { Container } from '../container';
-import { setContext } from '../context';
 import { GitUri } from '../git/gitUri';
 import type { GitCommit } from '../git/models/commit';
 import { isCommit } from '../git/models/commit';
@@ -19,6 +18,7 @@ import type { RepositoryChangeEvent } from '../git/models/repository';
 import { Repository, RepositoryChange, RepositoryChangeComparisonMode } from '../git/models/repository';
 import { executeCommand } from '../system/command';
 import { configuration } from '../system/configuration';
+import { setContext } from '../system/context';
 import { gate } from '../system/decorators/gate';
 import { debug } from '../system/decorators/log';
 import { disposableInterval } from '../system/function';
diff --git a/src/views/fileHistoryView.ts b/src/views/fileHistoryView.ts
index 39cf07b..84b89cd 100644
--- a/src/views/fileHistoryView.ts
+++ b/src/views/fileHistoryView.ts
@@ -2,10 +2,10 @@ import type { ConfigurationChangeEvent, Disposable } from 'vscode';
 import type { FileHistoryViewConfig } from '../config';
 import { Commands } from '../constants';
 import type { Container } from '../container';
-import { setContext } from '../context';
 import type { GitUri } from '../git/gitUri';
 import { executeCommand } from '../system/command';
 import { configuration } from '../system/configuration';
+import { setContext } from '../system/context';
 import { FileHistoryTrackerNode } from './nodes/fileHistoryTrackerNode';
 import { LineHistoryTrackerNode } from './nodes/lineHistoryTrackerNode';
 import { ViewBase } from './viewBase';
diff --git a/src/views/lineHistoryView.ts b/src/views/lineHistoryView.ts
index 4141de1..1807176 100644
--- a/src/views/lineHistoryView.ts
+++ b/src/views/lineHistoryView.ts
@@ -2,9 +2,9 @@ import type { ConfigurationChangeEvent, Disposable } from 'vscode';
 import type { LineHistoryViewConfig } from '../config';
 import { Commands } from '../constants';
 import type { Container } from '../container';
-import { setContext } from '../context';
 import { executeCommand } from '../system/command';
 import { configuration } from '../system/configuration';
+import { setContext } from '../system/context';
 import { LineHistoryTrackerNode } from './nodes/lineHistoryTrackerNode';
 import { ViewBase } from './viewBase';
 import { registerViewCommand } from './viewCommands';
diff --git a/src/views/nodes/branchNode.ts b/src/views/nodes/branchNode.ts
index 6739ff2..2abf313 100644
--- a/src/views/nodes/branchNode.ts
+++ b/src/views/nodes/branchNode.ts
@@ -1,9 +1,8 @@
 import { MarkdownString, ThemeColor, ThemeIcon, TreeItem, TreeItemCollapsibleState, Uri, window } from 'vscode';
 import type { ViewShowBranchComparison } from '../../config';
 import { ViewBranchesLayout } from '../../config';
-import type { Colors} from '../../constants';
+import type { Colors } from '../../constants';
 import { GlyphChars } from '../../constants';
-import { getContext } from '../../context';
 import type { GitUri } from '../../git/gitUri';
 import type { GitBranch } from '../../git/models/branch';
 import type { GitLog } from '../../git/models/log';
@@ -12,6 +11,7 @@ import { PullRequestState } from '../../git/models/pullRequest';
 import type { GitBranchReference } from '../../git/models/reference';
 import { GitRemote, GitRemoteType } from '../../git/models/remote';
 import type { GitUser } from '../../git/models/user';
+import { getContext } from '../../system/context';
 import { gate } from '../../system/decorators/gate';
 import { debug, log } from '../../system/decorators/log';
 import { map } from '../../system/iterable';
diff --git a/src/views/nodes/commitNode.ts b/src/views/nodes/commitNode.ts
index 2cd61f6..282db13 100644
--- a/src/views/nodes/commitNode.ts
+++ b/src/views/nodes/commitNode.ts
@@ -2,9 +2,8 @@ import type { Command } from 'vscode';
 import { MarkdownString, ThemeColor, ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode';
 import type { DiffWithPreviousCommandArgs } from '../../commands';
 import { ViewFilesLayout } from '../../config';
-import type { Colors} from '../../constants';
+import type { Colors } from '../../constants';
 import { Commands } from '../../constants';
-import { getContext } from '../../context';
 import { CommitFormatter } from '../../git/formatters/commitFormatter';
 import type { GitBranch } from '../../git/models/branch';
 import type { GitCommit } from '../../git/models/commit';
@@ -14,6 +13,7 @@ import type { GitRemote } from '../../git/models/remote';
 import type { RichRemoteProvider } from '../../git/remotes/richRemoteProvider';
 import { makeHierarchical } from '../../system/array';
 import { configuration } from '../../system/configuration';
+import { getContext } from '../../system/context';
 import { gate } from '../../system/decorators/gate';
 import { joinPaths, normalizePath } from '../../system/path';
 import type { Deferred } from '../../system/promise';
diff --git a/src/views/nodes/compareBranchNode.ts b/src/views/nodes/compareBranchNode.ts
index 23e8292..a741cf0 100644
--- a/src/views/nodes/compareBranchNode.ts
+++ b/src/views/nodes/compareBranchNode.ts
@@ -1,12 +1,12 @@
 import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode';
 import { ViewShowBranchComparison } from '../../config';
+import type { StoredBranchComparison, StoredBranchComparisons } from '../../constants';
 import { GlyphChars } from '../../constants';
 import type { GitUri } from '../../git/gitUri';
 import type { GitBranch } from '../../git/models/branch';
 import { createRevisionRange, shortenRevision } from '../../git/models/reference';
 import { CommandQuickPickItem } from '../../quickpicks/items/common';
 import { showReferencePicker } from '../../quickpicks/referencePicker';
-import type { StoredBranchComparison, StoredBranchComparisons } from '../../storage';
 import { gate } from '../../system/decorators/gate';
 import { debug, log } from '../../system/decorators/log';
 import { getSettledValue } from '../../system/promise';
diff --git a/src/views/nodes/comparePickerNode.ts b/src/views/nodes/comparePickerNode.ts
index 9064ce9..ba7cc90 100644
--- a/src/views/nodes/comparePickerNode.ts
+++ b/src/views/nodes/comparePickerNode.ts
@@ -1,7 +1,7 @@
 import { TreeItem, TreeItemCollapsibleState } from 'vscode';
+import type { StoredNamedRef } from '../../constants';
 import { GlyphChars } from '../../constants';
 import { unknownGitUri } from '../../git/gitUri';
-import type { StoredNamedRef } from '../../storage';
 import type { SearchAndCompareView, SearchAndCompareViewNode } from '../searchAndCompareView';
 import { ContextValues, ViewNode } from './viewNode';
 
diff --git a/src/views/nodes/compareResultsNode.ts b/src/views/nodes/compareResultsNode.ts
index 9eab689..454d036 100644
--- a/src/views/nodes/compareResultsNode.ts
+++ b/src/views/nodes/compareResultsNode.ts
@@ -1,8 +1,8 @@
 import { ThemeIcon, TreeItem, TreeItemCollapsibleState, window } from 'vscode';
 import { md5 } from '@env/crypto';
+import type { StoredNamedRef } from '../../constants';
 import { GitUri } from '../../git/gitUri';
 import { createRevisionRange, shortenRevision } from '../../git/models/reference';
-import type { StoredNamedRef } from '../../storage';
 import { gate } from '../../system/decorators/gate';
 import { debug, log } from '../../system/decorators/log';
 import { getSettledValue } from '../../system/promise';
diff --git a/src/views/nodes/fileHistoryTrackerNode.ts b/src/views/nodes/fileHistoryTrackerNode.ts
index c137c2c..5d22663 100644
--- a/src/views/nodes/fileHistoryTrackerNode.ts
+++ b/src/views/nodes/fileHistoryTrackerNode.ts
@@ -1,11 +1,11 @@
 import type { TextEditor } from 'vscode';
 import { Disposable, FileType, TreeItem, TreeItemCollapsibleState, window, workspace } from 'vscode';
-import { setContext } from '../../context';
 import type { GitCommitish } from '../../git/gitUri';
 import { GitUri, unknownGitUri } from '../../git/gitUri';
 import { isBranchReference, isSha } from '../../git/models/reference';
 import { showReferencePicker } from '../../quickpicks/referencePicker';
 import { UriComparer } from '../../system/comparers';
+import { setContext } from '../../system/context';
 import { gate } from '../../system/decorators/gate';
 import { debug, log } from '../../system/decorators/log';
 import type { Deferrable } from '../../system/function';
diff --git a/src/views/nodes/lineHistoryTrackerNode.ts b/src/views/nodes/lineHistoryTrackerNode.ts
index 26792bf..5f7e2c4 100644
--- a/src/views/nodes/lineHistoryTrackerNode.ts
+++ b/src/views/nodes/lineHistoryTrackerNode.ts
@@ -1,12 +1,12 @@
 import type { Selection } from 'vscode';
 import { TreeItem, TreeItemCollapsibleState, window } from 'vscode';
-import { setContext } from '../../context';
 import type { GitCommitish } from '../../git/gitUri';
 import { GitUri, unknownGitUri } from '../../git/gitUri';
 import { deletedOrMissing } from '../../git/models/constants';
 import { isBranchReference, isSha } from '../../git/models/reference';
 import { showReferencePicker } from '../../quickpicks/referencePicker';
 import { UriComparer } from '../../system/comparers';
+import { setContext } from '../../system/context';
 import { gate } from '../../system/decorators/gate';
 import { debug, log } from '../../system/decorators/log';
 import { debounce } from '../../system/function';
diff --git a/src/views/nodes/worktreeNode.ts b/src/views/nodes/worktreeNode.ts
index 4267153..22699df 100644
--- a/src/views/nodes/worktreeNode.ts
+++ b/src/views/nodes/worktreeNode.ts
@@ -1,6 +1,5 @@
 import { MarkdownString, ThemeIcon, TreeItem, TreeItemCollapsibleState, Uri, window } from 'vscode';
 import { GlyphChars } from '../../constants';
-import { getContext } from '../../context';
 import type { GitUri } from '../../git/gitUri';
 import type { GitBranch } from '../../git/models/branch';
 import type { GitLog } from '../../git/models/log';
@@ -9,6 +8,7 @@ import { PullRequestState } from '../../git/models/pullRequest';
 import { shortenRevision } from '../../git/models/reference';
 import { GitRemote, GitRemoteType } from '../../git/models/remote';
 import type { GitWorktree } from '../../git/models/worktree';
+import { getContext } from '../../system/context';
 import { gate } from '../../system/decorators/gate';
 import { debug } from '../../system/decorators/log';
 import { map } from '../../system/iterable';
diff --git a/src/views/repositoriesView.ts b/src/views/repositoriesView.ts
index d5c52ce..53d25f7 100644
--- a/src/views/repositoriesView.ts
+++ b/src/views/repositoriesView.ts
@@ -4,7 +4,6 @@ import type { RepositoriesViewConfig } from '../config';
 import { ViewBranchesLayout, ViewFilesLayout, ViewShowBranchComparison } from '../config';
 import { Commands } from '../constants';
 import type { Container } from '../container';
-import { setContext } from '../context';
 import { getRemoteNameFromBranchName } from '../git/models/branch';
 import type { GitCommit } from '../git/models/commit';
 import { isCommit } from '../git/models/commit';
@@ -20,6 +19,7 @@ import type { GitRemote } from '../git/models/remote';
 import type { GitWorktree } from '../git/models/worktree';
 import { executeCommand } from '../system/command';
 import { configuration } from '../system/configuration';
+import { setContext } from '../system/context';
 import { gate } from '../system/decorators/gate';
 import { BranchesNode } from './nodes/branchesNode';
 import { BranchNode } from './nodes/branchNode';
diff --git a/src/views/searchAndCompareView.ts b/src/views/searchAndCompareView.ts
index 94d8800..bb8fc14 100644
--- a/src/views/searchAndCompareView.ts
+++ b/src/views/searchAndCompareView.ts
@@ -2,9 +2,9 @@ import type { ConfigurationChangeEvent, Disposable } from 'vscode';
 import { TreeItem, TreeItemCollapsibleState } from 'vscode';
 import type { SearchAndCompareViewConfig } from '../config';
 import { ViewFilesLayout } from '../config';
+import type { StoredNamedRef, StoredPinnedItem, StoredPinnedItems } from '../constants';
 import { Commands } from '../constants';
 import type { Container } from '../container';
-import { setContext } from '../context';
 import { unknownGitUri } from '../git/gitUri';
 import type { GitLog } from '../git/models/log';
 import { isRevisionRange, shortenRevision, splitRevisionRange } from '../git/models/reference';
@@ -12,10 +12,10 @@ import type { SearchQuery } from '../git/search';
 import { getSearchQuery } from '../git/search';
 import { ReferencesQuickPickIncludes, showReferencePicker } from '../quickpicks/referencePicker';
 import { getRepositoryOrShowPicker } from '../quickpicks/repositoryPicker';
-import type { StoredNamedRef, StoredPinnedItem, StoredPinnedItems } from '../storage';
 import { filterMap } from '../system/array';
 import { executeCommand } from '../system/command';
 import { configuration } from '../system/configuration';
+import { setContext } from '../system/context';
 import { gate } from '../system/decorators/gate';
 import { debug, log } from '../system/decorators/log';
 import { updateRecordValue } from '../system/object';
diff --git a/src/views/viewCommands.ts b/src/views/viewCommands.ts
index b6ab7c8..75ac0a6 100644
--- a/src/views/viewCommands.ts
+++ b/src/views/viewCommands.ts
@@ -10,7 +10,6 @@ import type {
 import { FileAnnotationType, ViewShowBranchComparison } from '../config';
 import { Commands } from '../constants';
 import type { Container } from '../container';
-import { setContext } from '../context';
 import { browseAtRevision } from '../git/actions';
 import * as BranchActions from '../git/actions/branch';
 import * as CommitActions from '../git/actions/commit';
@@ -33,6 +32,7 @@ import {
 	registerCommand,
 } from '../system/command';
 import { configuration } from '../system/configuration';
+import { setContext } from '../system/context';
 import { debug } from '../system/decorators/log';
 import { sequentialize } from '../system/function';
 import { openWorkspace, OpenWorkspaceLocation } from '../system/utils';
diff --git a/src/vsls/vsls.ts b/src/vsls/vsls.ts
index c460ca5..a325d29 100644
--- a/src/vsls/vsls.ts
+++ b/src/vsls/vsls.ts
@@ -2,8 +2,8 @@ import { Disposable, extensions, workspace } from 'vscode';
 import type { LiveShare, LiveShareExtension, SessionChangeEvent } from '../@types/vsls';
 import { Schemes } from '../constants';
 import type { Container } from '../container';
-import { setContext } from '../context';
 import { configuration } from '../system/configuration';
+import { setContext } from '../system/context';
 import { debug } from '../system/decorators/log';
 import { timeout } from '../system/decorators/timeout';
 import { once } from '../system/event';
diff --git a/src/webviews/commitDetails/commitDetailsWebview.ts b/src/webviews/commitDetails/commitDetailsWebview.ts
index 2a1ab92..048ce62 100644
--- a/src/webviews/commitDetails/commitDetailsWebview.ts
+++ b/src/webviews/commitDetails/commitDetailsWebview.ts
@@ -5,7 +5,6 @@ import type { CopyShaToClipboardCommandArgs } from '../../commands';
 import type { CoreConfiguration } from '../../constants';
 import { Commands } from '../../constants';
 import type { Container } from '../../container';
-import { getContext } from '../../context';
 import type { CommitSelectedEvent } from '../../eventBus';
 import { executeGitCommand } from '../../git/actions';
 import {
@@ -30,6 +29,7 @@ import type { GitRemote } from '../../git/models/remote';
 import type { ShowInCommitGraphCommandArgs } from '../../plus/webviews/graph/protocol';
 import { executeCommand, executeCoreCommand } from '../../system/command';
 import { configuration } from '../../system/configuration';
+import { getContext } from '../../system/context';
 import type { DateTimeFormat } from '../../system/date';
 import { debug } from '../../system/decorators/log';
 import type { Deferrable } from '../../system/function';
diff --git a/src/webviews/home/homeWebview.ts b/src/webviews/home/homeWebview.ts
index 3b689fc..f9f4ed5 100644
--- a/src/webviews/home/homeWebview.ts
+++ b/src/webviews/home/homeWebview.ts
@@ -3,15 +3,15 @@ import { Disposable, window } from 'vscode';
 import { getAvatarUriFromGravatarEmail } from '../../avatars';
 import { ViewsLayout } from '../../commands/setViewsLayout';
 import type { Container } from '../../container';
-import { getContext, onDidChangeContext } from '../../context';
 import type { RepositoriesVisibility } from '../../git/gitProviderService';
 import type { SubscriptionChangeEvent } from '../../plus/subscription/subscriptionService';
-import type { StorageChangeEvent } from '../../storage';
 import type { Subscription } from '../../subscription';
 import { executeCoreCommand, registerCommand } from '../../system/command';
 import { configuration } from '../../system/configuration';
+import { getContext, onDidChangeContext } from '../../system/context';
 import type { Deferrable } from '../../system/function';
 import { debounce } from '../../system/function';
+import type { StorageChangeEvent } from '../../system/storage';
 import type { IpcMessage } from '../protocol';
 import { onIpc } from '../protocol';
 import type { WebviewController, WebviewProvider } from '../webviewController';
diff --git a/src/webviews/webviewController.ts b/src/webviews/webviewController.ts
index 4b66ce3..3fc2969 100644
--- a/src/webviews/webviewController.ts
+++ b/src/webviews/webviewController.ts
@@ -3,8 +3,8 @@ import { Disposable, EventEmitter, Uri, ViewColumn, window, workspace } from 'vs
 import { getNonce } from '@env/crypto';
 import type { Commands, CustomEditorIds, WebviewIds, WebviewViewIds } from '../constants';
 import type { Container } from '../container';
-import { setContext } from '../context';
 import { executeCommand } from '../system/command';
+import { setContext } from '../system/context';
 import { debug, logName } from '../system/decorators/log';
 import { serialize } from '../system/decorators/serialize';
 import { isPromise } from '../system/promise';