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:
@@ -47,6 +47,26 @@ void main() {
|
||||
expect(act['path'], 'doc.md');
|
||||
});
|
||||
|
||||
test('CLI positional path binds to editor.open (T-232)', () async {
|
||||
// The C client sends the argv shape {positional:[...]}; the registered
|
||||
// schema's normalize must map positional[0] -> path so the handler opens
|
||||
// it (rather than returning "path is required").
|
||||
final r = await call('editor.open', {
|
||||
'positional': ['doc.md'],
|
||||
});
|
||||
expect(r.ok, isTrue, reason: r.error?.message);
|
||||
expect(r.data['path'], 'doc.md');
|
||||
});
|
||||
|
||||
test('CLI --line flag coerces to a number and binds (T-232)', () async {
|
||||
final r = await call('editor.open', {
|
||||
'positional': ['doc.md'],
|
||||
'flags': {'line': '1'},
|
||||
});
|
||||
expect(r.ok, isTrue, reason: r.error?.message);
|
||||
expect(r.data['path'], 'doc.md');
|
||||
});
|
||||
|
||||
test('editor.open with a 1-based line jumps the initial selection (T-52)', () async {
|
||||
await File('${sandbox.path}/multi.txt').writeAsString('one\ntwo\nthree\n');
|
||||
final r = await call('editor.open', {'path': 'multi.txt', 'line': 3});
|
||||
|
||||
@@ -97,6 +97,15 @@ void main() {
|
||||
expect(r.data['path'], 'README.md');
|
||||
});
|
||||
|
||||
test('CLI positional path binds to files.read (T-232)', () async {
|
||||
// argv shape {positional:[...]} → schema normalize → args['path'].
|
||||
final r = await call('files.read', const {
|
||||
'positional': ['README.md'],
|
||||
});
|
||||
expect(r.ok, isTrue, reason: r.error?.message);
|
||||
expect(r.data['content'], 'hi');
|
||||
});
|
||||
|
||||
test('files.read accepts an absolute path under the workspace root', () async {
|
||||
// Regression: the markdown reader publishes absolute skill paths
|
||||
// (e.g. .claude/skills/.../SKILL.md). An absolute path under root
|
||||
|
||||
@@ -193,6 +193,35 @@ void main() {
|
||||
expect(unknown.ok, isFalse);
|
||||
expect(unknown.error!.kind, 'not_found');
|
||||
});
|
||||
|
||||
// T-232: the CLI argv shape ({positional, flags}) must reach the handlers
|
||||
// via the registered schemas' normalize, incl. numeric coercion.
|
||||
test('CLI positional id binds to pane.focus (T-232)', () async {
|
||||
final spawn = await call('pane.spawn', {
|
||||
'argv': const ['/bin/cat'],
|
||||
});
|
||||
final id = spawn.data['id']! as String;
|
||||
final r = await call('pane.focus', {
|
||||
'positional': [id],
|
||||
});
|
||||
expect(r.ok, isTrue, reason: r.error?.message);
|
||||
expect(r.data['id'], id);
|
||||
});
|
||||
|
||||
test('CLI positional id/cols/rows coerce + bind to pane.resize (T-232)', () async {
|
||||
final spawn = await call('pane.spawn', {
|
||||
'argv': const ['/bin/cat'],
|
||||
});
|
||||
final id = spawn.data['id']! as String;
|
||||
// cols/rows arrive as strings from argv; the schema coerces them to num
|
||||
// so the handler (which reads them as num) doesn't see "required".
|
||||
final r = await call('pane.resize', {
|
||||
'positional': [id, '100', '40'],
|
||||
});
|
||||
expect(r.ok, isTrue, reason: r.error?.message);
|
||||
expect(r.data['cols'], 100);
|
||||
expect(r.data['rows'], 40);
|
||||
});
|
||||
});
|
||||
|
||||
// T-219 / D-83: `pane list` reflects the GUI tabs the user sees, merged
|
||||
|
||||
Reference in New Issue
Block a user