From 17936bf215658f7fb2f015ca06b0f15005284ea1 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Wed, 22 Apr 2026 11:09:25 +0200 Subject: [PATCH] add clide git CLI shortcuts Maps clide git status|diff|stage|unstage|discard|commit|log|stash| pull|push to the git.* IPC verbs. Updates --help to list the new tier-3 subcommands. Co-Authored-By: Claude --- bin/clide.dart | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/bin/clide.dart b/bin/clide.dart index d432da96..ab87bfa3 100644 --- a/bin/clide.dart +++ b/bin/clide.dart @@ -18,6 +18,7 @@ import 'package:clide/clide.dart'; // can't compile for web. See lib/clide.dart for the barrel split. import 'package:clide/src/daemon/editor_commands.dart'; import 'package:clide/src/daemon/files_commands.dart'; +import 'package:clide/src/daemon/git_commands.dart'; import 'package:clide/src/daemon/pane_commands.dart'; import 'package:clide/src/editor/registry.dart' show EditorRegistry; import 'package:clide/src/panes/registry.dart'; @@ -65,6 +66,8 @@ Future main(List argv) async { ); case 'save': await _runCliArgs('editor.save', const {}, exitOnOk: true); + case 'git': + await _runGit(rest); case 'tail': await _runTail(rest); default: @@ -102,6 +105,20 @@ Editor (tier 2): `-` reads text from stdin. save Save the active buffer (editor.save). +Git (tier 3): + git status Working tree status (staged/unstaged/conflicts). + git diff [--staged] [P] Diff for file(s) P, or all if omitted. + git stage Stage files (git add). + git stage-all Stage everything (git add -A). + git unstage [paths…] Unstage files (git reset HEAD). + git discard Discard unstaged changes in files. + git commit "" Commit staged changes. + git log [--count N] Recent commit log (default 20). + git stash [--message M] Stash working changes. + git stash-pop Pop the top stash entry. + git pull Pull from remote. + git push [remote] [br] Push to remote. + Event subscription: tail --events [--filter SUBSYSTEM[:ID]] Stream events as JSON lines. --filter keeps @@ -133,6 +150,8 @@ Future _runDaemon(List args) async { final editor = EditorRegistry(events: events, workspaceRoot: files.root); registerEditorCommands(dispatcher, editor); + registerGitCommands(dispatcher, files.root, events); + final stopping = Completer(); void shutdown(ProcessSignal sig) { if (!stopping.isCompleted) { @@ -256,6 +275,77 @@ Future _runCliArgs( } } +/// `clide git [args]` — maps to `git.*` IPC verbs. +Future _runGit(List args) async { + if (args.isEmpty) { + _die('usage: clide git [args…]'); + } + switch (args.first) { + case 'status': + await _runCliArgs('git.status', const {}, exitOnOk: true); + case 'diff': + final staged = args.contains('--staged'); + final paths = args.sublist(1).where((a) => !a.startsWith('--')).toList(); + await _runCliArgs( + 'git.diff', + {'staged': staged, if (paths.isNotEmpty) 'paths': paths}, + exitOnOk: true, + ); + case 'stage': + final paths = args.sublist(1); + if (paths.isEmpty) _die('usage: clide git stage '); + await _runCliArgs('git.stage', {'paths': paths}, exitOnOk: true); + case 'stage-all': + await _runCliArgs('git.stage-all', const {}, exitOnOk: true); + case 'unstage': + final paths = args.sublist(1); + await _runCliArgs('git.unstage', {'paths': paths}, exitOnOk: true); + case 'discard': + final paths = args.sublist(1); + if (paths.isEmpty) _die('usage: clide git discard '); + await _runCliArgs('git.discard', {'paths': paths}, exitOnOk: true); + case 'commit': + if (args.length < 2) _die('usage: clide git commit ""'); + final message = args.sublist(1).join(' '); + await _runCliArgs('git.commit', {'message': message}, exitOnOk: true); + case 'log': + var count = 20; + for (var i = 1; i < args.length; i++) { + if (args[i] == '--count' && i + 1 < args.length) { + count = int.tryParse(args[i + 1]) ?? 20; + } + } + await _runCliArgs('git.log', {'count': count}, exitOnOk: true); + case 'stash': + String? message; + for (var i = 1; i < args.length; i++) { + if (args[i] == '--message' && i + 1 < args.length) { + message = args[i + 1]; + } + } + await _runCliArgs( + 'git.stash', + {if (message != null) 'message': message}, + exitOnOk: true, + ); + case 'stash-pop': + await _runCliArgs('git.stash-pop', const {}, exitOnOk: true); + case 'pull': + await _runCliArgs('git.pull', const {}, exitOnOk: true); + case 'push': + final rest = args.sublist(1); + final setUpstream = rest.contains('-u'); + final positional = rest.where((a) => a != '-u').toList(); + await _runCliArgs('git.push', { + if (setUpstream) 'setUpstream': true, + if (positional.isNotEmpty) 'remote': positional[0], + if (positional.length > 1) 'branch': positional[1], + }, exitOnOk: true); + default: + _die('unknown git verb: ${args.first}'); + } +} + /// `clide tail --events [--filter SUBSYSTEM[:ID]]` — stream events. Future _runTail(List args) async { // Parse flags: --events (required today; keeps us honest when more