libretto 0.5.0 → 0.5.2

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 (122) hide show
  1. package/README.md +109 -35
  2. package/dist/cli/cli.js +22 -97
  3. package/dist/cli/commands/browser.js +86 -59
  4. package/dist/cli/commands/execution.js +199 -86
  5. package/dist/cli/commands/init.js +34 -29
  6. package/dist/cli/commands/logs.js +4 -5
  7. package/dist/cli/commands/shared.js +30 -29
  8. package/dist/cli/commands/snapshot.js +26 -39
  9. package/dist/cli/core/ai-config.js +21 -4
  10. package/dist/cli/core/api-snapshot-analyzer.js +15 -5
  11. package/dist/cli/core/browser.js +207 -37
  12. package/dist/cli/core/context.js +4 -1
  13. package/dist/cli/core/session-telemetry.js +434 -174
  14. package/dist/cli/core/session.js +21 -8
  15. package/dist/cli/core/snapshot-analyzer.js +14 -31
  16. package/dist/cli/core/snapshot-api-config.js +2 -6
  17. package/dist/cli/core/telemetry.js +20 -4
  18. package/dist/cli/framework/simple-cli.js +45 -25
  19. package/dist/cli/router.js +14 -21
  20. package/dist/cli/workers/run-integration-runtime.js +24 -5
  21. package/dist/cli/workers/run-integration-worker-protocol.js +3 -1
  22. package/dist/cli/workers/run-integration-worker.js +1 -4
  23. package/dist/index.d.ts +1 -2
  24. package/dist/index.js +7 -10
  25. package/dist/runtime/download/download.js +5 -1
  26. package/dist/runtime/extract/extract.js +11 -2
  27. package/dist/runtime/network/network.js +8 -1
  28. package/dist/runtime/recovery/agent.js +6 -2
  29. package/dist/runtime/recovery/errors.js +3 -1
  30. package/dist/runtime/recovery/recovery.js +3 -1
  31. package/dist/shared/condense-dom/condense-dom.js +17 -69
  32. package/dist/shared/config/config.d.ts +1 -9
  33. package/dist/shared/config/config.js +0 -18
  34. package/dist/shared/config/index.d.ts +2 -1
  35. package/dist/shared/config/index.js +0 -10
  36. package/dist/shared/debug/pause.js +9 -3
  37. package/dist/shared/dom-semantics.d.ts +8 -0
  38. package/dist/shared/dom-semantics.js +69 -0
  39. package/dist/shared/instrumentation/instrument.js +101 -5
  40. package/dist/shared/llm/ai-sdk-adapter.js +3 -1
  41. package/dist/shared/llm/client.js +3 -1
  42. package/dist/shared/logger/index.js +4 -1
  43. package/dist/shared/run/api.js +3 -1
  44. package/dist/shared/run/browser.js +47 -3
  45. package/dist/shared/state/session-state.d.ts +2 -1
  46. package/dist/shared/state/session-state.js +5 -2
  47. package/dist/shared/visualization/ghost-cursor.js +36 -14
  48. package/dist/shared/visualization/highlight.js +9 -6
  49. package/dist/shared/workflow/workflow.d.ts +4 -5
  50. package/dist/shared/workflow/workflow.js +3 -5
  51. package/package.json +6 -2
  52. package/scripts/check-skills-sync.mjs +25 -0
  53. package/scripts/compare-eval-summary.mjs +47 -0
  54. package/scripts/postinstall.mjs +15 -15
  55. package/scripts/prepare-release.sh +97 -0
  56. package/scripts/skills-libretto.mjs +103 -0
  57. package/scripts/summarize-evals.mjs +135 -0
  58. package/scripts/sync-skills.mjs +12 -0
  59. package/skills/libretto/SKILL.md +132 -54
  60. package/skills/libretto/references/action-logs.md +101 -0
  61. package/skills/libretto/references/auth-profiles.md +1 -2
  62. package/skills/libretto/references/code-generation-rules.md +210 -0
  63. package/skills/libretto/references/configuration-file-reference.md +53 -0
  64. package/skills/libretto/references/pages-and-page-targeting.md +1 -1
  65. package/skills/libretto/references/site-security-review.md +143 -0
  66. package/src/cli/cli.ts +23 -110
  67. package/src/cli/commands/browser.ts +94 -70
  68. package/src/cli/commands/execution.ts +233 -102
  69. package/src/cli/commands/init.ts +37 -33
  70. package/src/cli/commands/logs.ts +7 -7
  71. package/src/cli/commands/shared.ts +36 -37
  72. package/src/cli/commands/snapshot.ts +44 -59
  73. package/src/cli/core/ai-config.ts +24 -4
  74. package/src/cli/core/api-snapshot-analyzer.ts +17 -6
  75. package/src/cli/core/browser.ts +260 -49
  76. package/src/cli/core/context.ts +7 -2
  77. package/src/cli/core/session-telemetry.ts +449 -197
  78. package/src/cli/core/session.ts +21 -7
  79. package/src/cli/core/snapshot-analyzer.ts +26 -46
  80. package/src/cli/core/snapshot-api-config.ts +170 -175
  81. package/src/cli/core/telemetry.ts +39 -4
  82. package/src/cli/framework/simple-cli.ts +144 -77
  83. package/src/cli/router.ts +13 -21
  84. package/src/cli/workers/run-integration-runtime.ts +36 -9
  85. package/src/cli/workers/run-integration-worker-protocol.ts +2 -0
  86. package/src/cli/workers/run-integration-worker.ts +1 -4
  87. package/src/index.ts +73 -66
  88. package/src/runtime/download/download.ts +62 -58
  89. package/src/runtime/download/index.ts +5 -5
  90. package/src/runtime/extract/extract.ts +71 -61
  91. package/src/runtime/network/index.ts +3 -3
  92. package/src/runtime/network/network.ts +99 -93
  93. package/src/runtime/recovery/agent.ts +217 -212
  94. package/src/runtime/recovery/errors.ts +107 -104
  95. package/src/runtime/recovery/index.ts +3 -3
  96. package/src/runtime/recovery/recovery.ts +38 -35
  97. package/src/shared/condense-dom/condense-dom.ts +27 -82
  98. package/src/shared/config/config.ts +0 -19
  99. package/src/shared/config/index.ts +0 -5
  100. package/src/shared/debug/pause.ts +57 -51
  101. package/src/shared/dom-semantics.ts +68 -0
  102. package/src/shared/instrumentation/errors.ts +64 -62
  103. package/src/shared/instrumentation/index.ts +5 -5
  104. package/src/shared/instrumentation/instrument.ts +339 -209
  105. package/src/shared/llm/ai-sdk-adapter.ts +58 -55
  106. package/src/shared/llm/client.ts +181 -174
  107. package/src/shared/llm/types.ts +39 -39
  108. package/src/shared/logger/index.ts +11 -4
  109. package/src/shared/logger/logger.ts +312 -306
  110. package/src/shared/logger/sinks.ts +118 -114
  111. package/src/shared/paths/paths.ts +50 -49
  112. package/src/shared/paths/repo-root.ts +17 -17
  113. package/src/shared/run/api.ts +5 -1
  114. package/src/shared/run/browser.ts +65 -3
  115. package/src/shared/state/index.ts +9 -9
  116. package/src/shared/state/session-state.ts +46 -43
  117. package/src/shared/visualization/ghost-cursor.ts +180 -149
  118. package/src/shared/visualization/highlight.ts +89 -86
  119. package/src/shared/visualization/index.ts +13 -13
  120. package/src/shared/workflow/workflow.ts +19 -25
  121. package/skills/libretto/references/reverse-engineering-network-requests.md +0 -39
  122. package/skills/libretto/references/user-action-log.md +0 -31
@@ -1,4 +1,9 @@
1
- import { appendFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
1
+ import {
2
+ appendFileSync,
3
+ existsSync,
4
+ readFileSync,
5
+ writeFileSync,
6
+ } from "node:fs";
2
7
  import type { Page } from "playwright";
3
8
  import {
4
9
  getSessionActionsLogPath,
@@ -21,7 +26,12 @@ export type NetworkLogEntry = {
21
26
 
22
27
  export function readNetworkLog(
23
28
  session: string,
24
- opts: { last?: number; filter?: string; method?: string; pageId?: string } = {},
29
+ opts: {
30
+ last?: number;
31
+ filter?: string;
32
+ method?: string;
33
+ pageId?: string;
34
+ } = {},
25
35
  ): NetworkLogEntry[] {
26
36
  assertSessionStateExistsOrThrow(session);
27
37
  const logPath = getSessionNetworkLogPath(session);
@@ -86,6 +96,15 @@ export type ActionLogEntry = {
86
96
  action: string;
87
97
  source: "user" | "agent";
88
98
  selector?: string;
99
+ bestSemanticSelector?: string;
100
+ targetSelector?: string;
101
+ ancestorSelectors?: string[];
102
+ nearbyText?: string;
103
+ composedPath?: string[];
104
+ coordinates?: {
105
+ x: number;
106
+ y: number;
107
+ };
89
108
  value?: string;
90
109
  url?: string;
91
110
  duration?: number;
@@ -99,7 +118,10 @@ export function parentLogAction(
99
118
  ): void {
100
119
  try {
101
120
  const record = { ts: new Date().toISOString(), ...entry };
102
- appendFileSync(getSessionActionsLogPath(session), JSON.stringify(record) + "\n");
121
+ appendFileSync(
122
+ getSessionActionsLogPath(session),
123
+ JSON.stringify(record) + "\n",
124
+ );
103
125
  } catch {}
104
126
  }
105
127
 
@@ -139,6 +161,11 @@ export function readActionLog(
139
161
  (e) =>
140
162
  re.test(e.action) ||
141
163
  re.test(e.selector || "") ||
164
+ re.test(e.bestSemanticSelector || "") ||
165
+ re.test(e.targetSelector || "") ||
166
+ re.test((e.ancestorSelectors || []).join(" ")) ||
167
+ re.test(e.nearbyText || "") ||
168
+ re.test((e.composedPath || []).join(" ")) ||
142
169
  re.test(e.value || "") ||
143
170
  re.test(e.url || ""),
144
171
  );
@@ -158,8 +185,16 @@ export function readActionLog(
158
185
  export function formatActionEntry(e: ActionLogEntry): string {
159
186
  const time = e.ts.replace(/.*T/, "").replace(/\.\d+Z$/, "");
160
187
  const src = e.source.toUpperCase().padEnd(5);
188
+ const displaySelector = e.bestSemanticSelector || e.selector;
161
189
  const parts = [`[${time}]`, `[${src}]`, e.action];
162
- if (e.selector) parts.push(e.selector);
190
+ if (displaySelector) parts.push(displaySelector);
191
+ if (e.targetSelector && e.targetSelector !== displaySelector) {
192
+ parts.push(`target=${e.targetSelector}`);
193
+ }
194
+ if (e.nearbyText) parts.push(`text="${e.nearbyText}"`);
195
+ if (e.coordinates) {
196
+ parts.push(`@(${e.coordinates.x},${e.coordinates.y})`);
197
+ }
163
198
  if (e.value) parts.push(`"${e.value}"`);
164
199
  if (e.url) parts.push(e.url);
165
200
  if (e.duration != null) parts.push(`${e.duration}ms`);
@@ -49,7 +49,10 @@ type SimpleCLIPositionalsDefinition = readonly SimpleCLIPositionalDefinition<
49
49
  ZodTypeAny
50
50
  >[];
51
51
 
52
- type SimpleCLINamedDefinition = Record<string, SimpleCLINamedArgDefinition<ZodTypeAny>>;
52
+ type SimpleCLINamedDefinition = Record<
53
+ string,
54
+ SimpleCLINamedArgDefinition<ZodTypeAny>
55
+ >;
53
56
 
54
57
  type SimpleCLIInputDefinition = {
55
58
  positionals: SimpleCLIPositionalsDefinition;
@@ -97,7 +100,8 @@ type NormalizedCommandDefinition<
97
100
 
98
101
  type SimpleCLIRouteTree<TContext extends SimpleCLIContext = {}> = Record<
99
102
  string,
100
- SimpleCLIGroup<TContext, any> | SimpleCLICommandBuilder<any, TContext, any, any>
103
+ | SimpleCLIGroup<TContext, any>
104
+ | SimpleCLICommandBuilder<any, TContext, any, any>
101
105
  >;
102
106
 
103
107
  export type SimpleCLIResolvedCommand = {
@@ -295,13 +299,23 @@ export class SimpleCLICommandBuilder<
295
299
  TResult,
296
300
  > {
297
301
  constructor(
298
- private readonly definition: NormalizedCommandDefinition<TInput, TContextIn, TContext, TResult>,
302
+ private readonly definition: NormalizedCommandDefinition<
303
+ TInput,
304
+ TContextIn,
305
+ TContext,
306
+ TResult
307
+ >,
299
308
  ) {}
300
309
 
301
310
  input<TNextInput>(
302
311
  input: SimpleCLIInput<TNextInput>,
303
312
  ): SimpleCLICommandBuilder<TNextInput, TContextIn, TContext, TResult> {
304
- return new SimpleCLICommandBuilder<TNextInput, TContextIn, TContext, TResult>({
313
+ return new SimpleCLICommandBuilder<
314
+ TNextInput,
315
+ TContextIn,
316
+ TContext,
317
+ TResult
318
+ >({
305
319
  config: this.definition.config,
306
320
  input,
307
321
  middlewares: this.definition.middlewares,
@@ -332,7 +346,12 @@ export class SimpleCLICommandBuilder<
332
346
  handle<TNextResult>(
333
347
  handler: SimpleCLIHandler<TInput, TContext, TNextResult>,
334
348
  ): SimpleCLICommandBuilder<TInput, TContextIn, TContext, TNextResult> {
335
- return new SimpleCLICommandBuilder<TInput, TContextIn, TContext, TNextResult>({
349
+ return new SimpleCLICommandBuilder<
350
+ TInput,
351
+ TContextIn,
352
+ TContext,
353
+ TNextResult
354
+ >({
336
355
  config: this.definition.config,
337
356
  input: this.definition.input,
338
357
  middlewares: this.definition.middlewares,
@@ -340,7 +359,12 @@ export class SimpleCLICommandBuilder<
340
359
  });
341
360
  }
342
361
 
343
- getDefinition(): NormalizedCommandDefinition<TInput, TContextIn, TContext, TResult> {
362
+ getDefinition(): NormalizedCommandDefinition<
363
+ TInput,
364
+ TContextIn,
365
+ TContext,
366
+ TResult
367
+ > {
344
368
  return this.definition;
345
369
  }
346
370
  }
@@ -358,7 +382,10 @@ export type SimpleCLIGroup<
358
382
  };
359
383
 
360
384
  export class SimpleCLIApp {
361
- private readonly resolvedCommands = new Map<string, InternalResolvedCommand>();
385
+ private readonly resolvedCommands = new Map<
386
+ string,
387
+ InternalResolvedCommand
388
+ >();
362
389
  private readonly resolvedGroups = new Map<string, InternalResolvedGroup>();
363
390
  private readonly routeEntries: InternalResolvedRouteEntry[];
364
391
  private readonly globalNamed: SimpleCLINamedDefinition;
@@ -485,7 +512,9 @@ export class SimpleCLIApp {
485
512
  return [];
486
513
  }
487
514
 
488
- const helpFlagIndex = argsBeforePassthrough.findIndex((arg) => isHelpFlag(arg));
515
+ const helpFlagIndex = argsBeforePassthrough.findIndex((arg) =>
516
+ isHelpFlag(arg),
517
+ );
489
518
  if (helpFlagIndex >= 0) {
490
519
  return argsBeforePassthrough.slice(0, helpFlagIndex);
491
520
  }
@@ -503,7 +532,10 @@ export class SimpleCLIApp {
503
532
  throw new Error(`Unknown command: ${args.join(" ")}`);
504
533
  }
505
534
 
506
- const rawInput = this.parseCommandInput(command, args.slice(command.path.length));
535
+ const rawInput = this.parseCommandInput(
536
+ command,
537
+ args.slice(command.path.length),
538
+ );
507
539
  return {
508
540
  routeKey: command.routeKey,
509
541
  rawInput,
@@ -517,7 +549,9 @@ export class SimpleCLIApp {
517
549
  const inputDefinition = command.input?.getDefinition();
518
550
  if (!inputDefinition) {
519
551
  if (args.length > 0) {
520
- throw new Error(`Unexpected arguments for ${this.name} ${command.path.join(" ")}.`);
552
+ throw new Error(
553
+ `Unexpected arguments for ${this.name} ${command.path.join(" ")}.`,
554
+ );
521
555
  }
522
556
  return {
523
557
  positionals: [],
@@ -537,7 +571,9 @@ export class SimpleCLIApp {
537
571
 
538
572
  if (arg === "--") {
539
573
  if (!passthroughEntry) {
540
- throw new Error(`Unexpected "--" for ${this.name} ${command.path.join(" ")}.`);
574
+ throw new Error(
575
+ `Unexpected "--" for ${this.name} ${command.path.join(" ")}.`,
576
+ );
541
577
  }
542
578
  named["--"] = args.slice(index + 1);
543
579
  break;
@@ -592,7 +628,11 @@ export class SimpleCLIApp {
592
628
  positionals.push(arg);
593
629
  }
594
630
 
595
- validateParsedPositionals(command, inputDefinition.positionals, positionals);
631
+ validateParsedPositionals(
632
+ command,
633
+ inputDefinition.positionals,
634
+ positionals,
635
+ );
596
636
  validateRequiredNamedArgs(inputDefinition.named, named);
597
637
 
598
638
  return {
@@ -685,7 +725,9 @@ export class SimpleCLIApp {
685
725
  return rawInput;
686
726
  }
687
727
 
688
- const inputDefinition = this.resolvedCommands.get(routeKey)?.input?.getDefinition();
728
+ const inputDefinition = this.resolvedCommands
729
+ .get(routeKey)
730
+ ?.input?.getDefinition();
689
731
  if (!inputDefinition) {
690
732
  return rawInput;
691
733
  }
@@ -756,8 +798,9 @@ export class SimpleCLIApp {
756
798
  lines.push(...argumentLines);
757
799
  }
758
800
 
759
- const optionLines = Object.entries(inputDefinition.named).map(([key, spec]) =>
760
- formatListEntry(buildNamedArgHelpLabel(key, spec), spec.help),
801
+ const optionLines = Object.entries(inputDefinition.named).map(
802
+ ([key, spec]) =>
803
+ formatListEntry(buildNamedArgHelpLabel(key, spec), spec.help),
761
804
  );
762
805
 
763
806
  if (optionLines.length > 0) {
@@ -819,7 +862,9 @@ export class SimpleCLIApp {
819
862
  return entries;
820
863
  }
821
864
 
822
- private findBestMatchingCommand(args: readonly string[]): InternalResolvedCommand | null {
865
+ private findBestMatchingCommand(
866
+ args: readonly string[],
867
+ ): InternalResolvedCommand | null {
823
868
  let bestMatch: InternalResolvedCommand | null = null;
824
869
 
825
870
  for (const command of this.resolvedCommands.values()) {
@@ -833,12 +878,16 @@ export class SimpleCLIApp {
833
878
  return bestMatch;
834
879
  }
835
880
 
836
- private findCommandByPath(path: readonly string[]): InternalResolvedCommand | null {
881
+ private findCommandByPath(
882
+ path: readonly string[],
883
+ ): InternalResolvedCommand | null {
837
884
  const routeKey = pathToRouteKey(path);
838
885
  return this.resolvedCommands.get(routeKey) ?? null;
839
886
  }
840
887
 
841
- private findGroupByPath(path: readonly string[]): InternalResolvedGroup | null {
888
+ private findGroupByPath(
889
+ path: readonly string[],
890
+ ): InternalResolvedGroup | null {
842
891
  const routeKey = pathToRouteKey(path);
843
892
  return this.resolvedGroups.get(routeKey) ?? null;
844
893
  }
@@ -847,10 +896,7 @@ export class SimpleCLIApp {
847
896
  function splitNamedArg(arg: string): [string, string | undefined] {
848
897
  const separatorIndex = arg.indexOf("=");
849
898
  if (separatorIndex < 0) return [arg, undefined];
850
- return [
851
- arg.slice(0, separatorIndex),
852
- arg.slice(separatorIndex + 1),
853
- ];
899
+ return [arg.slice(0, separatorIndex), arg.slice(separatorIndex + 1)];
854
900
  }
855
901
 
856
902
  function readNamedArgValue(
@@ -860,7 +906,10 @@ function readNamedArgValue(
860
906
  displayName: string,
861
907
  spec: SimpleCLINamedArgDefinition<ZodTypeAny>,
862
908
  inlineValue: string | undefined,
863
- namedSpecs: ReadonlyMap<string, { key: string; spec: SimpleCLINamedArgDefinition<ZodTypeAny> }>,
909
+ namedSpecs: ReadonlyMap<
910
+ string,
911
+ { key: string; spec: SimpleCLINamedArgDefinition<ZodTypeAny> }
912
+ >,
864
913
  ): unknown {
865
914
  if (spec.kind === "flag") {
866
915
  return inlineValue === undefined
@@ -874,9 +923,9 @@ function readNamedArgValue(
874
923
 
875
924
  const nextValue = args[index + 1];
876
925
  if (
877
- nextValue === undefined
878
- || nextValue === "--"
879
- || isRecognizedNamedArgToken(nextValue, namedSpecs)
926
+ nextValue === undefined ||
927
+ nextValue === "--" ||
928
+ isRecognizedNamedArgToken(nextValue, namedSpecs)
880
929
  ) {
881
930
  throw new Error(`Missing value for ${displayName}.`);
882
931
  }
@@ -886,7 +935,10 @@ function readNamedArgValue(
886
935
 
887
936
  function isRecognizedNamedArgToken(
888
937
  token: string,
889
- namedSpecs: ReadonlyMap<string, { key: string; spec: SimpleCLINamedArgDefinition<ZodTypeAny> }>,
938
+ namedSpecs: ReadonlyMap<
939
+ string,
940
+ { key: string; spec: SimpleCLINamedArgDefinition<ZodTypeAny> }
941
+ >,
890
942
  ): boolean {
891
943
  if (token === "-" || !token.startsWith("-")) {
892
944
  return false;
@@ -899,11 +951,13 @@ function isRecognizedNamedArgToken(
899
951
  return namedSpecs.has(rawName);
900
952
  }
901
953
 
902
- function buildNamedArgLookup(namedDefinition: SimpleCLINamedDefinition): Map<
903
- string,
904
- { key: string; spec: SimpleCLINamedArgDefinition<ZodTypeAny> }
905
- > {
906
- const lookup = new Map<string, { key: string; spec: SimpleCLINamedArgDefinition<ZodTypeAny> }>();
954
+ function buildNamedArgLookup(
955
+ namedDefinition: SimpleCLINamedDefinition,
956
+ ): Map<string, { key: string; spec: SimpleCLINamedArgDefinition<ZodTypeAny> }> {
957
+ const lookup = new Map<
958
+ string,
959
+ { key: string; spec: SimpleCLINamedArgDefinition<ZodTypeAny> }
960
+ >();
907
961
 
908
962
  for (const [key, spec] of Object.entries(namedDefinition)) {
909
963
  if (spec.source === "--") continue;
@@ -926,7 +980,9 @@ function validateParsedPositionals(
926
980
  definitions: SimpleCLIPositionalsDefinition,
927
981
  positionals: readonly string[],
928
982
  ): void {
929
- const variadicDefinition = definitions.find((definition) => definition.variadic);
983
+ const variadicDefinition = definitions.find(
984
+ (definition) => definition.variadic,
985
+ );
930
986
  if (!variadicDefinition && positionals.length > definitions.length) {
931
987
  throw new Error(`Unexpected arguments for ${command.path.join(" ")}.`);
932
988
  }
@@ -935,17 +991,22 @@ function validateParsedPositionals(
935
991
  const value = definition.variadic
936
992
  ? positionals.slice(index)
937
993
  : positionals[index];
938
- if (value !== undefined && (!Array.isArray(value) || value.length > 0)) return;
994
+ if (value !== undefined && (!Array.isArray(value) || value.length > 0))
995
+ return;
939
996
  if (schemaAcceptsUndefined(definition.schema)) return;
940
997
  throw new Error(`Missing required argument <${definition.key}>.`);
941
998
  });
942
999
  }
943
1000
 
944
1001
  function validateInputDefinition(definition: SimpleCLIInputDefinition): void {
945
- const variadicIndex = definition.positionals.findIndex((positional) => positional.variadic);
1002
+ const variadicIndex = definition.positionals.findIndex(
1003
+ (positional) => positional.variadic,
1004
+ );
946
1005
  if (variadicIndex < 0) return;
947
1006
  if (variadicIndex !== definition.positionals.length - 1) {
948
- throw new Error("Variadic positional arguments must be the last positional.");
1007
+ throw new Error(
1008
+ "Variadic positional arguments must be the last positional.",
1009
+ );
949
1010
  }
950
1011
  }
951
1012
 
@@ -955,7 +1016,8 @@ function validateRequiredNamedArgs(
955
1016
  ): void {
956
1017
  for (const [key, spec] of Object.entries(definitions)) {
957
1018
  if (schemaAcceptsUndefined(spec.schema)) continue;
958
- const flagName = spec.source === "--" ? "--" : buildNamedArgFlagName(key, spec);
1019
+ const flagName =
1020
+ spec.source === "--" ? "--" : buildNamedArgFlagName(key, spec);
959
1021
  if (Object.prototype.hasOwnProperty.call(named, flagName)) continue;
960
1022
  if (spec.source === "--") {
961
1023
  throw new Error(`Missing required passthrough arguments after --.`);
@@ -988,11 +1050,10 @@ function resolveRouteTree(
988
1050
  path: groupPath,
989
1051
  });
990
1052
 
991
- const nested = resolveRouteTree(
992
- routeValue.routes,
993
- groupPath,
994
- [...parentMiddlewares, ...routeValue.middlewares],
995
- );
1053
+ const nested = resolveRouteTree(routeValue.routes, groupPath, [
1054
+ ...parentMiddlewares,
1055
+ ...routeValue.middlewares,
1056
+ ]);
996
1057
  resolved.commands.push(...nested.commands);
997
1058
  resolved.groups.push(...nested.groups);
998
1059
  resolved.routeEntries.push(...nested.routeEntries);
@@ -1001,7 +1062,9 @@ function resolveRouteTree(
1001
1062
 
1002
1063
  const command = routeValue.getDefinition();
1003
1064
  if (!command.handler) {
1004
- throw new Error(`Command "${[...parentPath, token].join(" ")}" is missing a handler.`);
1065
+ throw new Error(
1066
+ `Command "${[...parentPath, token].join(" ")}" is missing a handler.`,
1067
+ );
1005
1068
  }
1006
1069
 
1007
1070
  const path = [...parentPath, token];
@@ -1010,7 +1073,10 @@ function resolveRouteTree(
1010
1073
  path,
1011
1074
  description: command.config.description,
1012
1075
  input: command.input,
1013
- middlewares: mergeInheritedMiddlewares(parentMiddlewares, command.middlewares),
1076
+ middlewares: mergeInheritedMiddlewares(
1077
+ parentMiddlewares,
1078
+ command.middlewares,
1079
+ ),
1014
1080
  handler: command.handler as unknown as SimpleCLIHandler<
1015
1081
  unknown,
1016
1082
  SimpleCLIContext,
@@ -1035,8 +1101,10 @@ function mergeInheritedMiddlewares(
1035
1101
  }
1036
1102
 
1037
1103
  if (
1038
- commandMiddlewares.length >= parentMiddlewares.length
1039
- && parentMiddlewares.every((middleware, index) => commandMiddlewares[index] === middleware)
1104
+ commandMiddlewares.length >= parentMiddlewares.length &&
1105
+ parentMiddlewares.every(
1106
+ (middleware, index) => commandMiddlewares[index] === middleware,
1107
+ )
1040
1108
  ) {
1041
1109
  return [...commandMiddlewares];
1042
1110
  }
@@ -1053,12 +1121,10 @@ function isGroup(
1053
1121
  function buildInputNormalizer<
1054
1122
  TPositionals extends SimpleCLIPositionalsDefinition,
1055
1123
  TNamed extends SimpleCLINamedDefinition,
1056
- >(
1057
- definition: {
1058
- positionals: TPositionals;
1059
- named: TNamed;
1060
- },
1061
- ): (raw: SimpleCLIInputRaw) => InputObjectFor<TPositionals, TNamed> {
1124
+ >(definition: {
1125
+ positionals: TPositionals;
1126
+ named: TNamed;
1127
+ }): (raw: SimpleCLIInputRaw) => InputObjectFor<TPositionals, TNamed> {
1062
1128
  return (raw) => {
1063
1129
  const output: RecordUnknown = {};
1064
1130
  const positionals = raw.positionals ?? [];
@@ -1077,10 +1143,7 @@ function buildInputNormalizer<
1077
1143
  spec.name ? toCamelCase(spec.name) : "",
1078
1144
  ...(spec.aliases ?? []).flatMap((alias) => {
1079
1145
  const normalizedAlias = normalizeNamedArgToken(alias);
1080
- return [
1081
- normalizedAlias,
1082
- toCamelCase(normalizedAlias),
1083
- ];
1146
+ return [normalizedAlias, toCamelCase(normalizedAlias)];
1084
1147
  }),
1085
1148
  toKebabCase(key),
1086
1149
  key,
@@ -1103,12 +1166,10 @@ function buildInputNormalizer<
1103
1166
  function buildInputSchema<
1104
1167
  TPositionals extends SimpleCLIPositionalsDefinition,
1105
1168
  TNamed extends SimpleCLINamedDefinition,
1106
- >(
1107
- definition: {
1108
- positionals: TPositionals;
1109
- named: TNamed;
1110
- },
1111
- ): z.ZodType<InputObjectFor<TPositionals, TNamed>, unknown> {
1169
+ >(definition: {
1170
+ positionals: TPositionals;
1171
+ named: TNamed;
1172
+ }): z.ZodType<InputObjectFor<TPositionals, TNamed>, unknown> {
1112
1173
  const shape: Record<string, ZodTypeAny> = {};
1113
1174
 
1114
1175
  for (const positional of definition.positionals) {
@@ -1140,7 +1201,12 @@ function positional<TKey extends string, TSchema extends ZodTypeAny>(
1140
1201
 
1141
1202
  function option<TSchema extends ZodTypeAny>(
1142
1203
  schema: TSchema,
1143
- options?: { help?: string; name?: string; aliases?: readonly string[]; source?: "--" },
1204
+ options?: {
1205
+ help?: string;
1206
+ name?: string;
1207
+ aliases?: readonly string[];
1208
+ source?: "--";
1209
+ },
1144
1210
  ): SimpleCLINamedArgDefinition<TSchema> {
1145
1211
  return {
1146
1212
  kind: "option",
@@ -1152,9 +1218,11 @@ function option<TSchema extends ZodTypeAny>(
1152
1218
  };
1153
1219
  }
1154
1220
 
1155
- function flag(
1156
- options?: { help?: string; name?: string; aliases?: readonly string[] },
1157
- ): SimpleCLINamedArgDefinition<z.ZodDefault<z.ZodBoolean>> {
1221
+ function flag(options?: {
1222
+ help?: string;
1223
+ name?: string;
1224
+ aliases?: readonly string[];
1225
+ }): SimpleCLINamedArgDefinition<z.ZodDefault<z.ZodBoolean>> {
1158
1226
  return {
1159
1227
  kind: "flag",
1160
1228
  schema: z.boolean().default(false),
@@ -1184,9 +1252,11 @@ type SimpleCLIScope<
1184
1252
  TContext extends SimpleCLIContext,
1185
1253
  > = {
1186
1254
  use<TContextOut extends SimpleCLIContext>(
1187
- middleware: SimpleCLIMiddleware<unknown, TContext, TContextOut>
1255
+ middleware: SimpleCLIMiddleware<unknown, TContext, TContextOut>,
1188
1256
  ): SimpleCLIScope<TParentContext, TContextOut>;
1189
- group(config: SimpleCLIGroupConfig<TContext>): SimpleCLIGroup<TParentContext, TContext>;
1257
+ group(
1258
+ config: SimpleCLIGroupConfig<TContext>,
1259
+ ): SimpleCLIGroup<TParentContext, TContext>;
1190
1260
  command(
1191
1261
  config: SimpleCLICommandConfig,
1192
1262
  ): SimpleCLICommandBuilder<unknown, TParentContext, TContext, unknown>;
@@ -1201,9 +1271,7 @@ function command(
1201
1271
  });
1202
1272
  }
1203
1273
 
1204
- function group(
1205
- config: SimpleCLIGroupConfig<{}>,
1206
- ): SimpleCLIGroup<{}, {}> {
1274
+ function group(config: SimpleCLIGroupConfig<{}>): SimpleCLIGroup<{}, {}> {
1207
1275
  return createScope<{}, {}>([]).group(config);
1208
1276
  }
1209
1277
 
@@ -1222,7 +1290,9 @@ function createScope<
1222
1290
  middleware,
1223
1291
  ]);
1224
1292
  },
1225
- group(config: SimpleCLIGroupConfig<TContext>): SimpleCLIGroup<TParentContext, TContext> {
1293
+ group(
1294
+ config: SimpleCLIGroupConfig<TContext>,
1295
+ ): SimpleCLIGroup<TParentContext, TContext> {
1226
1296
  return {
1227
1297
  kind: "group",
1228
1298
  description: config.description,
@@ -1255,11 +1325,8 @@ function define(
1255
1325
  return new SimpleCLIApp(name, routes, config);
1256
1326
  }
1257
1327
 
1258
- export type InferInput<TInput extends SimpleCLIInput<unknown>> = TInput extends SimpleCLIInput<
1259
- infer TOutput
1260
- >
1261
- ? TOutput
1262
- : never;
1328
+ export type InferInput<TInput extends SimpleCLIInput<unknown>> =
1329
+ TInput extends SimpleCLIInput<infer TOutput> ? TOutput : never;
1263
1330
 
1264
1331
  export const SimpleCLI = {
1265
1332
  define,
package/src/cli/router.ts CHANGED
@@ -1,28 +1,20 @@
1
- import type { Logger } from "../shared/logger/index.js";
2
1
  import { aiCommands } from "./commands/ai.js";
3
- import { createBrowserCommands } from "./commands/browser.js";
4
- import { createExecutionCommands } from "./commands/execution.js";
2
+ import { browserCommands } from "./commands/browser.js";
3
+ import { executionCommands } from "./commands/execution.js";
5
4
  import { initCommand } from "./commands/init.js";
6
5
  import { logCommands } from "./commands/logs.js";
7
- import { sessionOption } from "./commands/shared.js";
8
- import { createSnapshotCommand } from "./commands/snapshot.js";
6
+ import { snapshotCommand } from "./commands/snapshot.js";
9
7
  import { SimpleCLI } from "./framework/simple-cli.js";
10
8
 
11
- export function buildCLIRoutes(logger: Logger) {
12
- return {
13
- ...createBrowserCommands(logger),
14
- ...createExecutionCommands(logger),
15
- ...logCommands,
16
- ai: aiCommands,
17
- init: initCommand,
18
- snapshot: createSnapshotCommand(logger),
19
- };
20
- }
9
+ export const cliRoutes = {
10
+ ...browserCommands,
11
+ ...executionCommands,
12
+ ...logCommands,
13
+ ai: aiCommands,
14
+ init: initCommand,
15
+ snapshot: snapshotCommand,
16
+ };
21
17
 
22
- export function createCLIApp(logger: Logger) {
23
- return SimpleCLI.define("libretto", buildCLIRoutes(logger), {
24
- globalNamed: {
25
- session: sessionOption(),
26
- },
27
- });
18
+ export function createCLIApp() {
19
+ return SimpleCLI.define("libretto", cliRoutes);
28
20
  }