screenci 0.0.24 → 0.0.26

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.
package/dist/Dockerfile CHANGED
@@ -10,15 +10,23 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
10
10
  && rm -rf /var/lib/apt/lists/*
11
11
 
12
12
  # ── Dependency layer (cached until package.json changes) ──────────────────────
13
- # Install screenci as a workspace package so npm creates the bin link.
13
+ # Install through a workspace to keep dependency resolution unchanged, then
14
+ # replace the workspace symlink with a real package under node_modules. Playwright
15
+ # skips transforms for node_modules packages, but follows workspace symlinks to
16
+ # /app/screenci and can rewrite compiled ESM as CJS while loading TS configs.
14
17
  COPY package.json ./screenci/
15
- RUN printf '{"private":true,"workspaces":["screenci"]}' > package.json && npm install
18
+ RUN printf '{"private":true,"workspaces":["screenci"]}' > package.json && \
19
+ npm install && \
20
+ rm /app/node_modules/screenci && \
21
+ mkdir -p /app/node_modules/screenci
16
22
 
17
23
  # Playwright browser download: only re-runs when the playwright version changes.
18
24
  RUN npx playwright install chromium --with-deps
19
25
 
20
26
  # ── screenci build output ─────────────────────────────────────────────────────
21
27
  COPY dist ./screenci/dist/
28
+ COPY package.json ./node_modules/screenci/package.json
29
+ COPY dist ./node_modules/screenci/dist/
22
30
 
23
31
  # Explicit bin wrapper — no npm bin-linking magic needed.
24
32
  RUN printf '#!/bin/sh\nexec node /app/screenci/dist/cli.js "$@"\n' > /app/node_modules/.bin/screenci && \
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../cli.ts"],"names":[],"mappings":";AAwBA,OAAO,KAAK,EACV,uBAAuB,EACvB,aAAa,EAEd,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AA4D/C,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAiBD,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,GAAG,KAAK,GAAG,OAAO,CAAC,EACtD,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,GACxC,MAAM,IAAI,CAiBZ;AAuED,QAAA,MAAM,mCAAmC;;;CAG/B,CAAA;AAEV,KAAK,oBAAoB,GAAG,MAAM,OAAO,mCAAmC,CAAA;AAmc5E,wBAAgB,cAAc,CAC5B,KAAK,EAAE,QAAQ,GAAG,uBAAuB,GACxC,QAAQ,GAAG,uBAAuB,CAKpC;AAED,wBAAgB,oCAAoC,CAClD,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,mBAAmB,EAAE,GAC5B,aAAa,CAqEf;AAQD,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GACb,MAAM,CAeR;AAED,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GACb,IAAI,CAcN;AAgRD,wBAAgB,gBAAgB,IAAI,MAAM,CAKzC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAK1C;AA0FD,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,aAAa,GAAG,SAAS,GAClC,MAAM,GAAG,SAAS,CAepB;AAqjBD,wBAAsB,IAAI,kBAkPzB;AAoJD,wBAAgB,sBAAsB,CACpC,aAAa,CAAC,EAAE,oBAAoB,GACnC,oBAAoB,CAoBtB"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../cli.ts"],"names":[],"mappings":";AAwBA,OAAO,KAAK,EACV,uBAAuB,EACvB,aAAa,EAEd,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AA4D/C,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAiBD,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,GAAG,KAAK,GAAG,OAAO,CAAC,EACtD,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,GACxC,MAAM,IAAI,CAiBZ;AAuED,QAAA,MAAM,mCAAmC;;;CAG/B,CAAA;AAEV,KAAK,oBAAoB,GAAG,MAAM,OAAO,mCAAmC,CAAA;AAmc5E,wBAAgB,cAAc,CAC5B,KAAK,EAAE,QAAQ,GAAG,uBAAuB,GACxC,QAAQ,GAAG,uBAAuB,CAKpC;AAED,wBAAgB,oCAAoC,CAClD,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,mBAAmB,EAAE,GAC5B,aAAa,CAqEf;AAQD,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GACb,MAAM,CAeR;AAED,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GACb,IAAI,CAcN;AAgRD,wBAAgB,gBAAgB,IAAI,MAAM,CAKzC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAK1C;AA0FD,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,aAAa,GAAG,SAAS,GAClC,MAAM,GAAG,SAAS,CAepB;AAuvBD,wBAAsB,IAAI,kBAoQzB;AAoJD,wBAAgB,sBAAsB,CACpC,aAAa,CAAC,EAAE,oBAAoB,GACnC,oBAAoB,CAoBtB"}
package/dist/cli.js CHANGED
@@ -982,11 +982,10 @@ export default defineConfig({
982
982
  })
983
983
  `;
984
984
  }
985
- function generatePackageJson(projectName, includePlaywrightCli = false) {
985
+ function generatePackageJson(projectName, includePlaywrightCli = false, screenciDependency = 'latest') {
986
986
  const npmName = projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
987
987
  const devDependencies = {
988
988
  '@types/node': '^25.0.0',
989
- tsx: '^4.21.0',
990
989
  };
991
990
  if (includePlaywrightCli) {
992
991
  devDependencies['@playwright/cli'] = 'latest';
@@ -1002,7 +1001,7 @@ function generatePackageJson(projectName, includePlaywrightCli = false) {
1002
1001
  test: 'screenci test',
1003
1002
  },
1004
1003
  dependencies: {
1005
- screenci: 'latest',
1004
+ screenci: screenciDependency,
1006
1005
  },
1007
1006
  devDependencies,
1008
1007
  }, null, 2) + '\n');
@@ -1020,13 +1019,17 @@ Learn more: https://screenci.com/docs/intro/
1020
1019
 
1021
1020
  ## Quick start
1022
1021
 
1023
- 1. Run tests in UI mode:
1022
+ 1. Create your own videos in \`videos/*.video.ts\`, either manually or with an AI agent using your source code or a URL.
1024
1023
 
1025
- \`npx screenci test\` or \`npx screenci test --ui\`
1024
+ 2. Run videos locally to test the script working:
1026
1025
 
1027
- 2. Record and upload videos:
1026
+ \`npx screenci test\` or with UI mode: \`npx screenci test --ui\`
1027
+
1028
+ 3. Record videos:
1028
1029
 
1029
1030
  \`npx screenci record\`
1031
+
1032
+ 4. View results on screenci.com and optionally enable a public URL to embed the video on your site.
1030
1033
  `;
1031
1034
  }
1032
1035
  function generateDockerfile() {
@@ -1062,25 +1065,23 @@ jobs:
1062
1065
  SCREENCI_SECRET: \${{ secrets.SCREENCI_SECRET }}
1063
1066
  run: |
1064
1067
  if [ -z "$SCREENCI_SECRET" ]; then
1065
- echo "::error::SCREENCI_SECRET is not set. Add it under Settings → Secrets and variables → Actions."
1068
+ echo "::error::SCREENCI_SECRET is not set. Copy it from https://app.screenci.com/secrets and add it under Settings → Secrets and variables → Actions → Repository secrets."
1066
1069
  exit 1
1067
1070
  fi
1068
1071
 
1069
1072
  - uses: actions/checkout@v4
1070
1073
 
1071
- - name: Build Docker image
1072
- run: docker build -t screenci-project .
1074
+ - uses: actions/setup-node@v6
1075
+ with:
1076
+ node-version: latest
1077
+
1078
+ - name: Install dependencies
1079
+ run: npm install
1073
1080
 
1074
1081
  - name: Record
1075
1082
  env:
1076
1083
  SCREENCI_SECRET: \${{ secrets.SCREENCI_SECRET }}
1077
- run: |
1078
- docker run --rm \\
1079
- -e SCREENCI_SECRET \\
1080
- -e SCREENCI_IN_CONTAINER=true \\
1081
- -e SCREENCI_RECORD=true \\
1082
- screenci-project \\
1083
- npm run record
1084
+ run: npm run record
1084
1085
  `;
1085
1086
  }
1086
1087
  function openBrowser(url) {
@@ -1134,23 +1135,110 @@ async function performBrowserLogin(appUrl) {
1134
1135
  });
1135
1136
  }
1136
1137
  function generateExampleVideo() {
1137
- return `import { video } from 'screenci'
1138
+ return `import { createNarration, hide, video, voices } from 'screenci'
1139
+
1140
+ const narration = createNarration({
1141
+ voice: { name: voices.Sophie, style: 'Clear, friendly product walkthrough' },
1142
+ languages: {
1143
+ en: {
1144
+ cues: {
1145
+ intro:
1146
+ 'Here is how to find instructions for starting to create your own ScreenCI [pronounce: screen see eye] videos. [short pause] Start on the homepage, then open the documentation from the hero section.',
1147
+ docs: 'The documentation opens with the guide sidebar on the left. In the Guides group, choose AI-Supported Editing.',
1148
+ },
1149
+ },
1150
+ es: {
1151
+ cues: {
1152
+ intro:
1153
+ 'Aqui se muestra como encontrar instrucciones para empezar a crear tus propios videos de ScreenCI [pronounce: screen see eye]. [short pause] Comienza en la pagina principal y abre la documentacion desde la seccion principal.',
1154
+ docs: 'La documentacion se abre con la barra lateral de guias a la izquierda. En el grupo Guias, elige Edicion asistida por IA.',
1155
+ },
1156
+ },
1157
+ },
1158
+ })
1138
1159
 
1139
- video('Example video', async ({ page }) => {
1140
- await page.goto('https://example.com')
1141
- await page.waitForTimeout(3000)
1160
+ video('Navigate to AI editing documentation', async ({ page }) => {
1161
+ await hide(async () => {
1162
+ await page.goto('https://screenci.com/')
1163
+ await page.getByText('ScreenCI', { exact: true }).first().waitFor()
1164
+ })
1165
+
1166
+ await narration.intro
1167
+ await page.getByRole('link', { name: 'View Documentation' }).click()
1168
+
1169
+ await narration.docs
1170
+ await page
1171
+ .getByRole('link', { name: 'AI-Supported Editing', exact: true })
1172
+ .click()
1142
1173
  })
1143
1174
  `;
1144
1175
  }
1145
1176
  async function promptProjectName() {
1146
1177
  return input({ message: 'Project name:' });
1147
1178
  }
1179
+ async function promptInitGitRepository() {
1180
+ return confirm({
1181
+ message: 'Initialize a git repository? (Y/n)',
1182
+ default: true,
1183
+ });
1184
+ }
1185
+ async function promptInitDependencies() {
1186
+ return confirm({
1187
+ message: 'Install dependencies now, including Chromium for Playwright? (Y/n)',
1188
+ default: true,
1189
+ });
1190
+ }
1191
+ async function promptInitAiAuthoring() {
1192
+ return confirm({
1193
+ message: 'Do you want to write videos with an AI agent based on a URL and not just source code? If yes, playwright-cli will be also installed.',
1194
+ default: true,
1195
+ });
1196
+ }
1197
+ async function promptInitGithubActionCi() {
1198
+ return confirm({
1199
+ message: 'Do you want to add Github Action CI? (Y/n)',
1200
+ default: true,
1201
+ });
1202
+ }
1148
1203
  function getProjectDirName(name) {
1149
1204
  return name.toLowerCase().replace(/\s+/g, '-');
1150
1205
  }
1151
1206
  function getInitProjectRoot() {
1152
1207
  return process.env['SCREENCI_INIT_CWD'] ?? process.cwd();
1153
1208
  }
1209
+ function isSourceCliEntrypoint(entrypoint) {
1210
+ return (entrypoint?.endsWith('/cli.ts') === true ||
1211
+ entrypoint?.endsWith('\\cli.ts') === true);
1212
+ }
1213
+ function getDevScreenciPackageRoot() {
1214
+ const explicitRoot = process.env.SCREENCI_DEV_PACKAGE_ROOT?.trim();
1215
+ if (explicitRoot)
1216
+ return resolve(explicitRoot);
1217
+ if (!isSourceCliEntrypoint(process.argv[1]))
1218
+ return undefined;
1219
+ return dirname(fileURLToPath(import.meta.url));
1220
+ }
1221
+ function getLocalScreenciDependency(packageRoot, projectDir) {
1222
+ const relativePath = pathRelative(projectDir, packageRoot) || '.';
1223
+ const normalizedPath = relativePath.replace(/\\/g, '/');
1224
+ return `file:${normalizedPath.startsWith('.') ? normalizedPath : `./${normalizedPath}`}`;
1225
+ }
1226
+ async function buildLocalScreenciPackage(packageRoot) {
1227
+ logger.info(`Using local screenci package: ${packageRoot}`);
1228
+ logger.info("Running 'npm run build' for local screenci package...");
1229
+ await spawnInherited('npm', ['run', 'build'], packageRoot, 'screenci dev build');
1230
+ }
1231
+ async function installLocalScreenciPackage(projectDir, packageRoot) {
1232
+ const packageJsonPath = resolve(projectDir, 'package.json');
1233
+ const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));
1234
+ packageJson.dependencies = {
1235
+ ...packageJson.dependencies,
1236
+ screenci: getLocalScreenciDependency(packageRoot, projectDir),
1237
+ };
1238
+ await writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
1239
+ logger.info('Installing local screenci package into this project...');
1240
+ await spawnInherited('npm', ['install', '--install-links'], projectDir, 'screenci dev install');
1241
+ }
1154
1242
  function buildChildEnv() {
1155
1243
  const { PATH, HOME, USER, LOGNAME, TMPDIR, TEMP, TMP } = process.env;
1156
1244
  return {
@@ -1169,6 +1257,7 @@ function buildChildEnv() {
1169
1257
  SCREENCI_IN_CONTAINER: process.env.SCREENCI_IN_CONTAINER,
1170
1258
  SCREENCI_RECORD: process.env.SCREENCI_RECORD,
1171
1259
  SCREENCI_SIGNAL_LOGGING: process.env.SCREENCI_SIGNAL_LOGGING,
1260
+ SCREENCI_DEV_PACKAGE_ROOT: process.env.SCREENCI_DEV_PACKAGE_ROOT,
1172
1261
  };
1173
1262
  }
1174
1263
  function requireContainerRuntime() {
@@ -1220,7 +1309,8 @@ function checkNodeVersion() {
1220
1309
  process.exit(1);
1221
1310
  }
1222
1311
  }
1223
- async function runInit(projectNameArg, verbose = false) {
1312
+ async function runInit(projectNameArg, options) {
1313
+ const { verbose, git, install, yes, skill, ci } = options;
1224
1314
  checkNodeVersion();
1225
1315
  checkContainerRuntimeForInit();
1226
1316
  const initCwd = getInitProjectRoot();
@@ -1238,10 +1328,26 @@ async function runInit(projectNameArg, verbose = false) {
1238
1328
  logger.error(`Error: Directory "${dirName}" already exists`);
1239
1329
  process.exit(1);
1240
1330
  }
1241
- const shouldAddPlaywrightCli = await confirm({
1242
- message: 'Do you want to write videos with an AI agent based on a URL and not just source code? If yes, playwright-cli will be also installed.',
1243
- default: true,
1244
- });
1331
+ const shouldInitializeGitRepository = yes
1332
+ ? true
1333
+ : git
1334
+ ? true
1335
+ : await promptInitGitRepository();
1336
+ const shouldInstallDependencies = yes
1337
+ ? true
1338
+ : install
1339
+ ? true
1340
+ : await promptInitDependencies();
1341
+ const shouldAddPlaywrightCli = yes
1342
+ ? true
1343
+ : skill
1344
+ ? true
1345
+ : await promptInitAiAuthoring();
1346
+ const shouldAddGithubActionCi = yes
1347
+ ? true
1348
+ : ci
1349
+ ? true
1350
+ : await promptInitGithubActionCi();
1245
1351
  const skillsArgs = [
1246
1352
  '--yes',
1247
1353
  'skills',
@@ -1253,15 +1359,28 @@ async function runInit(projectNameArg, verbose = false) {
1253
1359
  '-y',
1254
1360
  ];
1255
1361
  const skillsCommand = `npx ${skillsArgs.join(' ')}`;
1362
+ const devScreenciPackageRoot = getDevScreenciPackageRoot();
1363
+ const screenciDependency = devScreenciPackageRoot
1364
+ ? getLocalScreenciDependency(devScreenciPackageRoot, projectDir)
1365
+ : 'latest';
1366
+ if (devScreenciPackageRoot) {
1367
+ await buildLocalScreenciPackage(devScreenciPackageRoot);
1368
+ }
1256
1369
  await mkdir(resolve(projectDir, 'videos'), { recursive: true });
1257
- await mkdir(resolve(projectDir, '.github', 'workflows'), { recursive: true });
1370
+ if (shouldAddGithubActionCi) {
1371
+ await mkdir(resolve(projectDir, '.github', 'workflows'), {
1372
+ recursive: true,
1373
+ });
1374
+ }
1258
1375
  await writeFile(resolve(projectDir, 'screenci.config.ts'), generateConfig(projectName));
1259
- await writeFile(resolve(projectDir, 'package.json'), generatePackageJson(dirName, shouldAddPlaywrightCli));
1376
+ await writeFile(resolve(projectDir, 'package.json'), generatePackageJson(dirName, shouldAddPlaywrightCli, screenciDependency));
1260
1377
  await writeFile(resolve(projectDir, 'README.md'), generateReadme(projectName));
1261
1378
  await writeFile(resolve(projectDir, 'Dockerfile'), generateDockerfile());
1262
1379
  await writeFile(resolve(projectDir, '.gitignore'), generateGitignore());
1263
1380
  await writeFile(resolve(projectDir, 'videos', 'example.video.ts'), generateExampleVideo());
1264
- await writeFile(resolve(projectDir, '.github', 'workflows', 'record.yml'), generateGithubAction());
1381
+ if (shouldAddGithubActionCi) {
1382
+ await writeFile(resolve(projectDir, '.github', 'workflows', 'record.yml'), generateGithubAction());
1383
+ }
1265
1384
  await writeFile(resolve(projectDir, '.env'), '');
1266
1385
  logger.info(`Initialized screenci project "${projectName}" in ${projectDir}/`);
1267
1386
  logger.info('Files created:');
@@ -1271,44 +1390,77 @@ async function runInit(projectNameArg, verbose = false) {
1271
1390
  logger.info(' Dockerfile');
1272
1391
  logger.info(' .gitignore');
1273
1392
  logger.info(' videos/example.video.ts');
1274
- logger.info(' .github/workflows/record.yml');
1393
+ if (shouldAddGithubActionCi) {
1394
+ logger.info(' .github/workflows/record.yml');
1395
+ }
1275
1396
  logger.info(' .env (empty placeholder)');
1276
1397
  logger.info('');
1277
- logger.info('screenci requires dependencies to be installed.');
1278
- if (verbose) {
1279
- logger.info(`Running '${skillsCommand}'...`);
1280
- await spawnInherited('npx', skillsArgs, projectDir, 'screenci init');
1281
- }
1282
- else {
1283
- const spinner = ora('Adding ScreenCI skills...').start();
1284
- try {
1285
- await spawnSilent('npx', skillsArgs, projectDir);
1286
- spinner.succeed('ScreenCI skills added');
1398
+ if (shouldInitializeGitRepository) {
1399
+ if (verbose) {
1400
+ logger.info("Running 'git init'...");
1401
+ await spawnInherited('git', ['init'], projectDir, 'screenci init');
1287
1402
  }
1288
- catch (err) {
1289
- spinner.fail('ScreenCI skills install failed');
1290
- throw err;
1403
+ else {
1404
+ const spinner = ora('Initializing git repository...').start();
1405
+ try {
1406
+ await spawnSilent('git', ['init'], projectDir);
1407
+ spinner.succeed('Git repository initialized');
1408
+ }
1409
+ catch (err) {
1410
+ spinner.fail('git init failed');
1411
+ throw err;
1412
+ }
1291
1413
  }
1292
1414
  }
1293
- if (verbose) {
1294
- logger.info("Running 'npm install'...");
1295
- await spawnInherited('npm', ['install'], projectDir, 'screenci init');
1296
- }
1297
- else {
1298
- const spinner = ora('Running npm install...').start();
1299
- try {
1300
- await spawnSilent('npm', ['install', '--prefix', projectDir]);
1301
- spinner.succeed('npm install complete');
1415
+ if (shouldInstallDependencies) {
1416
+ logger.info('screenci requires dependencies to be installed.');
1417
+ if (verbose) {
1418
+ logger.info(`Running '${skillsCommand}'...`);
1419
+ await spawnInherited('npx', skillsArgs, projectDir, 'screenci init');
1302
1420
  }
1303
- catch (err) {
1304
- spinner.fail('npm install failed');
1305
- throw err;
1421
+ else {
1422
+ const spinner = ora('Adding ScreenCI skills...').start();
1423
+ try {
1424
+ await spawnSilent('npx', skillsArgs, projectDir);
1425
+ spinner.succeed('ScreenCI skills added');
1426
+ }
1427
+ catch (err) {
1428
+ spinner.fail('ScreenCI skills install failed');
1429
+ throw err;
1430
+ }
1431
+ }
1432
+ if (verbose) {
1433
+ const installArgs = devScreenciPackageRoot
1434
+ ? ['install', '--install-links']
1435
+ : ['install'];
1436
+ logger.info(`Running 'npm ${installArgs.join(' ')}'...`);
1437
+ await spawnInherited('npm', installArgs, projectDir, 'screenci init');
1438
+ }
1439
+ else {
1440
+ const spinner = ora('Running npm install...').start();
1441
+ try {
1442
+ const installArgs = devScreenciPackageRoot
1443
+ ? ['install', '--install-links', '--prefix', projectDir]
1444
+ : ['install', '--prefix', projectDir];
1445
+ await spawnSilent('npm', installArgs);
1446
+ spinner.succeed('npm install complete');
1447
+ }
1448
+ catch (err) {
1449
+ spinner.fail('npm install failed');
1450
+ throw err;
1451
+ }
1306
1452
  }
1453
+ logger.info("Local development requires Chromium for Playwright, running 'npx playwright install chromium --with-deps'...");
1454
+ await spawnInherited('npx', ['playwright', 'install', 'chromium', '--with-deps'], projectDir, 'screenci init');
1455
+ logger.info(`${pc.green('✔')} Playwright installed successfully`);
1456
+ }
1457
+ else {
1458
+ logger.info('Dependencies were not installed automatically.');
1459
+ logger.info('Run these commands when you are ready:');
1460
+ logger.info(` ${skillsCommand}`);
1461
+ logger.info(' npm install');
1462
+ logger.info(' npx playwright install chromium --with-deps');
1307
1463
  }
1308
- logger.info('Local development requires Chromium for Playwright.');
1309
- logger.info("Running 'npx playwright install chromium --with-deps'...");
1310
- await spawnInherited('npx', ['playwright', 'install', 'chromium', '--with-deps'], projectDir, 'screenci init');
1311
- logger.info(`${pc.green('ok')} Playwright installed successfully`);
1312
1464
  const cliDir = dirname(fileURLToPath(import.meta.url));
1313
1465
  await buildRecordImages(requireContainerRuntime(), cliDir, resolve(projectDir, 'Dockerfile'), projectDir, verbose);
1314
1466
  logger.info('');
@@ -1428,6 +1580,9 @@ export async function main() {
1428
1580
  .action(async () => {
1429
1581
  const parsed = parseConfigCliArgs(getSubcommandArgv('test'));
1430
1582
  await run('test', parsed.otherArgs, parsed.configPath);
1583
+ if (process.env.SCREENCI_IN_CONTAINER === 'true')
1584
+ return;
1585
+ logger.info('Tests passed. Run `npx screenci record` to render the videos.');
1431
1586
  });
1432
1587
  program
1433
1588
  .command('info')
@@ -1463,13 +1618,25 @@ export async function main() {
1463
1618
  program
1464
1619
  .command('init [name]')
1465
1620
  .description('Initialize a new screenci project')
1621
+ .option('--git', 'initialize a git repository without prompting')
1622
+ .option('--install', 'install skills, dependencies, and Chromium without prompting')
1623
+ .option('--ci', 'add GitHub Action CI without prompting')
1624
+ .option('--skill', 'enable playwright-cli without prompting')
1625
+ .option('-y, --yes', 'answer yes to all init prompts')
1466
1626
  .option('-v, --verbose', 'verbose output')
1467
1627
  .action(async (name, options) => {
1468
1628
  if (name === 'auth') {
1469
1629
  await runInitAuth();
1470
1630
  }
1471
1631
  else {
1472
- await runInit(name, options['verbose'] ?? false);
1632
+ await runInit(name, {
1633
+ verbose: options['verbose'] ?? false,
1634
+ git: options['git'] ?? false,
1635
+ install: options['install'] ?? false,
1636
+ yes: options['yes'] ?? false,
1637
+ skill: options['skill'] ?? false,
1638
+ ci: options['ci'] ?? false,
1639
+ });
1473
1640
  }
1474
1641
  });
1475
1642
  try {
@@ -1834,6 +2001,12 @@ async function run(command, additionalArgs, customConfigPath) {
1834
2001
  logger.info(`Running ScreenCI ${mode} with npx...`);
1835
2002
  logger.info(`Using config: ${configPath}`);
1836
2003
  }
2004
+ const devScreenciPackageRoot = getDevScreenciPackageRoot();
2005
+ if (devScreenciPackageRoot) {
2006
+ const configDir = dirname(configPath);
2007
+ await buildLocalScreenciPackage(devScreenciPackageRoot);
2008
+ await installLocalScreenciPackage(configDir, devScreenciPackageRoot);
2009
+ }
1837
2010
  const playwrightArgs = [
1838
2011
  'playwright',
1839
2012
  'test',