make the clide command surface self-describing
Parity guarantees a verb exists for every UI action, but a verb is unreachable if nothing advertises it. Add `clide capabilities` — it reflects the live dispatcher registry to JSON (subsystem, verb, arg schema) so the surface is discoverable and can't drift from what dispatches. A thin /clide skill points Claude at it rather than hard-coding a verb list, so new panels become reachable the moment they register. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
library;
|
||||
|
||||
import 'package:clide/clide.dart';
|
||||
import 'package:clide/src/ipc/command_schema.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
IpcRequest _req(String cmd, {String id = '1', Map<String, Object?> args = const {}}) {
|
||||
@@ -51,7 +52,44 @@ void main() {
|
||||
expect(d.isEmpty, isFalse);
|
||||
});
|
||||
|
||||
test('clear removes user handlers but keeps ping + version', () async {
|
||||
test('capabilities reflects the live registry with schemas (T-248)', () async {
|
||||
final d = DaemonDispatcher();
|
||||
d.register('echo', (req) async => IpcResponse.ok(id: req.id, data: const {}));
|
||||
d.register(
|
||||
'pane.resize',
|
||||
(req) async => IpcResponse.ok(id: req.id, data: const {}),
|
||||
schema: const CommandSchema(
|
||||
positional: ['id', 'cols'],
|
||||
args: {
|
||||
'id': ArgSpec(required: true),
|
||||
'cols': ArgSpec(type: ArgType.number, min: 1),
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
final r = await d.dispatch(_req('capabilities'));
|
||||
expect(r.ok, isTrue);
|
||||
final commands = r.data['commands'] as Map<String, Object?>;
|
||||
// Built-ins + the two just registered are all discoverable.
|
||||
expect(commands.keys, containsAll(['ping', 'version', 'capabilities', 'echo', 'pane.resize']));
|
||||
|
||||
// Subsystem/verb split.
|
||||
final resize = commands['pane.resize'] as Map<String, Object?>;
|
||||
expect(resize['subsystem'], 'pane');
|
||||
expect(resize['verb'], 'resize');
|
||||
expect(resize['positional'], ['id', 'cols']);
|
||||
final args = resize['args'] as Map<String, Object?>;
|
||||
expect((args['id'] as Map)['required'], true);
|
||||
expect((args['cols'] as Map)['type'], 'number');
|
||||
expect((args['cols'] as Map)['min'], 1);
|
||||
|
||||
// A schema-less command carries no positional/args keys.
|
||||
final echo = commands['echo'] as Map<String, Object?>;
|
||||
expect(echo['subsystem'], '');
|
||||
expect(echo.containsKey('args'), isFalse);
|
||||
});
|
||||
|
||||
test('clear removes user handlers but keeps the built-ins', () async {
|
||||
final d = DaemonDispatcher();
|
||||
d.register('extra', (req) async => IpcResponse.ok(id: req.id, data: const {}));
|
||||
expect(d.isEmpty, isFalse);
|
||||
|
||||
Reference in New Issue
Block a user