@synkro-sh/cli 1.3.8 → 1.3.10

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
@@ -2438,10 +2438,13 @@ var init_auth = __esm({
2438
2438
 
2439
2439
  // cli/api/projects.ts
2440
2440
  import { config } from "dotenv";
2441
- async function callApi(method, endpoint, body) {
2442
- if (!API_URL) {
2443
- throw new Error("SYNKRO_CRUD_URL (or SYNKRO_API_URL) is not set. Add it to your .env file.");
2441
+ function resolveApiUrl() {
2442
+ for (const v of [process.env.SYNKRO_CRUD_URL, process.env.SYNKRO_API_URL]) {
2443
+ if (v && /^https?:\/\//.test(v)) return v;
2444
2444
  }
2445
+ return "https://api.synkro.sh/api";
2446
+ }
2447
+ async function callApi(method, endpoint, body) {
2445
2448
  const url = `${API_URL}${endpoint}`;
2446
2449
  const accessToken = getAccessToken();
2447
2450
  const headers = {
@@ -2478,7 +2481,7 @@ var init_projects = __esm({
2478
2481
  "use strict";
2479
2482
  init_auth();
2480
2483
  config({ quiet: true });
2481
- API_URL = process.env.SYNKRO_CRUD_URL || process.env.SYNKRO_API_URL;
2484
+ API_URL = resolveApiUrl();
2482
2485
  }
2483
2486
  });
2484
2487
 
@@ -2752,8 +2755,30 @@ async function connectGithubAndSelectRepos() {
2752
2755
  rl.close();
2753
2756
  }
2754
2757
  }
2755
- async function promptRepoConnection() {
2758
+ async function promptRepoConnection(opts) {
2756
2759
  const localRepo = detectGitRepo();
2760
+ if (opts?.linkRepo !== void 0) {
2761
+ if (opts.linkRepo && localRepo) {
2762
+ try {
2763
+ const existing = await listProjects();
2764
+ const alreadyLinked = existing.some(
2765
+ (p) => p.repos?.some((r) => r.full_name === localRepo.fullName)
2766
+ );
2767
+ if (!alreadyLinked) {
2768
+ await createProject(localRepo.shortName, [{ full_name: localRepo.fullName }]);
2769
+ console.log(` \u2713 Created project "${localRepo.shortName}" linked to ${localRepo.fullName}`);
2770
+ } else {
2771
+ console.log(` \u2713 ${localRepo.fullName} is already linked to a Synkro project.`);
2772
+ }
2773
+ } catch (err) {
2774
+ console.warn(` \u26A0 Could not link repo: ${err.message}`);
2775
+ }
2776
+ } else if (opts.linkRepo) {
2777
+ console.warn(" \u26A0 --link-repo specified but not in a git repo. Skipping.");
2778
+ }
2779
+ console.log();
2780
+ return;
2781
+ }
2757
2782
  const rl = createInterface({ input: process.stdin, output: process.stdout });
2758
2783
  try {
2759
2784
  console.log("Connect repos to Synkro:\n");
@@ -2918,11 +2943,14 @@ async function setupGithubCommand(opts = {}) {
2918
2943
  process.exit(1);
2919
2944
  }
2920
2945
  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();
2946
+ let ghToken = opts.githubToken || "";
2947
+ if (!ghToken) {
2948
+ console.log("Synkro PR scan setup\n");
2949
+ console.log("Requirements:");
2950
+ console.log(" \u2022 A GitHub personal access token with `repo` scope");
2951
+ console.log(" (create at https://github.com/settings/tokens?type=beta)\n");
2952
+ ghToken = (await prompt(rl, "GitHub token (paste): ", { silent: true })).trim();
2953
+ }
2926
2954
  if (!ghToken || !ghToken.startsWith("ghp_") && !ghToken.startsWith("github_pat_")) {
2927
2955
  console.error("Invalid GitHub token format. Expected ghp_... or github_pat_...");
2928
2956
  rl.close();
@@ -3061,6 +3089,17 @@ function parseArgs(argv) {
3061
3089
  else if (a === "--skip-auth") opts.skipAuth = true;
3062
3090
  else if (a === "--no-mcp") opts.noMcp = true;
3063
3091
  else if (a === "--force" || a === "-f") opts.force = true;
3092
+ else if (a === "--non-interactive") opts.nonInteractive = true;
3093
+ else if (a === "--link-repo") opts.linkRepo = true;
3094
+ else if (a === "--sync-transcripts") opts.syncTranscripts = true;
3095
+ else if (a === "--no-sync-transcripts") opts.syncTranscripts = false;
3096
+ else if (a === "--pr-scan=claude-oauth") opts.prScan = "claude-oauth";
3097
+ else if (a === "--pr-scan=byok") opts.prScan = "byok";
3098
+ else if (a === "--pr-scan=skip") opts.prScan = "skip";
3099
+ else if (a.startsWith("--pr-scan=")) opts.prScan = a.slice("--pr-scan=".length);
3100
+ else if (a.startsWith("--inference-key=")) opts.inferenceKey = a.slice("--inference-key=".length);
3101
+ else if (a.startsWith("--inference-model=")) opts.inferenceModel = a.slice("--inference-model=".length);
3102
+ else if (a.startsWith("--github-token=")) opts.githubToken = a.slice("--github-token=".length);
3064
3103
  }
3065
3104
  if (!opts.gatewayUrl) {
3066
3105
  const fromEnv = sanitizeGatewayCandidate(process.env.SYNKRO_GATEWAY_URL);
@@ -3135,7 +3174,7 @@ function writeConfigEnv(opts) {
3135
3174
  `SYNKRO_GATEWAY_URL=${shellQuoteSingle(safeGateway)}`,
3136
3175
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
3137
3176
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
3138
- `SYNKRO_VERSION=${shellQuoteSingle("1.3.8")}`
3177
+ `SYNKRO_VERSION=${shellQuoteSingle("1.3.10")}`
3139
3178
  ];
3140
3179
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
3141
3180
  if (safeOrgId) lines.push(`SYNKRO_ORG_ID=${shellQuoteSingle(safeOrgId)}`);
@@ -3238,7 +3277,11 @@ async function installCommand(opts = {}) {
3238
3277
  console.error("No access token available after auth.");
3239
3278
  process.exit(1);
3240
3279
  }
3241
- await promptRepoConnection();
3280
+ if (opts.nonInteractive) {
3281
+ await promptRepoConnection({ linkRepo: opts.linkRepo ?? true });
3282
+ } else {
3283
+ await promptRepoConnection();
3284
+ }
3242
3285
  const agents = detectAgents();
3243
3286
  if (agents.length === 0) {
3244
3287
  console.error("No AI coding agents detected. Install Claude Code first: https://docs.claude.com/claude-code");
@@ -3336,7 +3379,7 @@ async function installCommand(opts = {}) {
3336
3379
  `);
3337
3380
  const repo = detectGitRepo2();
3338
3381
  if (repo && getClaudeProjectsFolder()) {
3339
- const syncHistory = await promptYesNo("Sync Claude Code session history for this repo?");
3382
+ const syncHistory = opts.nonInteractive ? opts.syncTranscripts ?? true : await promptYesNo("Sync Claude Code session history for this repo?");
3340
3383
  if (syncHistory) {
3341
3384
  try {
3342
3385
  const ingested = await ingestSessionTranscripts(gatewayUrl, token, repo);
@@ -3390,51 +3433,52 @@ async function installCommand(opts = {}) {
3390
3433
  if (ghConnected) {
3391
3434
  console.log(`GitHub is connected (${ghLogin || "linked via dashboard"}).`);
3392
3435
  }
3393
- const setupGh = await promptYesNo("Set up GitHub PR scanning?");
3436
+ const prScanChoice = opts.nonInteractive ? opts.prScan ?? "skip" : null;
3437
+ const setupGh = prScanChoice !== null ? prScanChoice !== "skip" : await promptYesNo("Set up GitHub PR scanning?");
3394
3438
  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 {
3439
+ let choice;
3440
+ if (prScanChoice) {
3441
+ choice = prScanChoice === "claude-oauth" ? "1" : prScanChoice === "byok" ? "2" : "3";
3442
+ } else {
3443
+ const rl = createInterface3({ input: stdinStream, output: stdoutStream });
3444
+ console.log("\nHow should PR scans authenticate for AI review?\n");
3445
+ console.log(" 1. Claude Code OAuth \u2014 opens browser, auto-captures token");
3446
+ console.log(" 2. Use your own API key \u2014 paste an Anthropic, OpenAI, or Gemini key");
3447
+ console.log(" 3. Skip for now\n");
3448
+ choice = (await rl.question("Choice [1/2/3]: ")).trim();
3449
+ rl.close();
3405
3450
  }
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
3451
  if (choice === "1") {
3414
3452
  try {
3415
- await setupGithubCommand();
3453
+ await setupGithubCommand({ githubToken: opts.githubToken });
3416
3454
  } catch (err) {
3417
3455
  console.warn(` \u26A0 GitHub setup failed: ${err.message}`);
3418
3456
  console.warn(" Run `synkro-cli setup-github` to retry.\n");
3419
3457
  }
3420
3458
  } 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();
3459
+ let apiKey = opts.inferenceKey || "";
3460
+ if (!apiKey) {
3461
+ const rl2 = createInterface3({ input: stdinStream, output: stdoutStream });
3462
+ apiKey = (await rl2.question("Paste your API key: ")).trim();
3463
+ rl2.close();
3464
+ }
3424
3465
  if (apiKey) {
3425
3466
  const provider = detectProviderFromKey(apiKey);
3426
3467
  if (provider) {
3427
- const models = PROVIDER_MODELS[provider];
3428
- console.log(`
3468
+ let selectedModel = opts.inferenceModel || "";
3469
+ if (!selectedModel) {
3470
+ const models = PROVIDER_MODELS[provider];
3471
+ console.log(`
3429
3472
  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];
3473
+ console.log(" Select a model for classification + grading:\n");
3474
+ models.forEach((m, i) => console.log(` ${i + 1}. ${m}`));
3475
+ console.log();
3476
+ const rl3 = createInterface3({ input: stdinStream, output: stdoutStream });
3477
+ const modelChoice = (await rl3.question(` Model [1-${models.length}]: `)).trim();
3478
+ rl3.close();
3479
+ const modelIdx = parseInt(modelChoice, 10) - 1;
3480
+ selectedModel = modelIdx >= 0 && modelIdx < models.length ? models[modelIdx] : models[0];
3481
+ }
3438
3482
  console.log(` Using: ${selectedModel}`);
3439
3483
  try {
3440
3484
  const resp = await fetch(`${gatewayUrl}/api/v1/settings/inference`, {
@@ -3468,7 +3512,7 @@ async function installCommand(opts = {}) {
3468
3512
  }
3469
3513
  }
3470
3514
  try {
3471
- await setupGithubCommand({ skipClaudeToken: true });
3515
+ await setupGithubCommand({ skipClaudeToken: true, githubToken: opts.githubToken });
3472
3516
  } catch (err) {
3473
3517
  console.warn(` \u26A0 GitHub setup failed: ${err.message}`);
3474
3518
  console.warn(" Run `synkro-cli setup-github` to retry.\n");
@@ -4488,7 +4532,16 @@ Usage:
4488
4532
  synkro <command> [options] (alias)
4489
4533
 
4490
4534
  Commands:
4491
- install [--force] Install Synkro hooks for detected agents (Claude Code, etc.)
4535
+ install [options] Install Synkro hooks for detected agents (Claude Code, etc.)
4536
+ --force Reinstall from scratch
4537
+ --non-interactive Skip all interactive prompts (use flags)
4538
+ --link-repo Auto-link the local git repo
4539
+ --sync-transcripts Sync CC session history (default in non-interactive)
4540
+ --no-sync-transcripts Skip transcript sync
4541
+ --pr-scan=MODE PR scan auth: claude-oauth, byok, or skip
4542
+ --inference-key=KEY BYOK API key (Anthropic, OpenAI, or Gemini)
4543
+ --inference-model=M Model for classification + grading
4544
+ --github-token=TOK GitHub PAT for pushing secrets + workflow
4492
4545
  login Authenticate with Synkro (browser OAuth via WorkOS)
4493
4546
  logout Clear local credentials
4494
4547
  status Show current setup state