libretto 0.4.4 → 0.5.0

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 (152) hide show
  1. package/dist/cli/cli.js +20 -19
  2. package/dist/cli/commands/ai.js +1 -1
  3. package/dist/cli/commands/browser.js +3 -3
  4. package/dist/cli/commands/execution.js +3 -3
  5. package/dist/cli/commands/logs.js +1 -1
  6. package/dist/cli/core/browser.js +11 -6
  7. package/dist/cli/core/context.js +4 -18
  8. package/dist/cli/core/session.js +2 -2
  9. package/dist/cli/core/snapshot-analyzer.js +2 -2
  10. package/dist/cli/router.js +1 -1
  11. package/dist/cli/workers/run-integration-runtime.js +2 -2
  12. package/dist/shared/paths/paths.js +2 -1
  13. package/dist/shared/paths/repo-root.d.ts +3 -0
  14. package/dist/shared/paths/repo-root.js +24 -0
  15. package/package.json +6 -7
  16. package/scripts/postinstall.mjs +12 -3
  17. package/skills/libretto/SKILL.md +93 -404
  18. package/skills/libretto/references/auth-profiles.md +30 -0
  19. package/skills/libretto/references/pages-and-page-targeting.md +29 -0
  20. package/skills/libretto/references/reverse-engineering-network-requests.md +39 -0
  21. package/skills/libretto/references/user-action-log.md +31 -0
  22. package/src/cli/cli.ts +173 -0
  23. package/src/cli/commands/ai.ts +35 -0
  24. package/src/cli/commands/browser.ts +165 -0
  25. package/src/cli/commands/execution.ts +691 -0
  26. package/src/cli/commands/init.ts +327 -0
  27. package/src/cli/commands/logs.ts +128 -0
  28. package/src/cli/commands/shared.ts +70 -0
  29. package/src/cli/commands/snapshot.ts +327 -0
  30. package/src/cli/core/ai-config.ts +255 -0
  31. package/src/cli/core/api-snapshot-analyzer.ts +97 -0
  32. package/src/cli/core/browser.ts +839 -0
  33. package/src/cli/core/context.ts +122 -0
  34. package/src/cli/core/pause-signals.ts +35 -0
  35. package/src/cli/core/session-telemetry.ts +553 -0
  36. package/src/cli/core/session.ts +209 -0
  37. package/src/cli/core/snapshot-analyzer.ts +875 -0
  38. package/src/cli/core/snapshot-api-config.ts +236 -0
  39. package/src/cli/core/telemetry.ts +446 -0
  40. package/src/cli/framework/simple-cli.ts +1273 -0
  41. package/src/cli/index.ts +13 -0
  42. package/src/cli/router.ts +28 -0
  43. package/src/cli/workers/run-integration-runtime.ts +311 -0
  44. package/src/cli/workers/run-integration-worker-protocol.ts +14 -0
  45. package/src/cli/workers/run-integration-worker.ts +75 -0
  46. package/src/index.ts +120 -0
  47. package/src/runtime/download/download.ts +100 -0
  48. package/src/runtime/download/index.ts +7 -0
  49. package/src/runtime/extract/extract.ts +92 -0
  50. package/src/runtime/extract/index.ts +1 -0
  51. package/src/runtime/network/index.ts +5 -0
  52. package/src/runtime/network/network.ts +113 -0
  53. package/src/runtime/recovery/agent.ts +256 -0
  54. package/src/runtime/recovery/errors.ts +152 -0
  55. package/src/runtime/recovery/index.ts +7 -0
  56. package/src/runtime/recovery/recovery.ts +50 -0
  57. package/{dist/shared/condense-dom/condense-dom.cjs → src/shared/condense-dom/condense-dom.ts} +243 -115
  58. package/src/shared/config/config.ts +22 -0
  59. package/src/shared/config/index.ts +5 -0
  60. package/src/shared/debug/index.ts +1 -0
  61. package/src/shared/debug/pause.ts +85 -0
  62. package/src/shared/instrumentation/errors.ts +82 -0
  63. package/src/shared/instrumentation/index.ts +9 -0
  64. package/src/shared/instrumentation/instrument.ts +276 -0
  65. package/src/shared/llm/ai-sdk-adapter.ts +78 -0
  66. package/src/shared/llm/client.ts +217 -0
  67. package/src/shared/llm/index.ts +3 -0
  68. package/src/shared/llm/types.ts +63 -0
  69. package/src/shared/logger/index.ts +6 -0
  70. package/src/shared/logger/logger.ts +352 -0
  71. package/src/shared/logger/sinks.ts +144 -0
  72. package/src/shared/paths/paths.ts +109 -0
  73. package/src/shared/paths/repo-root.ts +27 -0
  74. package/src/shared/run/api.ts +2 -0
  75. package/src/shared/run/browser.ts +98 -0
  76. package/src/shared/state/index.ts +11 -0
  77. package/src/shared/state/session-state.ts +74 -0
  78. package/src/shared/visualization/ghost-cursor.ts +200 -0
  79. package/src/shared/visualization/highlight.ts +146 -0
  80. package/src/shared/visualization/index.ts +18 -0
  81. package/src/shared/workflow/workflow.ts +42 -0
  82. package/dist/index.cjs +0 -144
  83. package/dist/index.d.cts +0 -21
  84. package/dist/runtime/download/download.cjs +0 -70
  85. package/dist/runtime/download/download.d.cts +0 -35
  86. package/dist/runtime/download/index.cjs +0 -30
  87. package/dist/runtime/download/index.d.cts +0 -3
  88. package/dist/runtime/extract/extract.cjs +0 -88
  89. package/dist/runtime/extract/extract.d.cts +0 -23
  90. package/dist/runtime/extract/index.cjs +0 -28
  91. package/dist/runtime/extract/index.d.cts +0 -5
  92. package/dist/runtime/network/index.cjs +0 -28
  93. package/dist/runtime/network/index.d.cts +0 -4
  94. package/dist/runtime/network/network.cjs +0 -91
  95. package/dist/runtime/network/network.d.cts +0 -28
  96. package/dist/runtime/recovery/agent.cjs +0 -223
  97. package/dist/runtime/recovery/agent.d.cts +0 -13
  98. package/dist/runtime/recovery/errors.cjs +0 -124
  99. package/dist/runtime/recovery/errors.d.cts +0 -31
  100. package/dist/runtime/recovery/index.cjs +0 -34
  101. package/dist/runtime/recovery/index.d.cts +0 -7
  102. package/dist/runtime/recovery/recovery.cjs +0 -55
  103. package/dist/runtime/recovery/recovery.d.cts +0 -12
  104. package/dist/shared/condense-dom/condense-dom.d.cts +0 -34
  105. package/dist/shared/config/config.cjs +0 -44
  106. package/dist/shared/config/config.d.cts +0 -10
  107. package/dist/shared/config/index.cjs +0 -32
  108. package/dist/shared/config/index.d.cts +0 -1
  109. package/dist/shared/debug/index.cjs +0 -28
  110. package/dist/shared/debug/index.d.cts +0 -1
  111. package/dist/shared/debug/pause.cjs +0 -86
  112. package/dist/shared/debug/pause.d.cts +0 -12
  113. package/dist/shared/instrumentation/errors.cjs +0 -81
  114. package/dist/shared/instrumentation/errors.d.cts +0 -12
  115. package/dist/shared/instrumentation/index.cjs +0 -35
  116. package/dist/shared/instrumentation/index.d.cts +0 -6
  117. package/dist/shared/instrumentation/instrument.cjs +0 -206
  118. package/dist/shared/instrumentation/instrument.d.cts +0 -32
  119. package/dist/shared/llm/ai-sdk-adapter.cjs +0 -71
  120. package/dist/shared/llm/ai-sdk-adapter.d.cts +0 -22
  121. package/dist/shared/llm/client.cjs +0 -218
  122. package/dist/shared/llm/client.d.cts +0 -13
  123. package/dist/shared/llm/index.cjs +0 -31
  124. package/dist/shared/llm/index.d.cts +0 -5
  125. package/dist/shared/llm/types.cjs +0 -16
  126. package/dist/shared/llm/types.d.cts +0 -67
  127. package/dist/shared/logger/index.cjs +0 -37
  128. package/dist/shared/logger/index.d.cts +0 -2
  129. package/dist/shared/logger/logger.cjs +0 -232
  130. package/dist/shared/logger/logger.d.cts +0 -86
  131. package/dist/shared/logger/sinks.cjs +0 -160
  132. package/dist/shared/logger/sinks.d.cts +0 -9
  133. package/dist/shared/paths/paths.cjs +0 -104
  134. package/dist/shared/paths/paths.d.cts +0 -10
  135. package/dist/shared/run/api.cjs +0 -28
  136. package/dist/shared/run/api.d.cts +0 -2
  137. package/dist/shared/run/browser.cjs +0 -98
  138. package/dist/shared/run/browser.d.cts +0 -22
  139. package/dist/shared/state/index.cjs +0 -38
  140. package/dist/shared/state/index.d.cts +0 -2
  141. package/dist/shared/state/session-state.cjs +0 -92
  142. package/dist/shared/state/session-state.d.cts +0 -40
  143. package/dist/shared/visualization/ghost-cursor.cjs +0 -174
  144. package/dist/shared/visualization/ghost-cursor.d.cts +0 -37
  145. package/dist/shared/visualization/highlight.cjs +0 -134
  146. package/dist/shared/visualization/highlight.d.cts +0 -22
  147. package/dist/shared/visualization/index.cjs +0 -45
  148. package/dist/shared/visualization/index.d.cts +0 -3
  149. package/dist/shared/workflow/workflow.cjs +0 -47
  150. package/dist/shared/workflow/workflow.d.cts +0 -21
  151. package/skills/libretto/code-generation-rules.md +0 -223
  152. package/skills/libretto/integration-approach-selection.md +0 -174
package/dist/cli/cli.js CHANGED
@@ -16,30 +16,30 @@ Options:
16
16
  Built-in sessions: default, dev-server, browser-agent
17
17
 
18
18
  Examples:
19
- libretto-cli open https://linkedin.com
19
+ libretto open https://linkedin.com
20
20
 
21
21
  # ... manually log in ...
22
- libretto-cli save linkedin.com
22
+ libretto save linkedin.com
23
23
  # Next time you open linkedin.com, you'll be logged in automatically
24
24
 
25
- libretto-cli exec "await page.locator('button:has-text(\\"Sign in\\")').click()"
26
- libretto-cli exec "await page.fill('input[name=\\"email\\"]', 'test@example.com')"
27
- libretto-cli ai configure openai
28
- libretto-cli ai configure anthropic
29
- libretto-cli ai configure gemini
30
- libretto-cli ai configure vertex
31
- libretto-cli ai configure openai/gpt-4o
32
- libretto-cli snapshot
33
- libretto-cli snapshot --objective "Find the submit button" --context "Submitting a referral form, already filled in patient details"
34
- libretto-cli resume --session default
35
- libretto-cli close
36
- libretto-cli close --all
37
- libretto-cli close --all --force
25
+ libretto exec "await page.locator('button:has-text(\\"Sign in\\")').click()"
26
+ libretto exec "await page.fill('input[name=\\"email\\"]', 'test@example.com')"
27
+ libretto ai configure openai
28
+ libretto ai configure anthropic
29
+ libretto ai configure gemini
30
+ libretto ai configure vertex
31
+ libretto ai configure openai/gpt-4o
32
+ libretto snapshot
33
+ libretto snapshot --objective "Find the submit button" --context "Submitting a referral form, already filled in patient details"
34
+ libretto resume --session default
35
+ libretto close
36
+ libretto close --all
37
+ libretto close --all --force
38
38
 
39
39
  # Multiple sessions
40
- libretto-cli open https://site1.com --session test1
41
- libretto-cli open https://site2.com --session test2
42
- libretto-cli exec "return await page.title()" --session test1
40
+ libretto open https://site1.com --session test1
41
+ libretto open https://site2.com --session test2
42
+ libretto exec "return await page.title()" --session test1
43
43
 
44
44
  Available in exec:
45
45
  page, context, state, browser, networkLog, actionLog
@@ -93,7 +93,8 @@ function validateLegacySessionArg(rawArgs) {
93
93
  if (value === void 0) return;
94
94
  if (value === null) {
95
95
  throw new Error(
96
- "Usage: libretto-cli <command> [--session <name>]\nMissing or invalid --session value."
96
+ `Usage: libretto <command> [--session <name>]
97
+ Missing or invalid --session value.`
97
98
  );
98
99
  }
99
100
  validateSessionName(value);
@@ -23,7 +23,7 @@ const aiCommands = SimpleCLI.group({
23
23
  preset: input.preset
24
24
  },
25
25
  {
26
- configureCommandName: "libretto-cli ai configure"
26
+ configureCommandName: `libretto ai configure`
27
27
  }
28
28
  );
29
29
  })
@@ -47,7 +47,7 @@ const openInput = SimpleCLI.input({
47
47
  }
48
48
  }).refine(
49
49
  (input) => Boolean(input.url),
50
- "Usage: libretto-cli open <url> [--headless] [--viewport WxH] [--session <name>]"
50
+ `Usage: libretto open <url> [--headless] [--viewport WxH] [--session <name>]`
51
51
  ).refine(
52
52
  (input) => !(input.headed && input.headless),
53
53
  "Cannot pass both --headed and --headless."
@@ -73,7 +73,7 @@ const saveInput = SimpleCLI.input({
73
73
  }
74
74
  }).refine(
75
75
  (input) => Boolean(input.urlOrDomain),
76
- "Usage: libretto-cli save <url|domain> [--session <name>]"
76
+ `Usage: libretto save <url|domain> [--session <name>]`
77
77
  );
78
78
  function createSaveCommand(logger) {
79
79
  return SimpleCLI.command({
@@ -108,7 +108,7 @@ function createCloseCommand(logger) {
108
108
  description: "Close the browser"
109
109
  }).input(closeInput).use(resolveSessionMiddleware).handle(async ({ input, ctx }) => {
110
110
  if (input.force && !input.all) {
111
- throw new Error("Usage: libretto-cli close --all [--force]");
111
+ throw new Error(`Usage: libretto close --all [--force]`);
112
112
  }
113
113
  if (input.all) {
114
114
  await runCloseAllWithLogger(logger, { force: input.force });
@@ -292,7 +292,7 @@ async function runResume(session, logger, sessionState) {
292
292
  } = getPauseSignalPaths(session);
293
293
  if (!existsSync(pausedSignalPath)) {
294
294
  throw new Error(
295
- `Session "${session}" is not paused. Run "libretto-cli run ... --session ${session}" and call pause("${session}") first.`
295
+ `Session "${session}" is not paused. Run "libretto run ... --session ${session}" and call pause("${session}") first.`
296
296
  );
297
297
  }
298
298
  if (!isProcessRunning(sessionState.pid)) {
@@ -414,7 +414,7 @@ const execInput = SimpleCLI.input({
414
414
  }
415
415
  }).refine(
416
416
  (input) => input.codeParts.length > 0,
417
- "Usage: libretto-cli exec <code> [--session <name>] [--visualize]"
417
+ `Usage: libretto exec <code> [--session <name>] [--visualize]`
418
418
  );
419
419
  function createExecCommand(logger) {
420
420
  return SimpleCLI.command({
@@ -429,7 +429,7 @@ function createExecCommand(logger) {
429
429
  );
430
430
  });
431
431
  }
432
- const runUsage = "Usage: libretto-cli run <integrationFile> <integrationExport> [--params <json> | --params-file <path>] [--tsconfig <path>] [--headed|--headless]";
432
+ const runUsage = `Usage: libretto run <integrationFile> <integrationExport> [--params <json> | --params-file <path>] [--tsconfig <path>] [--headed|--headless]`;
433
433
  const runInput = SimpleCLI.input({
434
434
  positionals: [
435
435
  SimpleCLI.positional("integrationFile", z.string().optional(), {
@@ -26,7 +26,7 @@ async function resolvePageId(session, pageId) {
26
26
  const foundPage = pages.find((page) => page.id === pageId);
27
27
  if (!foundPage) {
28
28
  throw new Error(
29
- `Page "${pageId}" was not found in session "${session}". Run "libretto-cli pages --session ${session}" to list ids.`
29
+ `Page "${pageId}" was not found in session "${session}". Run "libretto pages --session ${session}" to list ids.`
30
30
  );
31
31
  }
32
32
  return pageId;
@@ -142,9 +142,14 @@ async function connect(session, logger, timeoutMs = 1e4, options) {
142
142
  port: state.port,
143
143
  pid: state.pid
144
144
  });
145
- clearSessionState(session, logger);
145
+ if (!isPidRunning(state.pid)) {
146
+ clearSessionState(session, logger);
147
+ throw new Error(
148
+ `No browser running for session "${session}". Run 'libretto open <url> --session ${session}' first.`
149
+ );
150
+ }
146
151
  throw new Error(
147
- `No browser running for session "${session}". Run 'libretto-cli open <url> --session ${session}' first.`
152
+ `Could not connect to the browser for session "${session}" at http://127.0.0.1:${state.port}, but the session process (pid ${state.pid}) is still running. Try the command again, or close and reopen the session if it stays stuck.`
148
153
  );
149
154
  }
150
155
  const contexts = browser.contexts();
@@ -170,14 +175,14 @@ async function connect(session, logger, timeoutMs = 1e4, options) {
170
175
  }
171
176
  if (options?.requireSinglePage && !options.pageId && pages.length > 1) {
172
177
  throw new Error(
173
- `Multiple pages are open in session "${session}". Pass --page <id> to target a page (run "libretto-cli pages --session ${session}" to list ids).`
178
+ `Multiple pages are open in session "${session}". Pass --page <id> to target a page (run "libretto pages --session ${session}" to list ids).`
174
179
  );
175
180
  }
176
181
  const pageRefs = await resolvePageReferences(pages);
177
182
  const pageRef = options?.pageId ? pageRefs.find((ref) => ref.id === options.pageId) ?? null : pageRefs[pageRefs.length - 1];
178
183
  if (!pageRef) {
179
184
  throw new Error(
180
- `Page "${options?.pageId}" was not found in session "${session}". Run "libretto-cli pages --session ${session}" to list ids.`
185
+ `Page "${options?.pageId}" was not found in session "${session}". Run "libretto pages --session ${session}" to list ids.`
181
186
  );
182
187
  }
183
188
  const page = pageRef.page;
@@ -293,7 +298,7 @@ function childLog(level, event, data = {}) {
293
298
  timestamp: new Date().toISOString(),
294
299
  id: Math.random().toString(36).slice(2, 10),
295
300
  level,
296
- scope: 'libretto-cli.child',
301
+ scope: 'libretto.child',
297
302
  event,
298
303
  data,
299
304
  });
@@ -606,7 +611,7 @@ async function runCloseAll(logger, options) {
606
611
  [
607
612
  `Failed to close ${survivors.length} session(s) gracefully: ${formatSessionList(survivors)}.`,
608
613
  `Closed ${closed} session(s).`,
609
- "Retry with: libretto-cli close --all --force"
614
+ `Retry with: libretto close --all --force`
610
615
  ].join("\n")
611
616
  );
612
617
  }
@@ -1,23 +1,9 @@
1
1
  import { Logger, createFileLogSink } from "../../shared/logger/index.js";
2
- import { spawnSync } from "node:child_process";
3
- import { cwd } from "node:process";
4
2
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
5
- import { join, resolve } from "node:path";
3
+ import { join } from "node:path";
4
+ import { resolveLibrettoRepoRoot } from "../../shared/paths/repo-root.js";
6
5
  import { validateSessionName } from "./session.js";
7
- function getRepoRoot() {
8
- const override = process.env.LIBRETTO_REPO_ROOT?.trim();
9
- if (override) {
10
- return resolve(override);
11
- }
12
- const result = spawnSync("git", ["rev-parse", "--show-toplevel"], {
13
- encoding: "utf-8"
14
- });
15
- if (result.status === 0 && result.stdout) {
16
- return result.stdout.trim();
17
- }
18
- return cwd();
19
- }
20
- const REPO_ROOT = getRepoRoot();
6
+ const REPO_ROOT = resolveLibrettoRepoRoot();
21
7
  const LIBRETTO_CONFIG_DIR = join(REPO_ROOT, ".libretto");
22
8
  const LIBRETTO_CONFIG_PATH = join(LIBRETTO_CONFIG_DIR, "config.json");
23
9
  const PROFILES_DIR = join(LIBRETTO_CONFIG_DIR, "profiles");
@@ -63,7 +49,7 @@ function createLoggerForSession(session) {
63
49
  const sessionDir = getSessionDir(session);
64
50
  mkdirSync(sessionDir, { recursive: true });
65
51
  const logFilePath = getSessionLogsPath(session);
66
- return new Logger(["libretto-cli"], [createFileLogSink({ filePath: logFilePath })]);
52
+ return new Logger(["libretto"], [createFileLogSink({ filePath: logFilePath })]);
67
53
  }
68
54
  async function closeLogger(logger) {
69
55
  if (!logger) return;
@@ -93,7 +93,7 @@ function throwSessionNotFoundError(session) {
93
93
  }
94
94
  lines.push("");
95
95
  lines.push("Start one with:");
96
- lines.push(` libretto-cli open <url> --session ${session}`);
96
+ lines.push(` libretto open <url> --session ${session}`);
97
97
  throw new Error(lines.join("\n"));
98
98
  }
99
99
  function assertSessionStateExistsOrThrow(session) {
@@ -161,7 +161,7 @@ function assertSessionAvailableForStart(session, logger) {
161
161
  }
162
162
  const endpoint = `http://127.0.0.1:${existingState.port}`;
163
163
  throw new Error(
164
- `Session "${session}" is already open and connected to ${endpoint} (pid ${existingState.pid}). Create a new session or close the current one with: libretto-cli close --session ${session}`
164
+ `Session "${session}" is already open and connected to ${endpoint} (pid ${existingState.pid}). Create a new session or close the current one with: libretto close --session ${session}`
165
165
  );
166
166
  }
167
167
  export {
@@ -76,7 +76,7 @@ ${stripAnsi(result.stderr).trim() || stripAnsi(result.stdout).trim() || "No erro
76
76
  }
77
77
  class CodexUserCodingAgent extends UserCodingAgent {
78
78
  async analyzeSnapshot(prompt, pngPath, logger) {
79
- const tempDir = mkdtempSync(join(tmpdir(), "libretto-cli-analyzer-"));
79
+ const tempDir = mkdtempSync(join(tmpdir(), "libretto-analyzer-"));
80
80
  const outputPath = join(
81
81
  tempDir,
82
82
  `snapshot-analyzer-${Date.now()}-${Math.random().toString(36).slice(2)}.json`
@@ -171,7 +171,7 @@ async function runExternalCommand(command, args, logger, stdinText) {
171
171
  if (error.code === "ENOENT") {
172
172
  reject(
173
173
  new Error(
174
- `Command not found: ${command}. Configure AI with 'libretto-cli ai configure'.`
174
+ `Command not found: ${command}. Configure AI with 'libretto ai configure'.`
175
175
  )
176
176
  );
177
177
  return;
@@ -17,7 +17,7 @@ function buildCLIRoutes(logger) {
17
17
  };
18
18
  }
19
19
  function createCLIApp(logger) {
20
- return SimpleCLI.define("libretto-cli", buildCLIRoutes(logger), {
20
+ return SimpleCLI.define("libretto", buildCLIRoutes(logger), {
21
21
  globalNamed: {
22
22
  session: sessionOption()
23
23
  }
@@ -77,9 +77,9 @@ function getMissingLocalAuthProfileError(args) {
77
77
  `Local auth profile not found for domain "${normalizedDomain}".`,
78
78
  `Expected profile file: ${args.profilePath}`,
79
79
  "To create it:",
80
- ` 1. libretto-cli open https://${normalizedDomain} --headed --session ${args.session}`,
80
+ ` 1. libretto open https://${normalizedDomain} --headed --session ${args.session}`,
81
81
  " 2. Log in manually in the browser window.",
82
- ` 3. libretto-cli save ${normalizedDomain} --session ${args.session}`
82
+ ` 3. libretto save ${normalizedDomain} --session ${args.session}`
83
83
  ].join("\n");
84
84
  }
85
85
  function getAbsoluteIntegrationPath(integrationPath) {
@@ -1,5 +1,6 @@
1
1
  import { mkdirSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
+ import { resolveLibrettoRepoRoot } from "./repo-root.js";
3
4
  const LIBRETTO_DIRNAME = ".libretto";
4
5
  const LIBRETTO_SESSIONS_DIRNAME = "sessions";
5
6
  const SESSION_STATE_FILENAME = "state.json";
@@ -8,7 +9,7 @@ const RUNNER_LOG_FILENAME = "logs.jsonl";
8
9
  const PAUSED_SIGNAL_SUFFIX = "paused";
9
10
  const RESUME_SIGNAL_SUFFIX = "resume";
10
11
  function getLibrettoRoot(cwd = process.cwd()) {
11
- return join(cwd, LIBRETTO_DIRNAME);
12
+ return join(resolveLibrettoRepoRoot(cwd), LIBRETTO_DIRNAME);
12
13
  }
13
14
  function getLibrettoSessionsDir(cwd = process.cwd()) {
14
15
  return join(getLibrettoRoot(cwd), LIBRETTO_SESSIONS_DIRNAME);
@@ -0,0 +1,3 @@
1
+ declare function resolveLibrettoRepoRoot(cwd?: string): string;
2
+
3
+ export { resolveLibrettoRepoRoot };
@@ -0,0 +1,24 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { resolve } from "node:path";
3
+ const repoRootCache = /* @__PURE__ */ new Map();
4
+ function resolveLibrettoRepoRoot(cwd = process.cwd()) {
5
+ const override = process.env.LIBRETTO_REPO_ROOT?.trim();
6
+ if (override) {
7
+ return resolve(override);
8
+ }
9
+ const normalizedCwd = resolve(cwd);
10
+ const cached = repoRootCache.get(normalizedCwd);
11
+ if (cached) {
12
+ return cached;
13
+ }
14
+ const result = spawnSync("git", ["rev-parse", "--show-toplevel"], {
15
+ cwd: normalizedCwd,
16
+ encoding: "utf-8"
17
+ });
18
+ const repoRoot = result.status === 0 && result.stdout ? result.stdout.trim() : normalizedCwd;
19
+ repoRootCache.set(normalizedCwd, repoRoot);
20
+ return repoRoot;
21
+ }
22
+ export {
23
+ resolveLibrettoRepoRoot
24
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "libretto",
3
- "version": "0.4.4",
3
+ "version": "0.5.0",
4
4
  "description": "AI-powered browser automation library and CLI built on Playwright",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -14,25 +14,24 @@
14
14
  },
15
15
  "files": [
16
16
  "dist",
17
+ "src",
17
18
  "scripts",
18
19
  "skills/libretto"
19
20
  ],
21
+ "types": "./dist/index.d.ts",
20
22
  "bin": {
21
- "libretto": "./dist/cli/index.js",
22
- "libretto-cli": "./dist/cli/index.js"
23
+ "libretto": "./dist/cli/index.js"
23
24
  },
24
25
  "exports": {
25
26
  ".": {
26
27
  "types": "./dist/index.d.ts",
27
28
  "import": "./dist/index.js",
28
- "require": "./dist/index.cjs"
29
+ "default": "./dist/index.js"
29
30
  }
30
31
  },
31
32
  "scripts": {
32
33
  "postinstall": "node scripts/postinstall.mjs",
33
- "build": "pnpm run build:runtime && pnpm run build:cli",
34
- "build:runtime": "tsup --config tsup.config.ts",
35
- "build:cli": "tsup --config tsup.cli.config.ts",
34
+ "build": "tsup --config tsup.config.ts",
36
35
  "type-check": "tsc --noEmit",
37
36
  "test": "pnpm run build && vitest run",
38
37
  "eval": "pnpm run build && vitest run --config vitest.evals.config.ts",
@@ -14,15 +14,24 @@ spawnSync("npx", ["playwright", "install", "chromium"], {
14
14
  shell: true,
15
15
  });
16
16
 
17
- // Find git repo root
17
+ const installCwd = process.env.INIT_CWD?.trim() || null;
18
+ if (!installCwd) {
19
+ console.warn(
20
+ "libretto: automatic skill install failed because INIT_CWD is not set. Run `npx skills add saffron-health/libretto` to add the skills manually.",
21
+ );
22
+ process.exit(0);
23
+ }
24
+
25
+ // Resolve the consuming project's repo root from the original install cwd,
26
+ // not pnpm's content-addressable store path.
18
27
  const gitResult = spawnSync("git", ["rev-parse", "--show-toplevel"], {
28
+ cwd: installCwd,
19
29
  encoding: "utf-8",
20
30
  stdio: ["pipe", "pipe", "pipe"],
21
31
  });
22
32
  const repoRoot = gitResult.status === 0 && gitResult.stdout
23
33
  ? gitResult.stdout.trim()
24
- : null;
25
- if (!repoRoot) process.exit(0);
34
+ : installCwd;
26
35
 
27
36
  // Sync skills to any agent dirs at repo root
28
37
  const sourceDir = join(packageRoot, "skills", "libretto");