@gh-symphony/cli 0.0.19 → 0.0.21

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.
@@ -1,7 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- parseWorkflowMarkdown
4
- } from "./chunk-M3IFVLQS.js";
5
2
  import {
6
3
  GitHubApiError,
7
4
  REQUIRED_GH_SCOPES,
@@ -15,7 +12,14 @@ import {
15
12
  runGhAuthLogin,
16
13
  runGhAuthRefresh,
17
14
  validateGitHubToken
18
- } from "./chunk-TILHWBP6.js";
15
+ } from "./chunk-C67H3OUL.js";
16
+ import {
17
+ isClaudeRuntimeCommand,
18
+ parseWorkflowMarkdown,
19
+ resolveClaudeCommandBinary,
20
+ resolveRuntimeCommandBinary,
21
+ runClaudePreflight
22
+ } from "./chunk-QEONJ5DZ.js";
19
23
  import {
20
24
  resolveRuntimeRoot
21
25
  } from "./chunk-5NV3LSAJ.js";
@@ -55,7 +59,8 @@ var DEFAULT_DEPENDENCIES = {
55
59
  stdinIsTTY: process.stdin.isTTY === true,
56
60
  stdoutIsTTY: process.stdout.isTTY === true,
57
61
  execPath: process.execPath,
58
- cliArgv: [...process.argv]
62
+ cliArgv: [...process.argv],
63
+ fetchImpl: fetch
59
64
  };
60
65
  var MINIMUM_NODE_MAJOR = 24;
61
66
  var MINIMUM_NODE_VERSION = `v${MINIMUM_NODE_MAJOR}.0.0`;
@@ -98,6 +103,17 @@ function failCheck(id, title, summary, remediation, details) {
98
103
  details
99
104
  };
100
105
  }
106
+ function warnCheck(id, title, summary, remediation, details) {
107
+ return {
108
+ id,
109
+ title,
110
+ status: "warn",
111
+ required: true,
112
+ summary,
113
+ remediation,
114
+ details
115
+ };
116
+ }
101
117
  function formatAuthSource(source) {
102
118
  return source === "env" ? "GITHUB_GRAPHQL_TOKEN" : "gh CLI";
103
119
  }
@@ -233,31 +249,27 @@ async function commandExistsOnPath(binary, deps) {
233
249
  }
234
250
  return false;
235
251
  }
236
- function extractCommandBinary(command) {
237
- const trimmed = command.trim();
238
- if (!trimmed) {
239
- return null;
252
+ function toDoctorClaudeCheck(check) {
253
+ const id = check.id;
254
+ if (check.status === "pass") {
255
+ return passCheck(id, check.title, check.summary, check.details);
240
256
  }
241
- const tokens = trimmed.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
242
- if (tokens.length === 0) {
243
- return null;
244
- }
245
- const shell = stripQuotes(tokens[0]);
246
- if ((shell === "bash" || shell === "sh" || shell === "zsh" || shell === "fish") && tokens.length >= 3) {
247
- const flagIndex = tokens.findIndex((token) => {
248
- const value = stripQuotes(token);
249
- return value === "-c" || value === "-lc";
250
- });
251
- if (flagIndex >= 0 && flagIndex + 1 < tokens.length) {
252
- const nested = stripQuotes(tokens[flagIndex + 1]);
253
- const nestedTokens = nested.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
254
- return nestedTokens.length > 0 ? stripQuotes(nestedTokens[0]) : shell;
255
- }
257
+ if (check.status === "warn") {
258
+ return warnCheck(
259
+ id,
260
+ check.title,
261
+ check.summary,
262
+ check.remediation,
263
+ check.details
264
+ );
256
265
  }
257
- return shell;
258
- }
259
- function stripQuotes(value) {
260
- return value.replace(/^['"]|['"]$/g, "");
266
+ return failCheck(
267
+ id,
268
+ check.title,
269
+ check.summary,
270
+ check.remediation ?? "Fix the Claude runtime readiness check.",
271
+ check.details
272
+ );
261
273
  }
262
274
  function parseMajorNodeVersion(version) {
263
275
  const matched = version.match(/^v?(\d+)(?:\.\d+)?(?:\.\d+)?$/);
@@ -276,7 +288,10 @@ async function checkGitInstallation(deps) {
276
288
  encoding: "utf8",
277
289
  stdio: ["pipe", "pipe", "pipe"]
278
290
  }).toString().trim();
279
- return version ? { installed: true, version } : { installed: false, error: "git --version returned an empty response." };
291
+ return version ? { installed: true, version } : {
292
+ installed: false,
293
+ error: "git --version returned an empty response."
294
+ };
280
295
  } catch (error) {
281
296
  return {
282
297
  installed: false,
@@ -387,7 +402,11 @@ Usage: gh-symphony doctor [--project-id <project-id>] [--fix]`
387
402
  const ghInstalled = deps.checkGhInstalled();
388
403
  if (ghInstalled) {
389
404
  checks.push(
390
- passCheck("gh_installation", "gh CLI installation", "gh CLI is installed.")
405
+ passCheck(
406
+ "gh_installation",
407
+ "gh CLI installation",
408
+ "gh CLI is installed."
409
+ )
391
410
  );
392
411
  } else if (envToken) {
393
412
  checks.push(
@@ -664,7 +683,7 @@ Usage: gh-symphony doctor [--project-id <project-id>] [--fix]`
664
683
  );
665
684
  }
666
685
  if (workflow.status === "pass") {
667
- const binary = extractCommandBinary(workflow.command);
686
+ const binary = resolveRuntimeCommandBinary(workflow.command);
668
687
  if (binary && await commandExistsOnPath(binary, deps)) {
669
688
  checks.push(
670
689
  passCheck(
@@ -685,6 +704,24 @@ Usage: gh-symphony doctor [--project-id <project-id>] [--fix]`
685
704
  )
686
705
  );
687
706
  }
707
+ if (isClaudeRuntimeCommand(workflow.command)) {
708
+ const claudePreflight = await runClaudePreflight(
709
+ {
710
+ cwd: process.cwd(),
711
+ env: process.env,
712
+ command: resolveClaudeCommandBinary(workflow.command) ?? void 0,
713
+ includeGhAuth: false
714
+ },
715
+ {
716
+ execFileSync: deps.execFileSync,
717
+ readFile: deps.readFile,
718
+ access: deps.access,
719
+ fetchImpl: deps.fetchImpl,
720
+ platform: deps.platform
721
+ }
722
+ );
723
+ checks.push(...claudePreflight.checks.map(toDoctorClaudeCheck));
724
+ }
688
725
  } else {
689
726
  checks.push(
690
727
  failCheck(
@@ -697,7 +734,7 @@ Usage: gh-symphony doctor [--project-id <project-id>] [--fix]`
697
734
  );
698
735
  }
699
736
  return {
700
- ok: checks.every((check) => check.status === "pass"),
737
+ ok: checks.every((check) => check.status !== "fail"),
701
738
  checkedAt: (/* @__PURE__ */ new Date()).toISOString(),
702
739
  configDir: options.configDir,
703
740
  projectId: resolvedProjectId,
@@ -749,9 +786,13 @@ function runCliRemediation(title, checkId, args, deps, options, interactive, det
749
786
  details
750
787
  );
751
788
  }
752
- const result = deps.spawnSync(deps.execPath, [cliEntry, "--config", options.configDir, ...args], {
753
- stdio: "inherit"
754
- });
789
+ const result = deps.spawnSync(
790
+ deps.execPath,
791
+ [cliEntry, "--config", options.configDir, ...args],
792
+ {
793
+ stdio: "inherit"
794
+ }
795
+ );
755
796
  if ((result.status ?? 1) === 0) {
756
797
  return remediationStep(
757
798
  `remediate_${checkId}`,
@@ -1007,7 +1048,15 @@ async function runDoctorFixes(report, deps, options) {
1007
1048
  const reason = typeof check.details?.reason === "string" ? check.details.reason : null;
1008
1049
  const title = reason === "missing" ? "Repository workflow initialization" : "Repository workflow regeneration";
1009
1050
  steps.push(
1010
- runCliRemediation(title, check.id, ["init"], deps, options, interactive, check.details)
1051
+ runCliRemediation(
1052
+ title,
1053
+ check.id,
1054
+ ["init"],
1055
+ deps,
1056
+ options,
1057
+ interactive,
1058
+ check.details
1059
+ )
1011
1060
  );
1012
1061
  break;
1013
1062
  }
@@ -1037,6 +1086,21 @@ async function runDoctorFixes(report, deps, options) {
1037
1086
  );
1038
1087
  break;
1039
1088
  }
1089
+ case "claude_binary":
1090
+ case "anthropic_api_key":
1091
+ case "claude_mcp_config":
1092
+ steps.push(
1093
+ remediationStep(
1094
+ `remediate_${check.id}`,
1095
+ check.id,
1096
+ check.title,
1097
+ "manual",
1098
+ check.remediation ?? "Fix the Claude runtime readiness check.",
1099
+ void 0,
1100
+ check.details
1101
+ )
1102
+ );
1103
+ break;
1040
1104
  }
1041
1105
  }
1042
1106
  return steps;
@@ -1060,7 +1124,8 @@ function renderTextReport(report) {
1060
1124
  lines.push("");
1061
1125
  }
1062
1126
  for (const check of report.checks) {
1063
- lines.push(`${check.status === "pass" ? "PASS" : "FAIL"} ${check.title}`);
1127
+ const statusLabel = check.status === "pass" ? "PASS" : check.status === "warn" ? "WARN" : "FAIL";
1128
+ lines.push(`${statusLabel} ${check.title}`);
1064
1129
  lines.push(` ${check.summary}`);
1065
1130
  if (check.remediation) {
1066
1131
  lines.push(` Fix: ${check.remediation}`);
package/dist/index.js CHANGED
@@ -278,21 +278,21 @@ ${bashFunction}complete -F _gh_symphony_completion gh-symphony
278
278
 
279
279
  // src/index.ts
280
280
  var COMMANDS = {
281
- workflow: () => import("./workflow-TBIFY5MO.js"),
282
- init: () => import("./init-KZT6YNOH.js"),
283
- setup: () => import("./setup-K4CYYJBF.js"),
284
- doctor: () => import("./doctor-IYHCFXOZ.js"),
285
- upgrade: () => import("./upgrade-F4VE4XBS.js"),
286
- start: () => import("./start-M6IQGRFO.js"),
281
+ workflow: () => import("./workflow-BLJH2HC3.js"),
282
+ init: () => import("./init-HZ3JEDGQ.js"),
283
+ setup: () => import("./setup-B2SVLW2R.js"),
284
+ doctor: () => import("./doctor-4HBRICHP.js"),
285
+ upgrade: () => import("./upgrade-OJXPZRYE.js"),
286
+ start: () => import("./start-I2CC7BLW.js"),
287
287
  stop: () => import("./stop-7MFCBQVW.js"),
288
288
  status: () => import("./status-QSCFVGRQ.js"),
289
- run: () => import("./run-XI2S5Y4V.js"),
290
- recover: () => import("./recover-C3V2QAUB.js"),
289
+ run: () => import("./run-XJQ6BF7U.js"),
290
+ recover: () => import("./recover-L3MJHHDA.js"),
291
291
  logs: () => import("./logs-6JKKYDGJ.js"),
292
- project: () => import("./project-DNALEWO3.js"),
293
- repo: () => import("./repo-HDDE7OUI.js"),
292
+ project: () => import("./project-25NQ4J4Y.js"),
293
+ repo: () => import("./repo-TDCWQR6P.js"),
294
294
  config: () => import("./config-cmd-DNXNL26Z.js"),
295
- version: () => import("./version-Y5RYNWMF.js")
295
+ version: () => import("./version-TBDCTKDO.js")
296
296
  };
297
297
  function addGlobalOptions(command) {
298
298
  return command.option("--config <dir>", "Config directory").addOption(new Option("--config-dir <dir>").hideHelp()).option("-v, --verbose", "Enable verbose output").option("--json", "Output in JSON format").option("--no-color", "Disable color output");
@@ -356,7 +356,7 @@ function createProgram() {
356
356
  new Command().name("gh-symphony").description("AI Coding Agent Orchestrator").exitOverride().helpOption("-h, --help", "Show help").addHelpCommand("help [command]", "Show help for command").showHelpAfterError("(run with --help for usage)").option("-V, --version", "Show version")
357
357
  );
358
358
  addGlobalOptions(
359
- program.command("init", { hidden: true }).description("Alias for 'gh-symphony workflow init'").option("--non-interactive", "Run without prompts").option("--project <id>", "GitHub Project ID or URL").option("--output <path>", "Write WORKFLOW.md to a custom path").option("--skip-skills", "Skip runtime skill generation").option("--skip-context", "Skip .gh-symphony/context.yaml generation").option("--dry-run", "Preview generated files without writing them").allowExcessArguments(false)
359
+ program.command("init", { hidden: true }).description("Alias for 'gh-symphony workflow init'").option("--non-interactive", "Run without prompts").option("--project <id>", "GitHub Project ID or URL").option("--output <path>", "Write WORKFLOW.md to a custom path").option("--runtime <kind>", "Runtime preset: codex-app-server or claude-print").option("--skip-skills", "Skip runtime skill generation").option("--skip-context", "Skip .gh-symphony/context.yaml generation").option("--dry-run", "Preview generated files without writing them").allowExcessArguments(false)
360
360
  ).action(async function() {
361
361
  markInvoked();
362
362
  const values = this.optsWithGlobals();
@@ -364,6 +364,7 @@ function createProgram() {
364
364
  pushOption(args, "--non-interactive", values.nonInteractive);
365
365
  pushOption(args, "--project", values.project);
366
366
  pushOption(args, "--output", values.output);
367
+ pushOption(args, "--runtime", values.runtime);
367
368
  pushOption(args, "--skip-skills", values.skipSkills);
368
369
  pushOption(args, "--skip-context", values.skipContext);
369
370
  pushOption(args, "--dry-run", values.dryRun);
@@ -381,7 +382,7 @@ function createProgram() {
381
382
  );
382
383
  });
383
384
  addGlobalOptions(
384
- workflow.command("init").description("Generate WORKFLOW.md and workflow support files").option("--non-interactive", "Run without prompts").option("--project <id>", "GitHub Project ID or URL").option("--output <path>", "Write WORKFLOW.md to a custom path").option("--skip-skills", "Skip runtime skill generation").option("--skip-context", "Skip .gh-symphony/context.yaml generation").option("--dry-run", "Preview generated files without writing them").allowExcessArguments(false)
385
+ workflow.command("init").description("Generate WORKFLOW.md and workflow support files").option("--non-interactive", "Run without prompts").option("--project <id>", "GitHub Project ID or URL").option("--output <path>", "Write WORKFLOW.md to a custom path").option("--runtime <kind>", "Runtime preset: codex-app-server or claude-print").option("--skip-skills", "Skip runtime skill generation").option("--skip-context", "Skip .gh-symphony/context.yaml generation").option("--dry-run", "Preview generated files without writing them").allowExcessArguments(false)
385
386
  ).action(async function() {
386
387
  markInvoked();
387
388
  const values = this.optsWithGlobals();
@@ -389,6 +390,7 @@ function createProgram() {
389
390
  pushOption(args, "--non-interactive", values.nonInteractive);
390
391
  pushOption(args, "--project", values.project);
391
392
  pushOption(args, "--output", values.output);
393
+ pushOption(args, "--runtime", values.runtime);
392
394
  pushOption(args, "--skip-skills", values.skipSkills);
393
395
  pushOption(args, "--skip-context", values.skipContext);
394
396
  pushOption(args, "--dry-run", values.dryRun);
@@ -404,12 +406,14 @@ function createProgram() {
404
406
  await invokeHandler("workflow", args, values);
405
407
  });
406
408
  addGlobalOptions(
407
- workflow.command("preview").description("Render the final worker prompt from a sample issue").option("--file <path>", "Read a custom WORKFLOW.md path").option("--sample <json>", "Read sample issue JSON from a file").option("--attempt <n>", "Render as retry attempt n").allowExcessArguments(false)
409
+ workflow.command("preview").description("Render the final worker prompt from a sample or live issue").option("--file <path>", "Read a custom WORKFLOW.md path").option("--issue <owner/repo#number>", "Load a live GitHub Project issue").option("--project-id <projectId>", "Managed project identifier").addOption(new Option("--project <projectId>").hideHelp()).option("--sample <json>", "Read sample issue JSON from a file").option("--attempt <n>", "Render as retry attempt n").allowExcessArguments(false)
408
410
  ).action(async function() {
409
411
  markInvoked();
410
412
  const values = this.optsWithGlobals();
411
413
  const args = ["preview"];
412
414
  pushOption(args, "--file", values.file);
415
+ pushOption(args, "--issue", values.issue);
416
+ pushOption(args, "--project-id", resolveProjectId(values));
413
417
  pushOption(args, "--sample", values.sample);
414
418
  pushOption(args, "--attempt", values.attempt);
415
419
  await invokeHandler("workflow", args, values);
@@ -430,7 +434,10 @@ function createProgram() {
430
434
  await invokeHandler("setup", args, values);
431
435
  });
432
436
  addGlobalOptions(
433
- program.command("doctor").description("Run diagnostics and optional first-run remediation").option("--project-id <projectId>", "Project identifier").option("--fix", "Apply safe remediation steps and print manual follow-ups").addOption(new Option("--project <projectId>").hideHelp()).allowExcessArguments(false)
437
+ program.command("doctor").description("Run diagnostics and optional first-run remediation").option("--project-id <projectId>", "Project identifier").option(
438
+ "--fix",
439
+ "Apply safe remediation steps and print manual follow-ups"
440
+ ).addOption(new Option("--project <projectId>").hideHelp()).allowExcessArguments(false)
434
441
  ).action(async function() {
435
442
  markInvoked();
436
443
  const values = this.optsWithGlobals();
@@ -446,7 +453,13 @@ function createProgram() {
446
453
  await invokeHandler("upgrade", [], this.optsWithGlobals());
447
454
  });
448
455
  addGlobalOptions(
449
- program.command("start").description("Start the orchestrator").option("-d, --daemon", "Start in daemon mode").option("--once", "Run a single orchestration tick and exit").option("--http [port]", "Expose dashboard and refresh endpoints over HTTP").option("--log-level <level>", "Orchestrator lifecycle log level").option("--project-id <projectId>", "Project identifier").addOption(new Option("--project <projectId>").hideHelp()).allowExcessArguments(false)
456
+ program.command("start").description("Start the orchestrator").option("-d, --daemon", "Start in daemon mode").option("--once", "Run a single orchestration tick and exit").option(
457
+ "--http [port]",
458
+ "Expose dashboard and refresh endpoints over HTTP"
459
+ ).option(
460
+ "--web [port]",
461
+ "Expose the control plane web dashboard and API over HTTP"
462
+ ).option("--log-level <level>", "Orchestrator lifecycle log level").option("--project-id <projectId>", "Project identifier").addOption(new Option("--project <projectId>").hideHelp()).allowExcessArguments(false)
450
463
  ).action(async function() {
451
464
  markInvoked();
452
465
  const values = this.optsWithGlobals();
@@ -455,6 +468,7 @@ function createProgram() {
455
468
  pushOption(args, "--daemon", values.daemon);
456
469
  pushOption(args, "--once", values.once);
457
470
  pushOption(args, "--http", values.http);
471
+ pushOption(args, "--web", values.web);
458
472
  pushOption(args, "--log-level", values.logLevel);
459
473
  await invokeHandler("start", args, values);
460
474
  });
@@ -549,7 +563,13 @@ function createProgram() {
549
563
  );
550
564
  });
551
565
  addGlobalOptions(
552
- project.command("start").description("Start a specific project").option("-d, --daemon", "Start in daemon mode").option("--once", "Run a single orchestration tick and exit").option("--http [port]", "Expose dashboard and refresh endpoints over HTTP").option("--log-level <level>", "Orchestrator lifecycle log level").option("--project-id <projectId>", "Project identifier").addOption(new Option("--project <projectId>").hideHelp()).allowExcessArguments(false)
566
+ project.command("start").description("Start a specific project").option("-d, --daemon", "Start in daemon mode").option("--once", "Run a single orchestration tick and exit").option(
567
+ "--http [port]",
568
+ "Expose dashboard and refresh endpoints over HTTP"
569
+ ).option(
570
+ "--web [port]",
571
+ "Expose the control plane web dashboard and API over HTTP"
572
+ ).option("--log-level <level>", "Orchestrator lifecycle log level").option("--project-id <projectId>", "Project identifier").addOption(new Option("--project <projectId>").hideHelp()).allowExcessArguments(false)
553
573
  ).action(async function() {
554
574
  markInvoked();
555
575
  const values = this.optsWithGlobals();
@@ -558,6 +578,7 @@ function createProgram() {
558
578
  pushOption(args, "--daemon", values.daemon);
559
579
  pushOption(args, "--once", values.once);
560
580
  pushOption(args, "--http", values.http);
581
+ pushOption(args, "--web", values.web);
561
582
  pushOption(args, "--log-level", values.logLevel);
562
583
  await invokeHandler("project", args, values);
563
584
  });
@@ -9,12 +9,15 @@ import {
9
9
  planWorkflowArtifacts,
10
10
  promptStateMappings,
11
11
  renderDryRunPreview,
12
+ resolvePriorityField,
12
13
  resolveStatusField,
14
+ warnIfProjectDiscoveryPartial,
13
15
  writeConfig,
14
16
  writeEcosystem,
15
17
  writeWorkflowPlan
16
- } from "./chunk-RN2PACNV.js";
17
- import "./chunk-TILHWBP6.js";
18
+ } from "./chunk-JN3TQVFV.js";
19
+ import "./chunk-C67H3OUL.js";
20
+ import "./chunk-QEONJ5DZ.js";
18
21
  import "./chunk-ROGRTUFI.js";
19
22
  export {
20
23
  abortIfCancelled,
@@ -26,7 +29,9 @@ export {
26
29
  planWorkflowArtifacts,
27
30
  promptStateMappings,
28
31
  renderDryRunPreview,
32
+ resolvePriorityField,
29
33
  resolveStatusField,
34
+ warnIfProjectDiscoveryPartial,
30
35
  writeConfig,
31
36
  writeEcosystem,
32
37
  writeWorkflowPlan
@@ -3,12 +3,14 @@ import {
3
3
  project_default,
4
4
  promptProjectRegistrationOptions,
5
5
  renderProjectRegistrationSummary
6
- } from "./chunk-H2YXSYOZ.js";
7
- import "./chunk-RN2PACNV.js";
8
- import "./chunk-GKENCODJ.js";
9
- import "./chunk-6CI3UUMH.js";
10
- import "./chunk-M3IFVLQS.js";
11
- import "./chunk-TILHWBP6.js";
6
+ } from "./chunk-S6VIK4FF.js";
7
+ import "./chunk-JN3TQVFV.js";
8
+ import "./chunk-KY6WKH66.js";
9
+ import "./chunk-MYVJ6HK4.js";
10
+ import "./chunk-A67CMOYE.js";
11
+ import "./chunk-SXGT7LOF.js";
12
+ import "./chunk-C67H3OUL.js";
13
+ import "./chunk-QEONJ5DZ.js";
12
14
  import "./chunk-XN5ABWZ6.js";
13
15
  import "./chunk-MVRF7BES.js";
14
16
  import "./chunk-5NV3LSAJ.js";
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "./chunk-6CI3UUMH.js";
5
- import "./chunk-M3IFVLQS.js";
4
+ } from "./chunk-MYVJ6HK4.js";
5
+ import "./chunk-A67CMOYE.js";
6
+ import "./chunk-SXGT7LOF.js";
7
+ import "./chunk-QEONJ5DZ.js";
6
8
  import {
7
9
  resolveRuntimeRoot
8
10
  } from "./chunk-5NV3LSAJ.js";
@@ -1,12 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ GitHubRepositoryLookupError,
3
4
  GitHubScopeError,
4
5
  checkRequiredScopes,
5
6
  createClient,
6
7
  getGhToken,
7
8
  getProjectDetail,
9
+ getRepositoryMetadata,
8
10
  validateToken
9
- } from "./chunk-TILHWBP6.js";
11
+ } from "./chunk-C67H3OUL.js";
10
12
  import {
11
13
  loadActiveProjectConfig,
12
14
  loadGlobalConfig,
@@ -77,6 +79,9 @@ function displayScopeError(error) {
77
79
  function formatRepoSpec(repo) {
78
80
  return `${repo.owner}/${repo.name}`;
79
81
  }
82
+ function fallbackCloneUrl(repo) {
83
+ return `https://github.com/${repo.owner}/${repo.name}.git`;
84
+ }
80
85
  function sortRepos(repos) {
81
86
  return [...repos].sort(
82
87
  (left, right) => formatRepoSpec(left).localeCompare(formatRepoSpec(right))
@@ -151,27 +156,79 @@ async function repoAdd(args, options) {
151
156
  process.exitCode = 1;
152
157
  return;
153
158
  }
159
+ const activeProjectId = global.activeProject;
154
160
  const [owner, name] = repoSpec.split("/");
155
161
  if (!owner || !name) {
156
162
  process.stderr.write("Invalid repo format. Use: owner/name\n");
157
163
  process.exitCode = 2;
158
164
  return;
159
165
  }
160
- if (ws.repositories.some(
161
- (r) => r.owner === owner && r.name === name
162
- )) {
163
- process.stdout.write(`Repository ${repoSpec} is already configured.
166
+ const requestedRepo = { owner, name };
167
+ const addRepository = async (repo, message, warning) => {
168
+ if (ws.repositories.some((entry) => repoKey(entry) === repoKey(repo))) {
169
+ process.stdout.write(
170
+ `Repository ${formatRepoSpec(repo)} is already configured.
171
+ `
172
+ );
173
+ return;
174
+ }
175
+ ws.repositories.push(repo);
176
+ await saveProjectConfig(options.configDir, activeProjectId, ws);
177
+ if (warning) {
178
+ process.stderr.write(`${warning}
179
+ `);
180
+ }
181
+ process.stdout.write(`${message}
164
182
  `);
183
+ };
184
+ let token;
185
+ try {
186
+ token = getGhToken();
187
+ } catch {
188
+ await addRepository(
189
+ {
190
+ ...requestedRepo,
191
+ cloneUrl: fallbackCloneUrl(requestedRepo)
192
+ },
193
+ `Added repository without validation: ${formatRepoSpec(requestedRepo)}`,
194
+ "Warning: GitHub authentication is unavailable, so the repository was saved without validation. Run 'gh auth login --scopes repo,read:org,project' or set GITHUB_GRAPHQL_TOKEN to validate access before saving."
195
+ );
165
196
  return;
166
197
  }
167
- ws.repositories.push({
168
- owner,
169
- name,
170
- cloneUrl: `https://github.com/${owner}/${name}.git`
171
- });
172
- await saveProjectConfig(options.configDir, global.activeProject, ws);
173
- process.stdout.write(`Added repository: ${repoSpec}
198
+ try {
199
+ const repository = await getRepositoryMetadata(createClient(token), owner, name);
200
+ await addRepository(
201
+ {
202
+ owner: repository.owner,
203
+ name: repository.name,
204
+ cloneUrl: repository.cloneUrl || fallbackCloneUrl(repository)
205
+ },
206
+ `Added repository after validation: ${formatRepoSpec(repository)}`
207
+ );
208
+ } catch (error) {
209
+ if (error instanceof GitHubRepositoryLookupError && error.reason === "offline") {
210
+ await addRepository(
211
+ {
212
+ ...requestedRepo,
213
+ cloneUrl: fallbackCloneUrl(requestedRepo)
214
+ },
215
+ `Added repository without validation: ${formatRepoSpec(requestedRepo)}`,
216
+ `Warning: ${error.message} Saved the repository without validation. ${error.remediation}`
217
+ );
218
+ return;
219
+ }
220
+ if (error instanceof GitHubRepositoryLookupError) {
221
+ process.stderr.write(`${error.message}
222
+ ${error.remediation}
174
223
  `);
224
+ } else {
225
+ process.stderr.write(
226
+ `${error instanceof Error ? error.message : "Repository validation failed."}
227
+ `
228
+ );
229
+ }
230
+ process.exitCode = 1;
231
+ }
175
232
  }
176
233
  async function repoRemove(args, options) {
177
234
  const [repoSpec] = args;
@@ -193,8 +250,9 @@ async function repoRemove(args, options) {
193
250
  return;
194
251
  }
195
252
  const [owner, name] = repoSpec.split("/");
253
+ const requestedRepo = { owner, name };
196
254
  const idx = ws.repositories.findIndex(
197
- (r) => r.owner === owner && r.name === name
255
+ (r) => repoKey(r) === repoKey(requestedRepo)
198
256
  );
199
257
  if (idx === -1) {
200
258
  process.stderr.write(`Repository ${repoSpec} is not configured.
@@ -204,7 +262,7 @@ async function repoRemove(args, options) {
204
262
  }
205
263
  ws.repositories.splice(idx, 1);
206
264
  await saveProjectConfig(options.configDir, global.activeProject, ws);
207
- process.stdout.write(`Removed repository: ${repoSpec}
265
+ process.stdout.write(`Removed repository: ${formatRepoSpec(requestedRepo)}
208
266
  `);
209
267
  }
210
268
  async function repoSync(args, options) {
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "./chunk-6CI3UUMH.js";
5
- import "./chunk-M3IFVLQS.js";
4
+ } from "./chunk-MYVJ6HK4.js";
5
+ import "./chunk-A67CMOYE.js";
6
+ import "./chunk-SXGT7LOF.js";
7
+ import "./chunk-QEONJ5DZ.js";
6
8
  import {
7
9
  resolveRuntimeRoot
8
10
  } from "./chunk-5NV3LSAJ.js";