Built-in design generation now runs as an agentic MCP tool-loop (reusing the agent-rs BuiltInProvider), gated behind OPENPENCIL_DESIGN_AGENT_LOOP / the Settings experimental toggle; the orchestrator stays the default. - design-agent system prompt + in-process design toolset (parity-locked with the MCP surface) + flag-gated Intent::Design routing - spawn_agents execution as sequential sub-loops + live creation-mode badges (per-agent glow + 'N/M designing...' header) - new MCP tools: get_guidelines, ToolSearch, get_screenshot, get_editor_state, export_nodes, spawn_agents; style-guide local audit - #27 AI panel restyle: rounded tool cards + green check-rings, gray user bubbles, model-pill bottom toolbar, header, empty-state pills, the PARALLEL AGENTS (agent_team_size) 1x-6x chip dropdown - multi-chat tabs: ChatSessions model (Deref-to-active) + tab row UI (switch / close / + / Cmd+T) with each run bound to its tab Large checkpoint commit spanning the working tree (Rust shell crates).
75 lines
2 KiB
TypeScript
75 lines
2 KiB
TypeScript
// apps/desktop/git/error.ts
|
|
//
|
|
// Unified error type for the desktop git layer. Phase 1b only emits a subset
|
|
// of these codes; Phase 2 will throw the rest (auth, network, merge, etc.)
|
|
// without modifying this file.
|
|
|
|
/**
|
|
* The complete error code union. New codes should land here, not in callsites,
|
|
* so the renderer's error matrix in the spec stays in sync with the reality.
|
|
*/
|
|
export type GitErrorCode =
|
|
// Phase 1b emits these:
|
|
| 'init-failed'
|
|
| 'open-failed'
|
|
| 'not-a-repo'
|
|
| 'commit-empty'
|
|
| 'branch-exists'
|
|
| 'branch-current'
|
|
| 'branch-unmerged'
|
|
| 'engine-crash'
|
|
// Phase 2 will emit these (declared here for forward-compat):
|
|
| 'no-file'
|
|
| 'clone-failed'
|
|
| 'clone-target-exists'
|
|
| 'clone-network'
|
|
| 'auth-required'
|
|
| 'auth-failed'
|
|
| 'auth-token-invalid'
|
|
| 'network'
|
|
| 'timeout'
|
|
| 'commit-author-missing'
|
|
| 'pull-non-fast-forward'
|
|
| 'push-rejected'
|
|
| 'push-no-remote'
|
|
| 'branch-switch-dirty'
|
|
| 'merge-conflict'
|
|
| 'merge-conflict-non-op'
|
|
| 'merge-still-conflicted'
|
|
| 'merge-abort-failed'
|
|
| 'restore-dirty'
|
|
| 'ssh-not-supported-iso'
|
|
| 'ssh-key-missing'
|
|
| 'concurrent-busy'
|
|
| 'external-modified'
|
|
| 'save-required';
|
|
|
|
export class GitError extends Error {
|
|
readonly code: GitErrorCode;
|
|
readonly recoverable: boolean;
|
|
readonly detail?: unknown;
|
|
|
|
constructor(
|
|
code: GitErrorCode,
|
|
message: string,
|
|
opts: { recoverable?: boolean; detail?: unknown; cause?: unknown } = {},
|
|
) {
|
|
super(message, opts.cause !== undefined ? { cause: opts.cause } : undefined);
|
|
this.name = 'GitError';
|
|
this.code = code;
|
|
this.recoverable = opts.recoverable ?? true;
|
|
if (opts.detail !== undefined) this.detail = opts.detail;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Type guard for catching GitError specifically (since `instanceof` across
|
|
* realms can be flaky in tests, this is a defensive backup).
|
|
*/
|
|
export function isGitError(err: unknown): err is GitError {
|
|
return (
|
|
err instanceof GitError ||
|
|
(typeof err === 'object' && err !== null && (err as { name?: string }).name === 'GitError')
|
|
);
|
|
}
|