make CLI argv args reach typed command handlers (T-232)
Parameterized subsystem commands were unreachable from the CLI: the
argv translator emits {positional, flags} but the handlers read named
top-level keys (args['path'], args['id'], ...), and nothing mapped
between them -- so 'clide editor open <path>' returned 'path is
required'. The fix needed no new mechanism: D-74's CommandSchema.normalize
already folds the argv shape into named args by a declared positional
ordering; these commands just never registered a schema.
Adopts it for the navigation/drive surface -- editor.open/activate/read/
save/close, files.read/ls, pane.close/focus/resize/write -- with
non-required positional schemas, so the only effect is positional->named
mapping plus numeric coercion of line/cols/rows. Handlers unchanged;
missing-arg errors unchanged. Edit-mutation verbs, pane.spawn, and git
arg verbs are deferred (noted on the ticket).
Takes effect on app restart (the dispatcher is built once at boot).
Closes T-232 (under T-208 'Give Claude hands').
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import 'dart:io' show FileSystemException;
|
||||
|
||||
import '../editor/buffer.dart' show Selection;
|
||||
import '../editor/registry.dart';
|
||||
import '../ipc/command_schema.dart';
|
||||
import '../ipc/envelope.dart';
|
||||
import '../ipc/errno_mapping.dart';
|
||||
import '../ipc/schema_v1.dart';
|
||||
@@ -20,18 +21,25 @@ import 'dispatcher.dart';
|
||||
|
||||
export '../editor/buffer.dart' show Selection;
|
||||
|
||||
// Positional schemas so the CLI binds `clide editor <verb> <args…>` to the
|
||||
// named keys the handlers read (T-232, via the D-74 normalize hook). Args are
|
||||
// declared non-required — the handlers keep their own presence checks — so the
|
||||
// only effect is positional→named mapping plus numeric coercion of `line`.
|
||||
const _idArg = CommandSchema(positional: ['id'], args: {'id': ArgSpec()});
|
||||
|
||||
void registerEditorCommands(DaemonDispatcher d, EditorRegistry registry) {
|
||||
d.register('editor.open', (req) => _open(req, registry));
|
||||
d.register('editor.open', (req) => _open(req, registry),
|
||||
schema: const CommandSchema(positional: ['path', 'line'], args: {'path': ArgSpec(), 'line': ArgSpec(type: ArgType.number)}));
|
||||
d.register('editor.active', (req) => _active(req, registry));
|
||||
d.register('editor.activate', (req) => _activate(req, registry));
|
||||
d.register('editor.activate', (req) => _activate(req, registry), schema: _idArg);
|
||||
d.register('editor.list', (req) => _list(req, registry));
|
||||
d.register('editor.read', (req) => _read(req, registry));
|
||||
d.register('editor.read', (req) => _read(req, registry), schema: _idArg);
|
||||
d.register('editor.insert', (req) => _insert(req, registry));
|
||||
d.register('editor.replace-selection', (req) => _replace(req, registry));
|
||||
d.register('editor.set-selection', (req) => _setSelection(req, registry));
|
||||
d.register('editor.set-content', (req) => _setContent(req, registry));
|
||||
d.register('editor.save', (req) => _save(req, registry));
|
||||
d.register('editor.close', (req) => _close(req, registry));
|
||||
d.register('editor.save', (req) => _save(req, registry), schema: _idArg);
|
||||
d.register('editor.close', (req) => _close(req, registry), schema: _idArg);
|
||||
}
|
||||
|
||||
IpcResponse _userErr(String id, String msg, {String? hint}) => IpcResponse.err(
|
||||
|
||||
@@ -9,11 +9,17 @@ import '../files/listing.dart';
|
||||
import '../files/path_safety.dart';
|
||||
import '../files/pql_config.dart';
|
||||
import '../files/watcher.dart';
|
||||
import '../ipc/command_schema.dart';
|
||||
import '../ipc/envelope.dart';
|
||||
import '../ipc/schema_v1.dart';
|
||||
import '../panes/event_sink.dart';
|
||||
import 'dispatcher.dart';
|
||||
|
||||
/// Positional schema binding `clide files <verb> <path>` to args['path']
|
||||
/// (T-232, via D-74 normalize). Non-required — the handlers keep their own
|
||||
/// path checks — so the only effect is the positional→named mapping.
|
||||
const _pathArg = CommandSchema(positional: ['path'], args: {'path': ArgSpec()});
|
||||
|
||||
/// Cap on `files.read` response size. UI doesn't render multi-MB
|
||||
/// blobs usefully and a single uncapped call can OOM. Range/stream
|
||||
/// reads will land as a separate command (T-104 follow-up).
|
||||
@@ -112,7 +118,7 @@ void registerFilesCommands(DaemonDispatcher d, FilesService files) {
|
||||
}
|
||||
final content = file.readAsStringSync();
|
||||
return IpcResponse.ok(id: req.id, data: {'path': path, 'content': content});
|
||||
});
|
||||
}, schema: _pathArg);
|
||||
|
||||
d.register('files.ls', (req) async {
|
||||
final dir = (req.args['path'] as String?) ?? '';
|
||||
@@ -135,7 +141,7 @@ void registerFilesCommands(DaemonDispatcher d, FilesService files) {
|
||||
'entries': [for (final e in entries) e.toJson()],
|
||||
},
|
||||
);
|
||||
});
|
||||
}, schema: _pathArg);
|
||||
|
||||
d.register('files.walk', (req) async {
|
||||
final result = await walkFiles(root: files.root, ignore: files.ignore);
|
||||
|
||||
@@ -11,6 +11,7 @@ library;
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import '../ipc/command_schema.dart';
|
||||
import '../ipc/envelope.dart';
|
||||
import '../ipc/errno_mapping.dart';
|
||||
import '../ipc/schema_v1.dart';
|
||||
@@ -27,12 +28,19 @@ import 'dispatcher.dart';
|
||||
typedef ViewPaneSource = List<ViewPane> Function();
|
||||
|
||||
void registerPaneCommands(DaemonDispatcher d, PaneRegistry registry, {ViewPaneSource? viewPanes}) {
|
||||
// Positional schemas bind `clide pane <verb> <args…>` to the named keys the
|
||||
// handlers read (T-232, via D-74 normalize). Non-required — handlers keep
|
||||
// their presence checks — so the effect is positional→named mapping plus
|
||||
// numeric coercion of cols/rows.
|
||||
const idArg = CommandSchema(positional: ['id'], args: {'id': ArgSpec()});
|
||||
d.register('pane.spawn', (req) => _spawn(req, registry));
|
||||
d.register('pane.list', (req) => _list(req, registry, viewPanes));
|
||||
d.register('pane.close', (req) => _close(req, registry));
|
||||
d.register('pane.write', (req) => _write(req, registry));
|
||||
d.register('pane.resize', (req) => _resize(req, registry));
|
||||
d.register('pane.focus', (req) => _focus(req, registry));
|
||||
d.register('pane.close', (req) => _close(req, registry), schema: idArg);
|
||||
d.register('pane.write', (req) => _write(req, registry), schema: const CommandSchema(positional: ['id', 'text'], args: {'id': ArgSpec(), 'text': ArgSpec()}));
|
||||
d.register('pane.resize', (req) => _resize(req, registry),
|
||||
schema: const CommandSchema(
|
||||
positional: ['id', 'cols', 'rows'], args: {'id': ArgSpec(), 'cols': ArgSpec(type: ArgType.number), 'rows': ArgSpec(type: ArgType.number)}));
|
||||
d.register('pane.focus', (req) => _focus(req, registry), schema: idArg);
|
||||
d.register('pane.tail', (req) => _tail(req, registry));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user