feat(vscode): Implement Git Backend via VS Code Git extension (#92)
This commit is contained in:
@@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
- VS Code extension: added git backend integration for UI to access
|
||||||
|
- VS Code extension: Only show the main Worktree in the Chat Sidebar
|
||||||
|
|
||||||
## [1.4.0] - 2026-01-01
|
## [1.4.0] - 2026-01-01
|
||||||
|
|
||||||
- Added the ability to run multiple agents from a single prompt, with each agent working in an isolated worktree.
|
- Added the ability to run multiple agents from a single prompt, with each agent working in an isolated worktree.
|
||||||
|
|||||||
@@ -159,6 +159,7 @@ export const VSCodeLayout: React.FC = () => {
|
|||||||
allowReselect
|
allowReselect
|
||||||
onSessionSelected={() => setCurrentView('chat')}
|
onSessionSelected={() => setCurrentView('chat')}
|
||||||
hideDirectoryControls
|
hideDirectoryControls
|
||||||
|
showOnlyMainWorkspace
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ interface SessionSidebarProps {
|
|||||||
onSessionSelected?: (sessionId: string) => void;
|
onSessionSelected?: (sessionId: string) => void;
|
||||||
allowReselect?: boolean;
|
allowReselect?: boolean;
|
||||||
hideDirectoryControls?: boolean;
|
hideDirectoryControls?: boolean;
|
||||||
|
showOnlyMainWorkspace?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||||
@@ -116,6 +117,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
|||||||
onSessionSelected,
|
onSessionSelected,
|
||||||
allowReselect = false,
|
allowReselect = false,
|
||||||
hideDirectoryControls = false,
|
hideDirectoryControls = false,
|
||||||
|
showOnlyMainWorkspace = false,
|
||||||
}) => {
|
}) => {
|
||||||
const [editingId, setEditingId] = React.useState<string | null>(null);
|
const [editingId, setEditingId] = React.useState<string | null>(null);
|
||||||
const [editTitle, setEditTitle] = React.useState('');
|
const [editTitle, setEditTitle] = React.useState('');
|
||||||
@@ -1061,10 +1063,10 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
|||||||
>
|
>
|
||||||
{groupedSessions.length === 0 ? (
|
{groupedSessions.length === 0 ? (
|
||||||
emptyState
|
emptyState
|
||||||
) : hideDirectoryControls && groupedSessions.length === 1 && groupedSessions[0].isMain ? (
|
) : (hideDirectoryControls && groupedSessions.length === 1 && groupedSessions[0].isMain) || showOnlyMainWorkspace ? (
|
||||||
<div className="space-y-[0.6rem] py-1">
|
<div className="space-y-[0.6rem] py-1">
|
||||||
{(() => {
|
{(() => {
|
||||||
const group = groupedSessions[0];
|
const group = groupedSessions.find(g => g.isMain) ?? groupedSessions[0];
|
||||||
const maxVisible = hideDirectoryControls ? 10 : 7;
|
const maxVisible = hideDirectoryControls ? 10 : 7;
|
||||||
const totalSessions = group.sessions.length;
|
const totalSessions = group.sessions.length;
|
||||||
const isExpanded = expandedSessionGroups.has(group.id);
|
const isExpanded = expandedSessionGroups.has(group.id);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import * as path from 'path';
|
|||||||
import type { OpenCodeManager } from './opencode';
|
import type { OpenCodeManager } from './opencode';
|
||||||
import { createAgent, createCommand, deleteAgent, deleteCommand, getAgentSources, getCommandSources, updateAgent, updateCommand, type AgentScope, type CommandScope, AGENT_SCOPE, COMMAND_SCOPE, discoverSkills, getSkillSources, createSkill, updateSkill, deleteSkill, readSkillSupportingFile, writeSkillSupportingFile, deleteSkillSupportingFile, type SkillScope, SKILL_SCOPE } from './opencodeConfig';
|
import { createAgent, createCommand, deleteAgent, deleteCommand, getAgentSources, getCommandSources, updateAgent, updateCommand, type AgentScope, type CommandScope, AGENT_SCOPE, COMMAND_SCOPE, discoverSkills, getSkillSources, createSkill, updateSkill, deleteSkill, readSkillSupportingFile, writeSkillSupportingFile, deleteSkillSupportingFile, type SkillScope, SKILL_SCOPE } from './opencodeConfig';
|
||||||
import { removeProviderAuth } from './opencodeAuth';
|
import { removeProviderAuth } from './opencodeAuth';
|
||||||
|
import * as gitService from './gitService';
|
||||||
import {
|
import {
|
||||||
getSkillsCatalog,
|
getSkillsCatalog,
|
||||||
scanSkillsRepository as scanSkillsRepositoryFromGit,
|
scanSkillsRepository as scanSkillsRepositoryFromGit,
|
||||||
@@ -1047,6 +1048,287 @@ export async function handleBridgeMessage(message: BridgeRequest, ctx?: BridgeCo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============== Git Operations ==============
|
||||||
|
|
||||||
|
case 'api:git/check': {
|
||||||
|
const { directory } = (payload || {}) as { directory?: string };
|
||||||
|
if (!directory) {
|
||||||
|
return { id, type, success: false, error: 'Directory is required' };
|
||||||
|
}
|
||||||
|
const isRepo = await gitService.checkIsGitRepository(directory);
|
||||||
|
return { id, type, success: true, data: isRepo };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'api:git/worktree-type': {
|
||||||
|
const { directory } = (payload || {}) as { directory?: string };
|
||||||
|
if (!directory) {
|
||||||
|
return { id, type, success: false, error: 'Directory is required' };
|
||||||
|
}
|
||||||
|
const isLinked = await gitService.isLinkedWorktree(directory);
|
||||||
|
return { id, type, success: true, data: isLinked };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'api:git/status': {
|
||||||
|
const { directory } = (payload || {}) as { directory?: string };
|
||||||
|
if (!directory) {
|
||||||
|
return { id, type, success: false, error: 'Directory is required' };
|
||||||
|
}
|
||||||
|
const status = await gitService.getGitStatus(directory);
|
||||||
|
return { id, type, success: true, data: status };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'api:git/branches': {
|
||||||
|
const { directory, method, name, startPoint, force } = (payload || {}) as {
|
||||||
|
directory?: string;
|
||||||
|
method?: string;
|
||||||
|
name?: string;
|
||||||
|
startPoint?: string;
|
||||||
|
force?: boolean;
|
||||||
|
};
|
||||||
|
if (!directory) {
|
||||||
|
return { id, type, success: false, error: 'Directory is required' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedMethod = typeof method === 'string' ? method.toUpperCase() : 'GET';
|
||||||
|
|
||||||
|
if (normalizedMethod === 'GET') {
|
||||||
|
const branches = await gitService.getGitBranches(directory);
|
||||||
|
return { id, type, success: true, data: branches };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedMethod === 'POST') {
|
||||||
|
if (!name) {
|
||||||
|
return { id, type, success: false, error: 'Branch name is required' };
|
||||||
|
}
|
||||||
|
const result = await gitService.createBranch(directory, name, startPoint);
|
||||||
|
return { id, type, success: true, data: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedMethod === 'DELETE') {
|
||||||
|
if (!name) {
|
||||||
|
return { id, type, success: false, error: 'Branch name is required' };
|
||||||
|
}
|
||||||
|
const result = await gitService.deleteGitBranch(directory, name, force);
|
||||||
|
return { id, type, success: true, data: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { id, type, success: false, error: `Unsupported method: ${normalizedMethod}` };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'api:git/remote-branches': {
|
||||||
|
const { directory, branch, remote } = (payload || {}) as {
|
||||||
|
directory?: string;
|
||||||
|
branch?: string;
|
||||||
|
remote?: string;
|
||||||
|
};
|
||||||
|
if (!directory || !branch) {
|
||||||
|
return { id, type, success: false, error: 'Directory and branch are required' };
|
||||||
|
}
|
||||||
|
const result = await gitService.deleteRemoteBranch(directory, branch, remote);
|
||||||
|
return { id, type, success: true, data: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'api:git/checkout': {
|
||||||
|
const { directory, branch } = (payload || {}) as { directory?: string; branch?: string };
|
||||||
|
if (!directory || !branch) {
|
||||||
|
return { id, type, success: false, error: 'Directory and branch are required' };
|
||||||
|
}
|
||||||
|
const result = await gitService.checkoutBranch(directory, branch);
|
||||||
|
return { id, type, success: true, data: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'api:git/worktrees': {
|
||||||
|
const { directory, method, path: worktreePath, branch, createBranch, force } = (payload || {}) as {
|
||||||
|
directory?: string;
|
||||||
|
method?: string;
|
||||||
|
path?: string;
|
||||||
|
branch?: string;
|
||||||
|
createBranch?: boolean;
|
||||||
|
force?: boolean;
|
||||||
|
};
|
||||||
|
if (!directory) {
|
||||||
|
return { id, type, success: false, error: 'Directory is required' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedMethod = typeof method === 'string' ? method.toUpperCase() : 'GET';
|
||||||
|
|
||||||
|
if (normalizedMethod === 'GET') {
|
||||||
|
const worktrees = await gitService.listGitWorktrees(directory);
|
||||||
|
return { id, type, success: true, data: worktrees };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedMethod === 'POST') {
|
||||||
|
if (!worktreePath || !branch) {
|
||||||
|
return { id, type, success: false, error: 'Path and branch are required' };
|
||||||
|
}
|
||||||
|
const result = await gitService.addGitWorktree(directory, worktreePath, branch, createBranch);
|
||||||
|
return { id, type, success: true, data: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedMethod === 'DELETE') {
|
||||||
|
if (!worktreePath) {
|
||||||
|
return { id, type, success: false, error: 'Path is required' };
|
||||||
|
}
|
||||||
|
const result = await gitService.removeGitWorktree(directory, worktreePath, force);
|
||||||
|
return { id, type, success: true, data: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { id, type, success: false, error: `Unsupported method: ${normalizedMethod}` };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'api:git/diff': {
|
||||||
|
const { directory, path: filePath, staged, contextLines } = (payload || {}) as {
|
||||||
|
directory?: string;
|
||||||
|
path?: string;
|
||||||
|
staged?: boolean;
|
||||||
|
contextLines?: number;
|
||||||
|
};
|
||||||
|
if (!directory || !filePath) {
|
||||||
|
return { id, type, success: false, error: 'Directory and path are required' };
|
||||||
|
}
|
||||||
|
const result = await gitService.getGitDiff(directory, filePath, staged, contextLines);
|
||||||
|
return { id, type, success: true, data: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'api:git/file-diff': {
|
||||||
|
const { directory, path: filePath, staged } = (payload || {}) as {
|
||||||
|
directory?: string;
|
||||||
|
path?: string;
|
||||||
|
staged?: boolean;
|
||||||
|
};
|
||||||
|
if (!directory || !filePath) {
|
||||||
|
return { id, type, success: false, error: 'Directory and path are required' };
|
||||||
|
}
|
||||||
|
const result = await gitService.getGitFileDiff(directory, filePath, staged);
|
||||||
|
return { id, type, success: true, data: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'api:git/revert': {
|
||||||
|
const { directory, path: filePath } = (payload || {}) as { directory?: string; path?: string };
|
||||||
|
if (!directory || !filePath) {
|
||||||
|
return { id, type, success: false, error: 'Directory and path are required' };
|
||||||
|
}
|
||||||
|
await gitService.revertGitFile(directory, filePath);
|
||||||
|
return { id, type, success: true, data: { success: true } };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'api:git/commit': {
|
||||||
|
const { directory, message, addAll, files } = (payload || {}) as {
|
||||||
|
directory?: string;
|
||||||
|
message?: string;
|
||||||
|
addAll?: boolean;
|
||||||
|
files?: string[];
|
||||||
|
};
|
||||||
|
if (!directory || !message) {
|
||||||
|
return { id, type, success: false, error: 'Directory and message are required' };
|
||||||
|
}
|
||||||
|
const result = await gitService.createGitCommit(directory, message, { addAll, files });
|
||||||
|
return { id, type, success: true, data: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'api:git/push': {
|
||||||
|
const { directory, remote, branch, options } = (payload || {}) as {
|
||||||
|
directory?: string;
|
||||||
|
remote?: string;
|
||||||
|
branch?: string;
|
||||||
|
options?: string[] | Record<string, unknown>;
|
||||||
|
};
|
||||||
|
if (!directory) {
|
||||||
|
return { id, type, success: false, error: 'Directory is required' };
|
||||||
|
}
|
||||||
|
const result = await gitService.gitPush(directory, { remote, branch, options });
|
||||||
|
return { id, type, success: true, data: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'api:git/pull': {
|
||||||
|
const { directory, remote, branch } = (payload || {}) as {
|
||||||
|
directory?: string;
|
||||||
|
remote?: string;
|
||||||
|
branch?: string;
|
||||||
|
};
|
||||||
|
if (!directory) {
|
||||||
|
return { id, type, success: false, error: 'Directory is required' };
|
||||||
|
}
|
||||||
|
const result = await gitService.gitPull(directory, { remote, branch });
|
||||||
|
return { id, type, success: true, data: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'api:git/fetch': {
|
||||||
|
const { directory, remote, branch } = (payload || {}) as {
|
||||||
|
directory?: string;
|
||||||
|
remote?: string;
|
||||||
|
branch?: string;
|
||||||
|
};
|
||||||
|
if (!directory) {
|
||||||
|
return { id, type, success: false, error: 'Directory is required' };
|
||||||
|
}
|
||||||
|
const result = await gitService.gitFetch(directory, { remote, branch });
|
||||||
|
return { id, type, success: true, data: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'api:git/log': {
|
||||||
|
const { directory, maxCount, from, to, file } = (payload || {}) as {
|
||||||
|
directory?: string;
|
||||||
|
maxCount?: number;
|
||||||
|
from?: string;
|
||||||
|
to?: string;
|
||||||
|
file?: string;
|
||||||
|
};
|
||||||
|
if (!directory) {
|
||||||
|
return { id, type, success: false, error: 'Directory is required' };
|
||||||
|
}
|
||||||
|
const result = await gitService.getGitLog(directory, { maxCount, from, to, file });
|
||||||
|
return { id, type, success: true, data: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'api:git/commit-files': {
|
||||||
|
const { directory, hash } = (payload || {}) as { directory?: string; hash?: string };
|
||||||
|
if (!directory || !hash) {
|
||||||
|
return { id, type, success: false, error: 'Directory and hash are required' };
|
||||||
|
}
|
||||||
|
const result = await gitService.getCommitFiles(directory, hash);
|
||||||
|
return { id, type, success: true, data: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'api:git/identity': {
|
||||||
|
const { directory, method, userName, userEmail, sshKey } = (payload || {}) as {
|
||||||
|
directory?: string;
|
||||||
|
method?: string;
|
||||||
|
userName?: string;
|
||||||
|
userEmail?: string;
|
||||||
|
sshKey?: string | null;
|
||||||
|
};
|
||||||
|
if (!directory) {
|
||||||
|
return { id, type, success: false, error: 'Directory is required' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedMethod = typeof method === 'string' ? method.toUpperCase() : 'GET';
|
||||||
|
|
||||||
|
if (normalizedMethod === 'GET') {
|
||||||
|
const identity = await gitService.getCurrentGitIdentity(directory);
|
||||||
|
return { id, type, success: true, data: identity };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedMethod === 'POST') {
|
||||||
|
if (!userName || !userEmail) {
|
||||||
|
return { id, type, success: false, error: 'userName and userEmail are required' };
|
||||||
|
}
|
||||||
|
const result = await gitService.setGitIdentity(directory, userName, userEmail, sshKey);
|
||||||
|
return { id, type, success: true, data: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { id, type, success: false, error: `Unsupported method: ${normalizedMethod}` };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'api:git/ignore-openchamber': {
|
||||||
|
const { directory } = (payload || {}) as { directory?: string };
|
||||||
|
if (!directory) {
|
||||||
|
return { id, type, success: false, error: 'Directory is required' };
|
||||||
|
}
|
||||||
|
await gitService.ensureOpenChamberIgnored(directory);
|
||||||
|
return { id, type, success: true, data: { success: true } };
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return { id, type, success: false, error: `Unknown message type: ${type}` };
|
return { id, type, success: false, error: `Unknown message type: ${type}` };
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+349
@@ -0,0 +1,349 @@
|
|||||||
|
/**
|
||||||
|
* VS Code Git Extension API type definitions
|
||||||
|
* Based on the vscode.git extension API
|
||||||
|
* @see https://github.com/microsoft/vscode/blob/main/extensions/git/src/api/git.d.ts
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { Uri, Event, Disposable } from 'vscode';
|
||||||
|
|
||||||
|
export interface Git {
|
||||||
|
readonly path: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InputBox {
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum ForcePushMode {
|
||||||
|
Force,
|
||||||
|
ForceWithLease,
|
||||||
|
ForceWithLeaseIfIncludes,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum RefType {
|
||||||
|
Head,
|
||||||
|
RemoteHead,
|
||||||
|
Tag,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Ref {
|
||||||
|
readonly type: RefType;
|
||||||
|
readonly name?: string;
|
||||||
|
readonly commit?: string;
|
||||||
|
readonly remote?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpstreamRef {
|
||||||
|
readonly remote: string;
|
||||||
|
readonly name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Branch extends Ref {
|
||||||
|
readonly upstream?: UpstreamRef;
|
||||||
|
readonly ahead?: number;
|
||||||
|
readonly behind?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Commit {
|
||||||
|
readonly hash: string;
|
||||||
|
readonly message: string;
|
||||||
|
readonly parents: string[];
|
||||||
|
readonly authorDate?: Date;
|
||||||
|
readonly authorName?: string;
|
||||||
|
readonly authorEmail?: string;
|
||||||
|
readonly commitDate?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Submodule {
|
||||||
|
readonly name: string;
|
||||||
|
readonly path: string;
|
||||||
|
readonly url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Remote {
|
||||||
|
readonly name: string;
|
||||||
|
readonly fetchUrl?: string;
|
||||||
|
readonly pushUrl?: string;
|
||||||
|
readonly isReadOnly: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum Status {
|
||||||
|
INDEX_MODIFIED,
|
||||||
|
INDEX_ADDED,
|
||||||
|
INDEX_DELETED,
|
||||||
|
INDEX_RENAMED,
|
||||||
|
INDEX_COPIED,
|
||||||
|
MODIFIED,
|
||||||
|
DELETED,
|
||||||
|
UNTRACKED,
|
||||||
|
IGNORED,
|
||||||
|
INTENT_TO_ADD,
|
||||||
|
INTENT_TO_RENAME,
|
||||||
|
TYPE_CHANGED,
|
||||||
|
ADDED_BY_US,
|
||||||
|
ADDED_BY_THEM,
|
||||||
|
DELETED_BY_US,
|
||||||
|
DELETED_BY_THEM,
|
||||||
|
BOTH_ADDED,
|
||||||
|
BOTH_DELETED,
|
||||||
|
BOTH_MODIFIED,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Change {
|
||||||
|
readonly uri: Uri;
|
||||||
|
readonly originalUri: Uri;
|
||||||
|
readonly renameUri: Uri | undefined;
|
||||||
|
readonly status: Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RepositoryState {
|
||||||
|
readonly HEAD: Branch | undefined;
|
||||||
|
readonly refs: Ref[];
|
||||||
|
readonly remotes: Remote[];
|
||||||
|
readonly submodules: Submodule[];
|
||||||
|
readonly rebaseCommit: Commit | undefined;
|
||||||
|
readonly mergeChanges: Change[];
|
||||||
|
readonly indexChanges: Change[];
|
||||||
|
readonly workingTreeChanges: Change[];
|
||||||
|
readonly onDidChange: Event<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RepositoryUIState {
|
||||||
|
readonly selected: boolean;
|
||||||
|
readonly onDidChange: Event<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CommitOptions {
|
||||||
|
all?: boolean | 'tracked';
|
||||||
|
amend?: boolean;
|
||||||
|
signoff?: boolean;
|
||||||
|
signCommit?: boolean;
|
||||||
|
empty?: boolean;
|
||||||
|
noVerify?: boolean;
|
||||||
|
requireUserConfig?: boolean;
|
||||||
|
useEditor?: boolean;
|
||||||
|
verbose?: boolean;
|
||||||
|
postCommitCommand?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FetchOptions {
|
||||||
|
remote?: string;
|
||||||
|
ref?: string;
|
||||||
|
all?: boolean;
|
||||||
|
prune?: boolean;
|
||||||
|
depth?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BranchQuery {
|
||||||
|
readonly remote?: boolean;
|
||||||
|
readonly pattern?: string;
|
||||||
|
readonly count?: number;
|
||||||
|
readonly contains?: string;
|
||||||
|
readonly sort?: 'alphabetically' | 'committerdate';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LogOptions {
|
||||||
|
readonly maxEntries?: number;
|
||||||
|
readonly path?: string;
|
||||||
|
readonly reverse?: boolean;
|
||||||
|
readonly sortByAuthorDate?: boolean;
|
||||||
|
readonly shortStats?: boolean;
|
||||||
|
readonly range?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Repository {
|
||||||
|
readonly rootUri: Uri;
|
||||||
|
readonly inputBox: InputBox;
|
||||||
|
readonly state: RepositoryState;
|
||||||
|
readonly ui: RepositoryUIState;
|
||||||
|
|
||||||
|
getConfigs(): Promise<{ key: string; value: string }[]>;
|
||||||
|
getConfig(key: string): Promise<string>;
|
||||||
|
setConfig(key: string, value: string): Promise<string>;
|
||||||
|
getGlobalConfig(key: string): Promise<string>;
|
||||||
|
|
||||||
|
getObjectDetails(treeish: string, path: string): Promise<{ mode: string; object: string; size: number }>;
|
||||||
|
detectObjectType(object: string): Promise<{ mimetype: string; encoding?: string }>;
|
||||||
|
buffer(ref: string, path: string): Promise<Buffer>;
|
||||||
|
show(ref: string, path: string): Promise<string>;
|
||||||
|
getCommit(ref: string): Promise<Commit>;
|
||||||
|
|
||||||
|
add(paths: string[]): Promise<void>;
|
||||||
|
revert(paths: string[]): Promise<void>;
|
||||||
|
clean(paths: string[]): Promise<void>;
|
||||||
|
|
||||||
|
apply(patch: string, reverse?: boolean): Promise<void>;
|
||||||
|
diff(cached?: boolean): Promise<string>;
|
||||||
|
diffWithHEAD(): Promise<Change[]>;
|
||||||
|
diffWithHEAD(path: string): Promise<string>;
|
||||||
|
diffWith(ref: string): Promise<Change[]>;
|
||||||
|
diffWith(ref: string, path: string): Promise<string>;
|
||||||
|
diffIndexWithHEAD(): Promise<Change[]>;
|
||||||
|
diffIndexWithHEAD(path: string): Promise<string>;
|
||||||
|
diffIndexWith(ref: string): Promise<Change[]>;
|
||||||
|
diffIndexWith(ref: string, path: string): Promise<string>;
|
||||||
|
diffBlobs(object1: string, object2: string): Promise<string>;
|
||||||
|
diffBetween(ref1: string, ref2: string): Promise<Change[]>;
|
||||||
|
diffBetween(ref1: string, ref2: string, path: string): Promise<string>;
|
||||||
|
|
||||||
|
hashObject(data: string): Promise<string>;
|
||||||
|
|
||||||
|
createBranch(name: string, checkout: boolean, ref?: string): Promise<void>;
|
||||||
|
deleteBranch(name: string, force?: boolean): Promise<void>;
|
||||||
|
getBranch(name: string): Promise<Branch>;
|
||||||
|
getBranches(query: BranchQuery): Promise<Ref[]>;
|
||||||
|
getBranchBase(name: string): Promise<Branch | undefined>;
|
||||||
|
setBranchUpstream(name: string, upstream: string): Promise<void>;
|
||||||
|
|
||||||
|
getRefs(query: { contains?: string; count?: number; pattern?: string; sort?: 'alphabetically' | 'committerdate' }): Promise<Ref[]>;
|
||||||
|
|
||||||
|
getMergeBase(ref1: string, ref2: string): Promise<string>;
|
||||||
|
|
||||||
|
tag(name: string, upstream: string): Promise<void>;
|
||||||
|
deleteTag(name: string): Promise<void>;
|
||||||
|
|
||||||
|
status(): Promise<void>;
|
||||||
|
checkout(treeish: string): Promise<void>;
|
||||||
|
|
||||||
|
addRemote(name: string, url: string): Promise<void>;
|
||||||
|
removeRemote(name: string): Promise<void>;
|
||||||
|
renameRemote(name: string, newName: string): Promise<void>;
|
||||||
|
|
||||||
|
fetch(options?: FetchOptions): Promise<void>;
|
||||||
|
fetch(remote?: string, ref?: string, depth?: number): Promise<void>;
|
||||||
|
pull(unshallow?: boolean): Promise<void>;
|
||||||
|
push(remoteName?: string, branchName?: string, setUpstream?: boolean, force?: ForcePushMode): Promise<void>;
|
||||||
|
|
||||||
|
blame(path: string): Promise<string>;
|
||||||
|
log(options?: LogOptions): Promise<Commit[]>;
|
||||||
|
|
||||||
|
commit(message: string, opts?: CommitOptions): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RemoteSource {
|
||||||
|
readonly name: string;
|
||||||
|
readonly description?: string;
|
||||||
|
readonly url: string | string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RemoteSourceProvider {
|
||||||
|
readonly name: string;
|
||||||
|
readonly icon?: string;
|
||||||
|
readonly supportsQuery?: boolean;
|
||||||
|
getRemoteSources(query?: string): Promise<RemoteSource[]>;
|
||||||
|
getBranches?(url: string): Promise<string[]>;
|
||||||
|
publishRepository?(repository: Repository): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RemoteSourcePublisher {
|
||||||
|
readonly name: string;
|
||||||
|
readonly icon?: string;
|
||||||
|
publishRepository(repository: Repository): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Credentials {
|
||||||
|
readonly username: string;
|
||||||
|
readonly password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CredentialsProvider {
|
||||||
|
getCredentials(host: Uri): Promise<Credentials | undefined>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PostCommitCommandsProvider {
|
||||||
|
getCommands(repository: Repository): Promise<Array<{ command: string; title: string; tooltip?: string }>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PushErrorHandler {
|
||||||
|
handlePushError(repository: Repository, remote: Remote, refspec: string, error: Error & { gitErrorCode?: string }): Promise<boolean>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BranchProtection {
|
||||||
|
readonly remote: string;
|
||||||
|
readonly rules: BranchProtectionRule[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BranchProtectionRule {
|
||||||
|
readonly include?: string[];
|
||||||
|
readonly exclude?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BranchProtectionProvider {
|
||||||
|
onDidChangeBranchProtection: Event<Uri>;
|
||||||
|
provideBranchProtection(): Promise<BranchProtection[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type APIState = 'uninitialized' | 'initialized';
|
||||||
|
|
||||||
|
export interface PublishEvent {
|
||||||
|
repository: Repository;
|
||||||
|
branch?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface API {
|
||||||
|
readonly state: APIState;
|
||||||
|
readonly onDidChangeState: Event<APIState>;
|
||||||
|
readonly onDidPublish: Event<PublishEvent>;
|
||||||
|
readonly git: Git;
|
||||||
|
readonly repositories: Repository[];
|
||||||
|
readonly onDidOpenRepository: Event<Repository>;
|
||||||
|
readonly onDidCloseRepository: Event<Repository>;
|
||||||
|
|
||||||
|
toGitUri(uri: Uri, ref: string): Uri;
|
||||||
|
getRepository(uri: Uri): Repository | null;
|
||||||
|
init(root: Uri): Promise<Repository | null>;
|
||||||
|
openRepository(root: Uri): Promise<Repository | null>;
|
||||||
|
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
|
||||||
|
registerRemoteSourcePublisher(publisher: RemoteSourcePublisher): Disposable;
|
||||||
|
registerCredentialsProvider(provider: CredentialsProvider): Disposable;
|
||||||
|
registerPostCommitCommandsProvider(provider: PostCommitCommandsProvider): Disposable;
|
||||||
|
registerPushErrorHandler(handler: PushErrorHandler): Disposable;
|
||||||
|
registerBranchProtectionProvider(root: Uri, provider: BranchProtectionProvider): Disposable;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GitExtension {
|
||||||
|
readonly enabled: boolean;
|
||||||
|
readonly onDidChangeEnablement: Event<boolean>;
|
||||||
|
getAPI(version: 1): API;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum GitErrorCodes {
|
||||||
|
BadConfigFile = 'BadConfigFile',
|
||||||
|
AuthenticationFailed = 'AuthenticationFailed',
|
||||||
|
NoUserNameConfigured = 'NoUserNameConfigured',
|
||||||
|
NoUserEmailConfigured = 'NoUserEmailConfigured',
|
||||||
|
NoRemoteRepositorySpecified = 'NoRemoteRepositorySpecified',
|
||||||
|
NotAGitRepository = 'NotAGitRepository',
|
||||||
|
NotAtRepositoryRoot = 'NotAtRepositoryRoot',
|
||||||
|
Conflict = 'Conflict',
|
||||||
|
StashConflict = 'StashConflict',
|
||||||
|
UnmergedChanges = 'UnmergedChanges',
|
||||||
|
PushRejected = 'PushRejected',
|
||||||
|
RemoteConnectionError = 'RemoteConnectionError',
|
||||||
|
DirtyWorkTree = 'DirtyWorkTree',
|
||||||
|
CantOpenResource = 'CantOpenResource',
|
||||||
|
GitNotFound = 'GitNotFound',
|
||||||
|
CantCreatePipe = 'CantCreatePipe',
|
||||||
|
PermissionDenied = 'PermissionDenied',
|
||||||
|
CantAccessRemote = 'CantAccessRemote',
|
||||||
|
RepositoryNotFound = 'RepositoryNotFound',
|
||||||
|
RepositoryIsLocked = 'RepositoryIsLocked',
|
||||||
|
BranchNotFullyMerged = 'BranchNotFullyMerged',
|
||||||
|
NoRemoteReference = 'NoRemoteReference',
|
||||||
|
InvalidBranchName = 'InvalidBranchName',
|
||||||
|
BranchAlreadyExists = 'BranchAlreadyExists',
|
||||||
|
NoLocalChanges = 'NoLocalChanges',
|
||||||
|
NoStashFound = 'NoStashFound',
|
||||||
|
LocalChangesOverwritten = 'LocalChangesOverwritten',
|
||||||
|
NoUpstreamBranch = 'NoUpstreamBranch',
|
||||||
|
IsInSubmodule = 'IsInSubmodule',
|
||||||
|
WrongCase = 'WrongCase',
|
||||||
|
CantLockRef = 'CantLockRef',
|
||||||
|
CantRebaseMultipleBranches = 'CantRebaseMultipleBranches',
|
||||||
|
PatchDoesNotApply = 'PatchDoesNotApply',
|
||||||
|
NoPathFound = 'NoPathFound',
|
||||||
|
UnknownPath = 'UnknownPath',
|
||||||
|
EmptyCommitMessage = 'EmptyCommitMessage',
|
||||||
|
BranchFastForwardRejected = 'BranchFastForwardRejected',
|
||||||
|
TagConflict = 'TagConflict',
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,227 @@
|
|||||||
|
/**
|
||||||
|
* VS Code Git API implementation
|
||||||
|
* Uses bridge messages to communicate with the extension host
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { sendBridgeMessage } from './bridge';
|
||||||
|
import type {
|
||||||
|
GitAPI,
|
||||||
|
GitStatus,
|
||||||
|
GitDiffResponse,
|
||||||
|
GetGitDiffOptions,
|
||||||
|
GitFileDiffResponse,
|
||||||
|
GetGitFileDiffOptions,
|
||||||
|
GitBranch,
|
||||||
|
GitDeleteBranchPayload,
|
||||||
|
GitDeleteRemoteBranchPayload,
|
||||||
|
GeneratedCommitMessage,
|
||||||
|
GitWorktreeInfo,
|
||||||
|
GitAddWorktreePayload,
|
||||||
|
GitRemoveWorktreePayload,
|
||||||
|
GitCommitResult,
|
||||||
|
CreateGitCommitOptions,
|
||||||
|
GitPushResult,
|
||||||
|
GitPullResult,
|
||||||
|
GitLogResponse,
|
||||||
|
GitLogOptions,
|
||||||
|
GitCommitFilesResponse,
|
||||||
|
GitIdentitySummary,
|
||||||
|
GitIdentityProfile,
|
||||||
|
} from '@openchamber/ui/lib/api/types';
|
||||||
|
|
||||||
|
export const createVSCodeGitAPI = (): GitAPI => ({
|
||||||
|
checkIsGitRepository: async (directory: string): Promise<boolean> => {
|
||||||
|
return sendBridgeMessage<boolean>('api:git/check', { directory });
|
||||||
|
},
|
||||||
|
|
||||||
|
getGitStatus: async (directory: string): Promise<GitStatus> => {
|
||||||
|
return sendBridgeMessage<GitStatus>('api:git/status', { directory });
|
||||||
|
},
|
||||||
|
|
||||||
|
getGitDiff: async (directory: string, options: GetGitDiffOptions): Promise<GitDiffResponse> => {
|
||||||
|
return sendBridgeMessage<GitDiffResponse>('api:git/diff', {
|
||||||
|
directory,
|
||||||
|
path: options.path,
|
||||||
|
staged: options.staged,
|
||||||
|
contextLines: options.contextLines,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getGitFileDiff: async (directory: string, options: GetGitFileDiffOptions): Promise<GitFileDiffResponse> => {
|
||||||
|
return sendBridgeMessage<GitFileDiffResponse>('api:git/file-diff', {
|
||||||
|
directory,
|
||||||
|
path: options.path,
|
||||||
|
staged: options.staged,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
revertGitFile: async (directory: string, filePath: string): Promise<void> => {
|
||||||
|
await sendBridgeMessage('api:git/revert', { directory, path: filePath });
|
||||||
|
},
|
||||||
|
|
||||||
|
isLinkedWorktree: async (directory: string): Promise<boolean> => {
|
||||||
|
return sendBridgeMessage<boolean>('api:git/worktree-type', { directory });
|
||||||
|
},
|
||||||
|
|
||||||
|
getGitBranches: async (directory: string): Promise<GitBranch> => {
|
||||||
|
return sendBridgeMessage<GitBranch>('api:git/branches', { directory, method: 'GET' });
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteGitBranch: async (directory: string, payload: GitDeleteBranchPayload): Promise<{ success: boolean }> => {
|
||||||
|
return sendBridgeMessage<{ success: boolean }>('api:git/branches', {
|
||||||
|
directory,
|
||||||
|
method: 'DELETE',
|
||||||
|
name: payload.branch,
|
||||||
|
force: payload.force,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteRemoteBranch: async (directory: string, payload: GitDeleteRemoteBranchPayload): Promise<{ success: boolean }> => {
|
||||||
|
return sendBridgeMessage<{ success: boolean }>('api:git/remote-branches', {
|
||||||
|
directory,
|
||||||
|
branch: payload.branch,
|
||||||
|
remote: payload.remote,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
generateCommitMessage: async (directory: string, files: string[]): Promise<{ message: GeneratedCommitMessage }> => {
|
||||||
|
// This requires AI integration - stubbed for now
|
||||||
|
void directory; // Unused for now
|
||||||
|
void files; // Unused for now
|
||||||
|
return {
|
||||||
|
message: {
|
||||||
|
subject: '',
|
||||||
|
highlights: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
listGitWorktrees: async (directory: string): Promise<GitWorktreeInfo[]> => {
|
||||||
|
return sendBridgeMessage<GitWorktreeInfo[]>('api:git/worktrees', { directory, method: 'GET' });
|
||||||
|
},
|
||||||
|
|
||||||
|
addGitWorktree: async (directory: string, payload: GitAddWorktreePayload): Promise<{ success: boolean; path: string; branch: string }> => {
|
||||||
|
return sendBridgeMessage<{ success: boolean; path: string; branch: string }>('api:git/worktrees', {
|
||||||
|
directory,
|
||||||
|
method: 'POST',
|
||||||
|
path: payload.path,
|
||||||
|
branch: payload.branch,
|
||||||
|
createBranch: payload.createBranch,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeGitWorktree: async (directory: string, payload: GitRemoveWorktreePayload): Promise<{ success: boolean }> => {
|
||||||
|
return sendBridgeMessage<{ success: boolean }>('api:git/worktrees', {
|
||||||
|
directory,
|
||||||
|
method: 'DELETE',
|
||||||
|
path: payload.path,
|
||||||
|
force: payload.force,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
ensureOpenChamberIgnored: async (directory: string): Promise<void> => {
|
||||||
|
await sendBridgeMessage('api:git/ignore-openchamber', { directory });
|
||||||
|
},
|
||||||
|
|
||||||
|
createGitCommit: async (directory: string, message: string, options?: CreateGitCommitOptions): Promise<GitCommitResult> => {
|
||||||
|
return sendBridgeMessage<GitCommitResult>('api:git/commit', {
|
||||||
|
directory,
|
||||||
|
message,
|
||||||
|
addAll: options?.addAll,
|
||||||
|
files: options?.files,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
gitPush: async (directory: string, options?: { remote?: string; branch?: string; options?: string[] | Record<string, unknown> }): Promise<GitPushResult> => {
|
||||||
|
return sendBridgeMessage<GitPushResult>('api:git/push', {
|
||||||
|
directory,
|
||||||
|
remote: options?.remote,
|
||||||
|
branch: options?.branch,
|
||||||
|
options: options?.options,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
gitPull: async (directory: string, options?: { remote?: string; branch?: string }): Promise<GitPullResult> => {
|
||||||
|
return sendBridgeMessage<GitPullResult>('api:git/pull', {
|
||||||
|
directory,
|
||||||
|
remote: options?.remote,
|
||||||
|
branch: options?.branch,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
gitFetch: async (directory: string, options?: { remote?: string; branch?: string }): Promise<{ success: boolean }> => {
|
||||||
|
return sendBridgeMessage<{ success: boolean }>('api:git/fetch', {
|
||||||
|
directory,
|
||||||
|
remote: options?.remote,
|
||||||
|
branch: options?.branch,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
checkoutBranch: async (directory: string, branch: string): Promise<{ success: boolean; branch: string }> => {
|
||||||
|
return sendBridgeMessage<{ success: boolean; branch: string }>('api:git/checkout', {
|
||||||
|
directory,
|
||||||
|
branch,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
createBranch: async (directory: string, name: string, startPoint?: string): Promise<{ success: boolean; branch: string }> => {
|
||||||
|
return sendBridgeMessage<{ success: boolean; branch: string }>('api:git/branches', {
|
||||||
|
directory,
|
||||||
|
method: 'POST',
|
||||||
|
name,
|
||||||
|
startPoint,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getGitLog: async (directory: string, options?: GitLogOptions): Promise<GitLogResponse> => {
|
||||||
|
return sendBridgeMessage<GitLogResponse>('api:git/log', {
|
||||||
|
directory,
|
||||||
|
maxCount: options?.maxCount,
|
||||||
|
from: options?.from,
|
||||||
|
to: options?.to,
|
||||||
|
file: options?.file,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getCommitFiles: async (directory: string, hash: string): Promise<GitCommitFilesResponse> => {
|
||||||
|
return sendBridgeMessage<GitCommitFilesResponse>('api:git/commit-files', {
|
||||||
|
directory,
|
||||||
|
hash,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getCurrentGitIdentity: async (directory: string): Promise<GitIdentitySummary | null> => {
|
||||||
|
return sendBridgeMessage<GitIdentitySummary | null>('api:git/identity', {
|
||||||
|
directory,
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setGitIdentity: async (directory: string, profileId: string): Promise<{ success: boolean; profile: GitIdentityProfile }> => {
|
||||||
|
// For VS Code, we need to resolve the profile from the store
|
||||||
|
// This is a simplified implementation - the full implementation would need profile lookup
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
profile: { id: profileId, name: '', userName: '', userEmail: '' },
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// Git identity profile management - these are stored in extension settings
|
||||||
|
// For simplicity, return empty arrays/objects as these are managed through the settings UI
|
||||||
|
getGitIdentities: async (): Promise<GitIdentityProfile[]> => {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
|
||||||
|
createGitIdentity: async (profile: GitIdentityProfile): Promise<GitIdentityProfile> => {
|
||||||
|
return profile;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateGitIdentity: async (id: string, profile: GitIdentityProfile): Promise<GitIdentityProfile> => {
|
||||||
|
void id; // Unused for now
|
||||||
|
return profile;
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteGitIdentity: async (id: string): Promise<void> => {
|
||||||
|
void id; // Unused for now
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import type { RuntimeAPIs, TerminalAPI, GitAPI, NotificationsAPI, GitIdentityProfile } from '../../../ui/src/lib/api/types';
|
import type { RuntimeAPIs, TerminalAPI, NotificationsAPI } from '@openchamber/ui/lib/api/types';
|
||||||
import { createVSCodeFilesAPI } from './files';
|
import { createVSCodeFilesAPI } from './files';
|
||||||
import { createVSCodeSettingsAPI } from './settings';
|
import { createVSCodeSettingsAPI } from './settings';
|
||||||
import { createVSCodePermissionsAPI } from './permissions';
|
import { createVSCodePermissionsAPI } from './permissions';
|
||||||
import { createVSCodeToolsAPI } from './tools';
|
import { createVSCodeToolsAPI } from './tools';
|
||||||
import { createVSCodeEditorAPI } from './editor';
|
import { createVSCodeEditorAPI } from './editor';
|
||||||
|
import { createVSCodeGitAPI } from './git';
|
||||||
|
|
||||||
// Stub APIs return sensible defaults instead of throwing
|
// Stub APIs return sensible defaults instead of throwing
|
||||||
const createStubTerminalAPI = (): TerminalAPI => ({
|
const createStubTerminalAPI = (): TerminalAPI => ({
|
||||||
@@ -14,37 +15,6 @@ const createStubTerminalAPI = (): TerminalAPI => ({
|
|||||||
close: async () => {},
|
close: async () => {},
|
||||||
});
|
});
|
||||||
|
|
||||||
const createStubGitAPI = (): GitAPI => ({
|
|
||||||
checkIsGitRepository: async () => false,
|
|
||||||
getGitStatus: async () => ({ current: '', tracking: null, ahead: 0, behind: 0, files: [], isClean: true }),
|
|
||||||
getGitDiff: async () => ({ diff: '' }),
|
|
||||||
getGitFileDiff: async () => ({ original: '', modified: '', path: '' }),
|
|
||||||
revertGitFile: async () => {},
|
|
||||||
isLinkedWorktree: async () => false,
|
|
||||||
getGitBranches: async () => ({ all: [], current: '', branches: {} }),
|
|
||||||
deleteGitBranch: async () => ({ success: false }),
|
|
||||||
deleteRemoteBranch: async () => ({ success: false }),
|
|
||||||
generateCommitMessage: async () => ({ message: { subject: '', highlights: [] } }),
|
|
||||||
listGitWorktrees: async () => [],
|
|
||||||
addGitWorktree: async () => ({ success: false, path: '', branch: '' }),
|
|
||||||
removeGitWorktree: async () => ({ success: false }),
|
|
||||||
ensureOpenChamberIgnored: async () => {},
|
|
||||||
createGitCommit: async () => ({ success: false, commit: '', branch: '', summary: { changes: 0, insertions: 0, deletions: 0 } }),
|
|
||||||
gitPush: async () => ({ success: false, pushed: [], repo: '', ref: null }),
|
|
||||||
gitPull: async () => ({ success: false, summary: { changes: 0, insertions: 0, deletions: 0 }, files: [], insertions: 0, deletions: 0 }),
|
|
||||||
gitFetch: async () => ({ success: false }),
|
|
||||||
checkoutBranch: async () => ({ success: false, branch: '' }),
|
|
||||||
createBranch: async () => ({ success: false, branch: '' }),
|
|
||||||
getGitLog: async () => ({ all: [], latest: null, total: 0 }),
|
|
||||||
getCommitFiles: async () => ({ files: [] }),
|
|
||||||
getCurrentGitIdentity: async () => null,
|
|
||||||
setGitIdentity: async () => ({ success: false, profile: { id: '', name: '', userName: '', userEmail: '' } }),
|
|
||||||
getGitIdentities: async () => [],
|
|
||||||
createGitIdentity: async (profile: GitIdentityProfile) => profile,
|
|
||||||
updateGitIdentity: async (_id: string, profile: GitIdentityProfile) => profile,
|
|
||||||
deleteGitIdentity: async () => {},
|
|
||||||
});
|
|
||||||
|
|
||||||
const createStubNotificationsAPI = (): NotificationsAPI => ({
|
const createStubNotificationsAPI = (): NotificationsAPI => ({
|
||||||
notifyAgentCompletion: async () => true,
|
notifyAgentCompletion: async () => true,
|
||||||
canNotify: () => true,
|
canNotify: () => true,
|
||||||
@@ -53,7 +23,7 @@ const createStubNotificationsAPI = (): NotificationsAPI => ({
|
|||||||
export const createVSCodeAPIs = (): RuntimeAPIs => ({
|
export const createVSCodeAPIs = (): RuntimeAPIs => ({
|
||||||
runtime: { platform: 'vscode', isDesktop: false, isVSCode: true, label: 'VS Code Extension' },
|
runtime: { platform: 'vscode', isDesktop: false, isVSCode: true, label: 'VS Code Extension' },
|
||||||
terminal: createStubTerminalAPI(),
|
terminal: createStubTerminalAPI(),
|
||||||
git: createStubGitAPI(),
|
git: createVSCodeGitAPI(),
|
||||||
files: createVSCodeFilesAPI(),
|
files: createVSCodeFilesAPI(),
|
||||||
settings: createVSCodeSettingsAPI(),
|
settings: createVSCodeSettingsAPI(),
|
||||||
permissions: createVSCodePermissionsAPI(),
|
permissions: createVSCodePermissionsAPI(),
|
||||||
|
|||||||
Reference in New Issue
Block a user