@ottocode/server 0.1.265 → 0.1.267

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/package.json +3 -3
  2. package/src/routes/auth/copilot.ts +699 -0
  3. package/src/routes/auth/oauth.ts +578 -0
  4. package/src/routes/auth/onboarding.ts +45 -0
  5. package/src/routes/auth/providers.ts +189 -0
  6. package/src/routes/auth/service.ts +167 -0
  7. package/src/routes/auth/state.ts +23 -0
  8. package/src/routes/auth/status.ts +203 -0
  9. package/src/routes/auth/wallet.ts +229 -0
  10. package/src/routes/auth.ts +12 -2080
  11. package/src/routes/config/models-service.ts +411 -0
  12. package/src/routes/config/models.ts +6 -426
  13. package/src/routes/config/providers-service.ts +237 -0
  14. package/src/routes/config/providers.ts +10 -242
  15. package/src/routes/files/handlers.ts +297 -0
  16. package/src/routes/files/service.ts +313 -0
  17. package/src/routes/files.ts +12 -608
  18. package/src/routes/git/commit-service.ts +207 -0
  19. package/src/routes/git/commit.ts +6 -220
  20. package/src/routes/git/remote-service.ts +116 -0
  21. package/src/routes/git/remote.ts +8 -115
  22. package/src/routes/git/staging-service.ts +111 -0
  23. package/src/routes/git/staging.ts +10 -205
  24. package/src/routes/mcp/auth.ts +338 -0
  25. package/src/routes/mcp/lifecycle.ts +263 -0
  26. package/src/routes/mcp/servers.ts +212 -0
  27. package/src/routes/mcp/service.ts +664 -0
  28. package/src/routes/mcp/state.ts +13 -0
  29. package/src/routes/mcp.ts +6 -1233
  30. package/src/routes/ottorouter/billing.ts +593 -0
  31. package/src/routes/ottorouter/service.ts +92 -0
  32. package/src/routes/ottorouter/topup.ts +301 -0
  33. package/src/routes/ottorouter/wallet.ts +370 -0
  34. package/src/routes/ottorouter.ts +6 -1319
  35. package/src/routes/research/service.ts +339 -0
  36. package/src/routes/research.ts +12 -390
  37. package/src/routes/sessions/crud.ts +563 -0
  38. package/src/routes/sessions/queue.ts +242 -0
  39. package/src/routes/sessions/retry.ts +121 -0
  40. package/src/routes/sessions/service.ts +768 -0
  41. package/src/routes/sessions/share.ts +434 -0
  42. package/src/routes/sessions.ts +8 -1977
  43. package/src/routes/skills/service.ts +221 -0
  44. package/src/routes/skills/spec.ts +309 -0
  45. package/src/routes/skills.ts +31 -909
  46. package/src/routes/terminals/service.ts +326 -0
  47. package/src/routes/terminals.ts +19 -295
  48. package/src/routes/tunnel/service.ts +217 -0
  49. package/src/routes/tunnel.ts +29 -219
  50. package/src/runtime/agent/registry-prompts.ts +147 -0
  51. package/src/runtime/agent/registry.ts +6 -124
  52. package/src/runtime/agent/runner-errors.ts +116 -0
  53. package/src/runtime/agent/runner-reminders.ts +45 -0
  54. package/src/runtime/agent/runner-setup-model.ts +75 -0
  55. package/src/runtime/agent/runner-setup-prompt.ts +185 -0
  56. package/src/runtime/agent/runner-setup-tools.ts +103 -0
  57. package/src/runtime/agent/runner-setup-utils.ts +21 -0
  58. package/src/runtime/agent/runner-setup.ts +54 -288
  59. package/src/runtime/agent/runner-telemetry.ts +112 -0
  60. package/src/runtime/agent/runner-text.ts +108 -0
  61. package/src/runtime/agent/runner-tool-observer.ts +86 -0
  62. package/src/runtime/agent/runner.ts +79 -378
  63. package/src/runtime/prompt/builder.ts +5 -1
  64. package/src/runtime/prompt/capabilities.ts +13 -8
  65. package/src/runtime/provider/custom.ts +73 -0
  66. package/src/runtime/provider/index.ts +2 -85
  67. package/src/runtime/provider/reasoning-builders.ts +280 -0
  68. package/src/runtime/provider/reasoning.ts +67 -264
  69. package/src/tools/adapter/events.ts +116 -0
  70. package/src/tools/adapter/execution.ts +160 -0
  71. package/src/tools/adapter/pending.ts +37 -0
  72. package/src/tools/adapter/persistence.ts +166 -0
  73. package/src/tools/adapter/results.ts +97 -0
  74. package/src/tools/adapter.ts +124 -451
@@ -1,915 +1,37 @@
1
1
  import type { Hono } from 'hono';
2
- import {
3
- discoverSkills,
4
- filterDiscoveredSkills,
5
- loadSkill,
6
- loadSkillFile,
7
- discoverSkillFiles,
8
- findGitRoot,
9
- validateSkillName,
10
- parseSkillFile,
11
- logger,
12
- loadConfig,
13
- writeSkillSettings,
14
- } from '@ottocode/sdk';
15
- import { serializeError } from '../runtime/errors/api-error.ts';
16
2
  import { openApiRoute } from '../openapi/route.ts';
17
-
18
- function dedupeSkillsByName<T extends { name: string }>(skills: T[]): T[] {
19
- const seen = new Set<string>();
20
- return skills.filter((skill) => {
21
- const key = skill.name.trim();
22
- if (!key || seen.has(key)) return false;
23
- seen.add(key);
24
- return true;
25
- });
26
- }
27
-
28
- function sortSkillsByName<T extends { name: string }>(skills: T[]): T[] {
29
- return [...skills].sort((a, b) => a.name.localeCompare(b.name));
30
- }
31
-
32
- function mapSkillsWithEnabled(
33
- discovered: Array<{
34
- name: string;
35
- description: string;
36
- scope: string;
37
- path: string;
38
- }>,
39
- cfg: Awaited<ReturnType<typeof loadConfig>>,
40
- ) {
41
- return discovered.map((skill) => ({
42
- name: skill.name,
43
- description: skill.description,
44
- scope: skill.scope,
45
- path: skill.path,
46
- enabled: cfg.skills?.items?.[skill.name]?.enabled !== false,
47
- }));
3
+ import {
4
+ getSkill,
5
+ getSkillFile,
6
+ getSkillsConfig,
7
+ listSkillFiles,
8
+ listSkills,
9
+ updateSkillsConfig,
10
+ validateSkill,
11
+ validateSkillNameRoute,
12
+ } from './skills/service.ts';
13
+ import {
14
+ getSkillFileSpec,
15
+ getSkillsConfigSpec,
16
+ getSkillSpec,
17
+ listSkillFilesSpec,
18
+ listSkillsSpec,
19
+ updateSkillsConfigSpec,
20
+ validateSkillNameSpec,
21
+ validateSkillSpec,
22
+ } from './skills/spec.ts';
23
+
24
+ function routeSpec(spec: unknown): Parameters<typeof openApiRoute>[1] {
25
+ return spec as Parameters<typeof openApiRoute>[1];
48
26
  }
49
27
 
50
28
  export function registerSkillsRoutes(app: Hono) {
51
- openApiRoute(
52
- app,
53
- {
54
- method: 'get',
55
- path: '/v1/skills',
56
- tags: ['config'],
57
- operationId: 'listSkills',
58
- summary: 'List discovered skills',
59
- parameters: [
60
- {
61
- in: 'query',
62
- name: 'project',
63
- required: false,
64
- schema: {
65
- type: 'string',
66
- },
67
- description:
68
- 'Project root override (defaults to current working directory).',
69
- },
70
- ],
71
- responses: {
72
- '200': {
73
- description: 'OK',
74
- content: {
75
- 'application/json': {
76
- schema: {
77
- type: 'object',
78
- properties: {
79
- skills: {
80
- type: 'array',
81
- items: {
82
- type: 'object',
83
- properties: {
84
- name: {
85
- type: 'string',
86
- },
87
- description: {
88
- type: 'string',
89
- },
90
- scope: {
91
- type: 'string',
92
- },
93
- path: {
94
- type: 'string',
95
- },
96
- },
97
- required: ['name', 'description', 'scope', 'path'],
98
- },
99
- },
100
- },
101
- required: ['skills'],
102
- },
103
- },
104
- },
105
- },
106
- '500': {
107
- description: 'Bad Request',
108
- content: {
109
- 'application/json': {
110
- schema: {
111
- type: 'object',
112
- properties: {
113
- error: {
114
- type: 'string',
115
- },
116
- },
117
- required: ['error'],
118
- },
119
- },
120
- },
121
- },
122
- },
123
- },
124
- async (c) => {
125
- try {
126
- const projectRoot = c.req.query('project') || process.cwd();
127
- const cfg = await loadConfig(projectRoot);
128
- const repoRoot = (await findGitRoot(projectRoot)) ?? projectRoot;
129
- const discovered = sortSkillsByName(
130
- await discoverSkills(projectRoot, repoRoot),
131
- );
132
- const filtered = filterDiscoveredSkills(discovered, cfg.skills);
133
- const unique = sortSkillsByName(dedupeSkillsByName(filtered));
134
- return c.json({
135
- skills: mapSkillsWithEnabled(unique, cfg),
136
- });
137
- } catch (error) {
138
- logger.error('Failed to list skills', error);
139
- const errorResponse = serializeError(error);
140
- return c.json(
141
- errorResponse,
142
- (errorResponse.error.status || 500) as 500,
143
- );
144
- }
145
- },
146
- );
147
-
148
- openApiRoute(
149
- app,
150
- {
151
- method: 'get',
152
- path: '/v1/config/skills',
153
- tags: ['config'],
154
- operationId: 'getSkillsConfig',
155
- summary: 'Get skills enable/disable config and counts',
156
- parameters: [
157
- {
158
- in: 'query',
159
- name: 'project',
160
- required: false,
161
- schema: {
162
- type: 'string',
163
- },
164
- description:
165
- 'Project root override (defaults to current working directory).',
166
- },
167
- ],
168
- responses: {
169
- '200': {
170
- description: 'OK',
171
- content: {
172
- 'application/json': {
173
- schema: {
174
- type: 'object',
175
- properties: {
176
- enabled: {
177
- type: 'boolean',
178
- },
179
- totalCount: {
180
- type: 'number',
181
- },
182
- enabledCount: {
183
- type: 'number',
184
- },
185
- items: {
186
- type: 'array',
187
- items: {
188
- type: 'object',
189
- properties: {
190
- name: {
191
- type: 'string',
192
- },
193
- description: {
194
- type: 'string',
195
- },
196
- scope: {
197
- type: 'string',
198
- },
199
- path: {
200
- type: 'string',
201
- },
202
- enabled: {
203
- type: 'boolean',
204
- },
205
- },
206
- required: [
207
- 'name',
208
- 'description',
209
- 'scope',
210
- 'path',
211
- 'enabled',
212
- ],
213
- },
214
- },
215
- },
216
- required: ['enabled', 'totalCount', 'enabledCount', 'items'],
217
- },
218
- },
219
- },
220
- },
221
- '500': {
222
- description: 'Bad Request',
223
- content: {
224
- 'application/json': {
225
- schema: {
226
- type: 'object',
227
- properties: {
228
- error: {
229
- type: 'string',
230
- },
231
- },
232
- required: ['error'],
233
- },
234
- },
235
- },
236
- },
237
- },
238
- },
239
- async (c) => {
240
- try {
241
- const projectRoot = c.req.query('project') || process.cwd();
242
- const cfg = await loadConfig(projectRoot);
243
- const repoRoot = (await findGitRoot(projectRoot)) ?? projectRoot;
244
- const discovered = sortSkillsByName(
245
- dedupeSkillsByName(await discoverSkills(projectRoot, repoRoot)),
246
- );
247
- const filtered = sortSkillsByName(
248
- filterDiscoveredSkills(discovered, cfg.skills),
249
- );
250
- return c.json({
251
- enabled: cfg.skills?.enabled !== false,
252
- totalCount: discovered.length,
253
- enabledCount: filtered.length,
254
- items: mapSkillsWithEnabled(discovered, cfg),
255
- });
256
- } catch (error) {
257
- logger.error('Failed to get skills config', error);
258
- const errorResponse = serializeError(error);
259
- return c.json(
260
- errorResponse,
261
- (errorResponse.error.status || 500) as 500,
262
- );
263
- }
264
- },
265
- );
266
-
267
- openApiRoute(
268
- app,
269
- {
270
- method: 'put',
271
- path: '/v1/config/skills',
272
- tags: ['config'],
273
- operationId: 'updateSkillsConfig',
274
- summary: 'Update skills enable/disable config',
275
- parameters: [
276
- {
277
- in: 'query',
278
- name: 'project',
279
- required: false,
280
- schema: {
281
- type: 'string',
282
- },
283
- description:
284
- 'Project root override (defaults to current working directory).',
285
- },
286
- ],
287
- requestBody: {
288
- required: true,
289
- content: {
290
- 'application/json': {
291
- schema: {
292
- type: 'object',
293
- properties: {
294
- enabled: {
295
- type: 'boolean',
296
- },
297
- items: {
298
- type: 'object',
299
- additionalProperties: {
300
- type: 'object',
301
- properties: {
302
- enabled: {
303
- type: 'boolean',
304
- },
305
- },
306
- },
307
- },
308
- },
309
- },
310
- },
311
- },
312
- },
313
- responses: {
314
- '200': {
315
- description: 'OK',
316
- content: {
317
- 'application/json': {
318
- schema: {
319
- type: 'object',
320
- properties: {
321
- success: {
322
- type: 'boolean',
323
- },
324
- enabled: {
325
- type: 'boolean',
326
- },
327
- totalCount: {
328
- type: 'number',
329
- },
330
- enabledCount: {
331
- type: 'number',
332
- },
333
- items: {
334
- type: 'array',
335
- items: {
336
- type: 'object',
337
- properties: {
338
- name: {
339
- type: 'string',
340
- },
341
- description: {
342
- type: 'string',
343
- },
344
- scope: {
345
- type: 'string',
346
- },
347
- path: {
348
- type: 'string',
349
- },
350
- enabled: {
351
- type: 'boolean',
352
- },
353
- },
354
- required: [
355
- 'name',
356
- 'description',
357
- 'scope',
358
- 'path',
359
- 'enabled',
360
- ],
361
- },
362
- },
363
- },
364
- required: [
365
- 'success',
366
- 'enabled',
367
- 'totalCount',
368
- 'enabledCount',
369
- 'items',
370
- ],
371
- },
372
- },
373
- },
374
- },
375
- '500': {
376
- description: 'Bad Request',
377
- content: {
378
- 'application/json': {
379
- schema: {
380
- type: 'object',
381
- properties: {
382
- error: {
383
- type: 'string',
384
- },
385
- },
386
- required: ['error'],
387
- },
388
- },
389
- },
390
- },
391
- },
392
- },
393
- async (c) => {
394
- try {
395
- const projectRoot = c.req.query('project') || process.cwd();
396
- const body = await c.req.json<{
397
- enabled?: boolean;
398
- items?: Record<string, { enabled?: boolean }>;
399
- }>();
400
- await writeSkillSettings(
401
- 'global',
402
- {
403
- ...(body.enabled !== undefined ? { enabled: body.enabled } : {}),
404
- ...(body.items ? { items: body.items } : {}),
405
- },
406
- projectRoot,
407
- );
408
- const cfg = await loadConfig(projectRoot);
409
- const repoRoot = (await findGitRoot(projectRoot)) ?? projectRoot;
410
- const discovered = sortSkillsByName(
411
- dedupeSkillsByName(await discoverSkills(projectRoot, repoRoot)),
412
- );
413
- const filtered = sortSkillsByName(
414
- filterDiscoveredSkills(discovered, cfg.skills),
415
- );
416
- return c.json({
417
- success: true,
418
- enabled: cfg.skills?.enabled !== false,
419
- totalCount: discovered.length,
420
- enabledCount: filtered.length,
421
- items: mapSkillsWithEnabled(discovered, cfg),
422
- });
423
- } catch (error) {
424
- logger.error('Failed to update skills config', error);
425
- const errorResponse = serializeError(error);
426
- return c.json(
427
- errorResponse,
428
- (errorResponse.error.status || 500) as 500,
429
- );
430
- }
431
- },
432
- );
433
-
434
- openApiRoute(
435
- app,
436
- {
437
- method: 'get',
438
- path: '/v1/skills/{name}',
439
- tags: ['config'],
440
- operationId: 'getSkill',
441
- summary: 'Get a skill by name',
442
- parameters: [
443
- {
444
- in: 'path',
445
- name: 'name',
446
- required: true,
447
- schema: {
448
- type: 'string',
449
- },
450
- },
451
- {
452
- in: 'query',
453
- name: 'project',
454
- required: false,
455
- schema: {
456
- type: 'string',
457
- },
458
- description:
459
- 'Project root override (defaults to current working directory).',
460
- },
461
- ],
462
- responses: {
463
- '200': {
464
- description: 'OK',
465
- content: {
466
- 'application/json': {
467
- schema: {
468
- type: 'object',
469
- properties: {
470
- name: {
471
- type: 'string',
472
- },
473
- description: {
474
- type: 'string',
475
- },
476
- license: {
477
- type: 'string',
478
- nullable: true,
479
- },
480
- compatibility: {
481
- type: 'string',
482
- nullable: true,
483
- },
484
- metadata: {
485
- type: 'object',
486
- nullable: true,
487
- },
488
- allowedTools: {
489
- type: 'array',
490
- items: {
491
- type: 'string',
492
- },
493
- nullable: true,
494
- },
495
- path: {
496
- type: 'string',
497
- },
498
- scope: {
499
- type: 'string',
500
- },
501
- content: {
502
- type: 'string',
503
- },
504
- },
505
- required: ['name', 'description', 'path', 'scope', 'content'],
506
- },
507
- },
508
- },
509
- },
510
- '404': {
511
- description: 'Bad Request',
512
- content: {
513
- 'application/json': {
514
- schema: {
515
- type: 'object',
516
- properties: {
517
- error: {
518
- type: 'string',
519
- },
520
- },
521
- required: ['error'],
522
- },
523
- },
524
- },
525
- },
526
- '500': {
527
- description: 'Bad Request',
528
- content: {
529
- 'application/json': {
530
- schema: {
531
- type: 'object',
532
- properties: {
533
- error: {
534
- type: 'string',
535
- },
536
- },
537
- required: ['error'],
538
- },
539
- },
540
- },
541
- },
542
- },
543
- },
544
- async (c) => {
545
- try {
546
- const name = c.req.param('name');
547
- const projectRoot = c.req.query('project') || process.cwd();
548
- const repoRoot = (await findGitRoot(projectRoot)) ?? projectRoot;
549
- await discoverSkills(projectRoot, repoRoot);
550
-
551
- const skill = await loadSkill(name);
552
- if (!skill) {
553
- return c.json({ error: `Skill '${name}' not found` }, 404);
554
- }
555
-
556
- return c.json({
557
- name: skill.metadata.name,
558
- description: skill.metadata.description,
559
- license: skill.metadata.license ?? null,
560
- compatibility: skill.metadata.compatibility ?? null,
561
- metadata: skill.metadata.metadata ?? null,
562
- allowedTools: skill.metadata.allowedTools ?? null,
563
- path: skill.path,
564
- scope: skill.scope,
565
- content: skill.content,
566
- });
567
- } catch (error) {
568
- logger.error('Failed to load skill', error);
569
- const errorResponse = serializeError(error);
570
- return c.json(
571
- errorResponse,
572
- (errorResponse.error.status || 500) as 500,
573
- );
574
- }
575
- },
576
- );
577
-
578
- openApiRoute(
579
- app,
580
- {
581
- method: 'get',
582
- path: '/v1/skills/{name}/files',
583
- tags: ['config'],
584
- operationId: 'listSkillFiles',
585
- summary: 'List files in a skill directory',
586
- parameters: [
587
- {
588
- in: 'path',
589
- name: 'name',
590
- required: true,
591
- schema: {
592
- type: 'string',
593
- },
594
- },
595
- {
596
- in: 'query',
597
- name: 'project',
598
- required: false,
599
- schema: {
600
- type: 'string',
601
- },
602
- description:
603
- 'Project root override (defaults to current working directory).',
604
- },
605
- ],
606
- responses: {
607
- '200': {
608
- description: 'OK',
609
- content: {
610
- 'application/json': {
611
- schema: {
612
- type: 'object',
613
- properties: {
614
- files: {
615
- type: 'array',
616
- items: {
617
- type: 'object',
618
- properties: {
619
- relativePath: {
620
- type: 'string',
621
- },
622
- size: {
623
- type: 'number',
624
- },
625
- },
626
- required: ['relativePath', 'size'],
627
- },
628
- },
629
- },
630
- required: ['files'],
631
- },
632
- },
633
- },
634
- },
635
- '500': {
636
- description: 'Bad Request',
637
- content: {
638
- 'application/json': {
639
- schema: {
640
- type: 'object',
641
- properties: {
642
- error: {
643
- type: 'string',
644
- },
645
- },
646
- required: ['error'],
647
- },
648
- },
649
- },
650
- },
651
- },
652
- },
653
- async (c) => {
654
- try {
655
- const name = c.req.param('name');
656
- const projectRoot = c.req.query('project') || process.cwd();
657
- const repoRoot = (await findGitRoot(projectRoot)) ?? projectRoot;
658
- await discoverSkills(projectRoot, repoRoot);
659
-
660
- const files = await discoverSkillFiles(name);
661
- return c.json({ files });
662
- } catch (error) {
663
- logger.error('Failed to list skill files', error);
664
- const errorResponse = serializeError(error);
665
- return c.json(
666
- errorResponse,
667
- (errorResponse.error.status || 500) as 500,
668
- );
669
- }
670
- },
671
- );
672
-
673
- openApiRoute(
674
- app,
675
- {
676
- method: 'get',
677
- path: '/v1/skills/{name}/files/{filePath}',
678
- tags: ['config'],
679
- operationId: 'getSkillFile',
680
- summary: 'Read a specific file from a skill directory',
681
- parameters: [
682
- {
683
- in: 'path',
684
- name: 'name',
685
- required: true,
686
- schema: {
687
- type: 'string',
688
- },
689
- },
690
- {
691
- in: 'path',
692
- name: 'filePath',
693
- required: true,
694
- schema: {
695
- type: 'string',
696
- },
697
- },
698
- {
699
- in: 'query',
700
- name: 'project',
701
- required: false,
702
- schema: {
703
- type: 'string',
704
- },
705
- description:
706
- 'Project root override (defaults to current working directory).',
707
- },
708
- ],
709
- responses: {
710
- '200': {
711
- description: 'OK',
712
- content: {
713
- 'application/json': {
714
- schema: {
715
- type: 'object',
716
- properties: {
717
- content: {
718
- type: 'string',
719
- },
720
- path: {
721
- type: 'string',
722
- },
723
- },
724
- required: ['content', 'path'],
725
- },
726
- },
727
- },
728
- },
729
- '404': {
730
- description: 'Bad Request',
731
- content: {
732
- 'application/json': {
733
- schema: {
734
- type: 'object',
735
- properties: {
736
- error: {
737
- type: 'string',
738
- },
739
- },
740
- required: ['error'],
741
- },
742
- },
743
- },
744
- },
745
- '500': {
746
- description: 'Bad Request',
747
- content: {
748
- 'application/json': {
749
- schema: {
750
- type: 'object',
751
- properties: {
752
- error: {
753
- type: 'string',
754
- },
755
- },
756
- required: ['error'],
757
- },
758
- },
759
- },
760
- },
761
- },
762
- },
763
- async (c) => {
764
- try {
765
- const name = c.req.param('name');
766
- const filePath = c.req.path.replace(`/v1/skills/${name}/files/`, '');
767
- const projectRoot = c.req.query('project') || process.cwd();
768
- const repoRoot = (await findGitRoot(projectRoot)) ?? projectRoot;
769
- await discoverSkills(projectRoot, repoRoot);
770
-
771
- const result = await loadSkillFile(name, filePath);
772
- if (!result) {
773
- return c.json(
774
- { error: `File '${filePath}' not found in skill '${name}'` },
775
- 404,
776
- );
777
- }
778
- return c.json({ content: result.content, path: result.resolvedPath });
779
- } catch (error) {
780
- logger.error('Failed to load skill file', error);
781
- const errorResponse = serializeError(error);
782
- return c.json(
783
- errorResponse,
784
- (errorResponse.error.status || 500) as 500,
785
- );
786
- }
787
- },
788
- );
789
-
790
- openApiRoute(
791
- app,
792
- {
793
- method: 'post',
794
- path: '/v1/skills/validate',
795
- tags: ['config'],
796
- operationId: 'validateSkill',
797
- summary: 'Validate a SKILL.md content',
798
- requestBody: {
799
- required: true,
800
- content: {
801
- 'application/json': {
802
- schema: {
803
- type: 'object',
804
- properties: {
805
- content: {
806
- type: 'string',
807
- },
808
- path: {
809
- type: 'string',
810
- },
811
- },
812
- required: ['content'],
813
- },
814
- },
815
- },
816
- },
817
- responses: {
818
- '200': {
819
- description: 'OK',
820
- content: {
821
- 'application/json': {
822
- schema: {
823
- type: 'object',
824
- properties: {
825
- valid: {
826
- type: 'boolean',
827
- },
828
- name: {
829
- type: 'string',
830
- },
831
- description: {
832
- type: 'string',
833
- },
834
- license: {
835
- type: 'string',
836
- nullable: true,
837
- },
838
- error: {
839
- type: 'string',
840
- },
841
- },
842
- required: ['valid'],
843
- },
844
- },
845
- },
846
- },
847
- },
848
- },
849
- async (c) => {
850
- try {
851
- const body = await c.req.json<{ content: string; path?: string }>();
852
- if (!body.content) {
853
- return c.json({ error: 'content is required' }, 400);
854
- }
855
-
856
- const skillPath = body.path ?? 'SKILL.md';
857
- const skill = parseSkillFile(body.content, skillPath, 'cwd');
858
- return c.json({
859
- valid: true,
860
- name: skill.metadata.name,
861
- description: skill.metadata.description,
862
- license: skill.metadata.license ?? null,
863
- });
864
- } catch (error) {
865
- return c.json({
866
- valid: false,
867
- error: (error as Error).message,
868
- });
869
- }
870
- },
871
- );
872
-
873
- openApiRoute(
874
- app,
875
- {
876
- method: 'get',
877
- path: '/v1/skills/validate-name/{name}',
878
- tags: ['config'],
879
- operationId: 'validateSkillName',
880
- summary: 'Check if a skill name is valid',
881
- parameters: [
882
- {
883
- in: 'path',
884
- name: 'name',
885
- required: true,
886
- schema: {
887
- type: 'string',
888
- },
889
- },
890
- ],
891
- responses: {
892
- '200': {
893
- description: 'OK',
894
- content: {
895
- 'application/json': {
896
- schema: {
897
- type: 'object',
898
- properties: {
899
- valid: {
900
- type: 'boolean',
901
- },
902
- },
903
- required: ['valid'],
904
- },
905
- },
906
- },
907
- },
908
- },
909
- },
910
- async (c) => {
911
- const name = c.req.param('name');
912
- return c.json({ valid: validateSkillName(name) });
913
- },
914
- );
29
+ openApiRoute(app, routeSpec(listSkillsSpec), listSkills);
30
+ openApiRoute(app, routeSpec(getSkillsConfigSpec), getSkillsConfig);
31
+ openApiRoute(app, routeSpec(updateSkillsConfigSpec), updateSkillsConfig);
32
+ openApiRoute(app, routeSpec(getSkillSpec), getSkill);
33
+ openApiRoute(app, routeSpec(listSkillFilesSpec), listSkillFiles);
34
+ openApiRoute(app, routeSpec(getSkillFileSpec), getSkillFile);
35
+ openApiRoute(app, routeSpec(validateSkillSpec), validateSkill);
36
+ openApiRoute(app, routeSpec(validateSkillNameSpec), validateSkillNameRoute);
915
37
  }