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 <noreply@anthropic.com>
This commit is contained in:
2026-04-22 11:09:25 +02:00
co-authored by Claude
parent f619760e7b
commit 17936bf215
+90
View File
@@ -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<void> main(List<String> 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 <paths…> Stage files (git add).
git stage-all Stage everything (git add -A).
git unstage [paths…] Unstage files (git reset HEAD).
git discard <paths…> Discard unstaged changes in files.
git commit "<msg>" 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<void> _runDaemon(List<String> args) async {
final editor = EditorRegistry(events: events, workspaceRoot: files.root);
registerEditorCommands(dispatcher, editor);
registerGitCommands(dispatcher, files.root, events);
final stopping = Completer<void>();
void shutdown(ProcessSignal sig) {
if (!stopping.isCompleted) {
@@ -256,6 +275,77 @@ Future<void> _runCliArgs(
}
}
/// `clide git <verb> [args]` — maps to `git.*` IPC verbs.
Future<void> _runGit(List<String> args) async {
if (args.isEmpty) {
_die('usage: clide git <verb> [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 <path…>');
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 <path…>');
await _runCliArgs('git.discard', {'paths': paths}, exitOnOk: true);
case 'commit':
if (args.length < 2) _die('usage: clide git commit "<message>"');
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<void> _runTail(List<String> args) async {
// Parse flags: --events (required today; keeps us honest when more