@synkro-sh/cli 1.3.8 → 1.3.9

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/bootstrap.js CHANGED
@@ -2752,8 +2752,30 @@ async function connectGithubAndSelectRepos() {
2752
2752
  rl.close();
2753
2753
  }
2754
2754
  }
2755
- async function promptRepoConnection() {
2755
+ async function promptRepoConnection(opts) {
2756
2756
  const localRepo = detectGitRepo();
2757
+ if (opts?.linkRepo !== void 0) {
2758
+ if (opts.linkRepo && localRepo) {
2759
+ try {
2760
+ const existing = await listProjects();
2761
+ const alreadyLinked = existing.some(
2762
+ (p) => p.repos?.some((r) => r.full_name === localRepo.fullName)
2763
+ );
2764
+ if (!alreadyLinked) {
2765
+ await createProject(localRepo.shortName, [{ full_name: localRepo.fullName }]);
2766
+ console.log(` \u2713 Created project "${localRepo.shortName}" linked to ${localRepo.fullName}`);
2767
+ } else {
2768
+ console.log(` \u2713 ${localRepo.fullName} is already linked to a Synkro project.`);
2769
+ }
2770
+ } catch (err) {
2771
+ console.warn(` \u26A0 Could not link repo: ${err.message}`);
2772
+ }
2773
+ } else if (opts.linkRepo) {
2774
+ console.warn(" \u26A0 --link-repo specified but not in a git repo. Skipping.");
2775
+ }
2776
+ console.log();
2777
+ return;
2778
+ }
2757
2779
  const rl = createInterface({ input: process.stdin, output: process.stdout });
2758
2780
  try {
2759
2781
  console.log("Connect repos to Synkro:\n");
@@ -2918,11 +2940,14 @@ async function setupGithubCommand(opts = {}) {
2918
2940
  process.exit(1);
2919
2941
  }
2920
2942
  const rl = createInterface2({ input, output });
2921
- console.log("Synkro PR scan setup\n");
2922
- console.log("Requirements:");
2923
- console.log(" \u2022 A GitHub personal access token with `repo` scope");
2924
- console.log(" (create at https://github.com/settings/tokens?type=beta)\n");
2925
- const ghToken = (await prompt(rl, "GitHub token (paste): ", { silent: true })).trim();
2943
+ let ghToken = opts.githubToken || "";
2944
+ if (!ghToken) {
2945
+ console.log("Synkro PR scan setup\n");
2946
+ console.log("Requirements:");
2947
+ console.log(" \u2022 A GitHub personal access token with `repo` scope");
2948
+ console.log(" (create at https://github.com/settings/tokens?type=beta)\n");
2949
+ ghToken = (await prompt(rl, "GitHub token (paste): ", { silent: true })).trim();
2950
+ }
2926
2951
  if (!ghToken || !ghToken.startsWith("ghp_") && !ghToken.startsWith("github_pat_")) {
2927
2952
  console.error("Invalid GitHub token format. Expected ghp_... or github_pat_...");
2928
2953
  rl.close();
@@ -3061,6 +3086,17 @@ function parseArgs(argv) {
3061
3086
  else if (a === "--skip-auth") opts.skipAuth = true;
3062
3087
  else if (a === "--no-mcp") opts.noMcp = true;
3063
3088
  else if (a === "--force" || a === "-f") opts.force = true;
3089
+ else if (a === "--non-interactive") opts.nonInteractive = true;
3090
+ else if (a === "--link-repo") opts.linkRepo = true;
3091
+ else if (a === "--sync-transcripts") opts.syncTranscripts = true;
3092
+ else if (a === "--no-sync-transcripts") opts.syncTranscripts = false;
3093
+ else if (a === "--pr-scan=claude-oauth") opts.prScan = "claude-oauth";
3094
+ else if (a === "--pr-scan=byok") opts.prScan = "byok";
3095
+ else if (a === "--pr-scan=skip") opts.prScan = "skip";
3096
+ else if (a.startsWith("--pr-scan=")) opts.prScan = a.slice("--pr-scan=".length);
3097
+ else if (a.startsWith("--inference-key=")) opts.inferenceKey = a.slice("--inference-key=".length);
3098
+ else if (a.startsWith("--inference-model=")) opts.inferenceModel = a.slice("--inference-model=".length);
3099
+ else if (a.startsWith("--github-token=")) opts.githubToken = a.slice("--github-token=".length);
3064
3100
  }
3065
3101
  if (!opts.gatewayUrl) {
3066
3102
  const fromEnv = sanitizeGatewayCandidate(process.env.SYNKRO_GATEWAY_URL);
@@ -3135,7 +3171,7 @@ function writeConfigEnv(opts) {
3135
3171
  `SYNKRO_GATEWAY_URL=${shellQuoteSingle(safeGateway)}`,
3136
3172
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
3137
3173
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
3138
- `SYNKRO_VERSION=${shellQuoteSingle("1.3.8")}`
3174
+ `SYNKRO_VERSION=${shellQuoteSingle("1.3.9")}`
3139
3175
  ];
3140
3176
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
3141
3177
  if (safeOrgId) lines.push(`SYNKRO_ORG_ID=${shellQuoteSingle(safeOrgId)}`);
@@ -3238,7 +3274,11 @@ async function installCommand(opts = {}) {
3238
3274
  console.error("No access token available after auth.");
3239
3275
  process.exit(1);
3240
3276
  }
3241
- await promptRepoConnection();
3277
+ if (opts.nonInteractive) {
3278
+ await promptRepoConnection({ linkRepo: opts.linkRepo ?? true });
3279
+ } else {
3280
+ await promptRepoConnection();
3281
+ }
3242
3282
  const agents = detectAgents();
3243
3283
  if (agents.length === 0) {
3244
3284
  console.error("No AI coding agents detected. Install Claude Code first: https://docs.claude.com/claude-code");
@@ -3336,7 +3376,7 @@ async function installCommand(opts = {}) {
3336
3376
  `);
3337
3377
  const repo = detectGitRepo2();
3338
3378
  if (repo && getClaudeProjectsFolder()) {
3339
- const syncHistory = await promptYesNo("Sync Claude Code session history for this repo?");
3379
+ const syncHistory = opts.nonInteractive ? opts.syncTranscripts ?? true : await promptYesNo("Sync Claude Code session history for this repo?");
3340
3380
  if (syncHistory) {
3341
3381
  try {
3342
3382
  const ingested = await ingestSessionTranscripts(gatewayUrl, token, repo);
@@ -3390,51 +3430,52 @@ async function installCommand(opts = {}) {
3390
3430
  if (ghConnected) {
3391
3431
  console.log(`GitHub is connected (${ghLogin || "linked via dashboard"}).`);
3392
3432
  }
3393
- const setupGh = await promptYesNo("Set up GitHub PR scanning?");
3433
+ const prScanChoice = opts.nonInteractive ? opts.prScan ?? "skip" : null;
3434
+ const setupGh = prScanChoice !== null ? prScanChoice !== "skip" : await promptYesNo("Set up GitHub PR scanning?");
3394
3435
  if (setupGh) {
3395
- let hasInferenceKey = false;
3396
- try {
3397
- const resp = await fetch(`${gatewayUrl}/api/v1/settings/inference`, {
3398
- headers: { "Authorization": `Bearer ${token}` }
3399
- });
3400
- if (resp.ok) {
3401
- const data = await resp.json();
3402
- hasInferenceKey = data.configured && !!data.roles?.classification;
3403
- }
3404
- } catch {
3436
+ let choice;
3437
+ if (prScanChoice) {
3438
+ choice = prScanChoice === "claude-oauth" ? "1" : prScanChoice === "byok" ? "2" : "3";
3439
+ } else {
3440
+ const rl = createInterface3({ input: stdinStream, output: stdoutStream });
3441
+ console.log("\nHow should PR scans authenticate for AI review?\n");
3442
+ console.log(" 1. Claude Code OAuth \u2014 opens browser, auto-captures token");
3443
+ console.log(" 2. Use your own API key \u2014 paste an Anthropic, OpenAI, or Gemini key");
3444
+ console.log(" 3. Skip for now\n");
3445
+ choice = (await rl.question("Choice [1/2/3]: ")).trim();
3446
+ rl.close();
3405
3447
  }
3406
- const rl = createInterface3({ input: stdinStream, output: stdoutStream });
3407
- console.log("\nHow should PR scans authenticate for AI review?\n");
3408
- console.log(" 1. Claude Code OAuth \u2014 opens browser, auto-captures token");
3409
- console.log(" 2. Use your own API key \u2014 paste an Anthropic, OpenAI, or Gemini key");
3410
- console.log(" 3. Skip for now\n");
3411
- const choice = (await rl.question("Choice [1/2/3]: ")).trim();
3412
- rl.close();
3413
3448
  if (choice === "1") {
3414
3449
  try {
3415
- await setupGithubCommand();
3450
+ await setupGithubCommand({ githubToken: opts.githubToken });
3416
3451
  } catch (err) {
3417
3452
  console.warn(` \u26A0 GitHub setup failed: ${err.message}`);
3418
3453
  console.warn(" Run `synkro-cli setup-github` to retry.\n");
3419
3454
  }
3420
3455
  } else if (choice === "2") {
3421
- const rl2 = createInterface3({ input: stdinStream, output: stdoutStream });
3422
- const apiKey = (await rl2.question("Paste your API key: ")).trim();
3423
- rl2.close();
3456
+ let apiKey = opts.inferenceKey || "";
3457
+ if (!apiKey) {
3458
+ const rl2 = createInterface3({ input: stdinStream, output: stdoutStream });
3459
+ apiKey = (await rl2.question("Paste your API key: ")).trim();
3460
+ rl2.close();
3461
+ }
3424
3462
  if (apiKey) {
3425
3463
  const provider = detectProviderFromKey(apiKey);
3426
3464
  if (provider) {
3427
- const models = PROVIDER_MODELS[provider];
3428
- console.log(`
3465
+ let selectedModel = opts.inferenceModel || "";
3466
+ if (!selectedModel) {
3467
+ const models = PROVIDER_MODELS[provider];
3468
+ console.log(`
3429
3469
  Detected provider: ${provider}`);
3430
- console.log(" Select a model for classification + grading:\n");
3431
- models.forEach((m, i) => console.log(` ${i + 1}. ${m}`));
3432
- console.log();
3433
- const rl3 = createInterface3({ input: stdinStream, output: stdoutStream });
3434
- const modelChoice = (await rl3.question(` Model [1-${models.length}]: `)).trim();
3435
- rl3.close();
3436
- const modelIdx = parseInt(modelChoice, 10) - 1;
3437
- const selectedModel = modelIdx >= 0 && modelIdx < models.length ? models[modelIdx] : models[0];
3470
+ console.log(" Select a model for classification + grading:\n");
3471
+ models.forEach((m, i) => console.log(` ${i + 1}. ${m}`));
3472
+ console.log();
3473
+ const rl3 = createInterface3({ input: stdinStream, output: stdoutStream });
3474
+ const modelChoice = (await rl3.question(` Model [1-${models.length}]: `)).trim();
3475
+ rl3.close();
3476
+ const modelIdx = parseInt(modelChoice, 10) - 1;
3477
+ selectedModel = modelIdx >= 0 && modelIdx < models.length ? models[modelIdx] : models[0];
3478
+ }
3438
3479
  console.log(` Using: ${selectedModel}`);
3439
3480
  try {
3440
3481
  const resp = await fetch(`${gatewayUrl}/api/v1/settings/inference`, {
@@ -3468,7 +3509,7 @@ async function installCommand(opts = {}) {
3468
3509
  }
3469
3510
  }
3470
3511
  try {
3471
- await setupGithubCommand({ skipClaudeToken: true });
3512
+ await setupGithubCommand({ skipClaudeToken: true, githubToken: opts.githubToken });
3472
3513
  } catch (err) {
3473
3514
  console.warn(` \u26A0 GitHub setup failed: ${err.message}`);
3474
3515
  console.warn(" Run `synkro-cli setup-github` to retry.\n");
@@ -4488,7 +4529,16 @@ Usage:
4488
4529
  synkro <command> [options] (alias)
4489
4530
 
4490
4531
  Commands:
4491
- install [--force] Install Synkro hooks for detected agents (Claude Code, etc.)
4532
+ install [options] Install Synkro hooks for detected agents (Claude Code, etc.)
4533
+ --force Reinstall from scratch
4534
+ --non-interactive Skip all interactive prompts (use flags)
4535
+ --link-repo Auto-link the local git repo
4536
+ --sync-transcripts Sync CC session history (default in non-interactive)
4537
+ --no-sync-transcripts Skip transcript sync
4538
+ --pr-scan=MODE PR scan auth: claude-oauth, byok, or skip
4539
+ --inference-key=KEY BYOK API key (Anthropic, OpenAI, or Gemini)
4540
+ --inference-model=M Model for classification + grading
4541
+ --github-token=TOK GitHub PAT for pushing secrets + workflow
4492
4542
  login Authenticate with Synkro (browser OAuth via WorkOS)
4493
4543
  logout Clear local credentials
4494
4544
  status Show current setup state