@gh-symphony/cli 0.4.6 → 0.4.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.
@@ -15,9 +15,10 @@ import {
15
15
  yellow
16
16
  } from "./chunk-MVRF7BES.js";
17
17
  import {
18
+ MissingWorkflowFileError,
18
19
  initRepoRuntime,
19
20
  parseRepoRuntimeFlags
20
- } from "./chunk-EILO332E.js";
21
+ } from "./chunk-7XHWAJJA.js";
21
22
  import {
22
23
  OrchestratorService,
23
24
  acquireProjectLock,
@@ -33,8 +34,11 @@ import {
33
34
  resolveOrchestratorLogLevel,
34
35
  resolveTrackerAdapter,
35
36
  runCli
36
- } from "./chunk-RMNLHTIK.js";
37
- import "./chunk-XLDJTMW5.js";
37
+ } from "./chunk-D7HICFZ5.js";
38
+ import {
39
+ writeCliError
40
+ } from "./chunk-MAJOIZ5Q.js";
41
+ import "./chunk-LSH5HRQT.js";
38
42
  import {
39
43
  resolveRepoRuntimeRoot,
40
44
  resolveRuntimeRoot
@@ -48,7 +52,8 @@ import {
48
52
  resolveGitHubAuth,
49
53
  runGhAuthLogin,
50
54
  runGhAuthRefresh
51
- } from "./chunk-SMNIGNS3.js";
55
+ } from "./chunk-FFY5VKNV.js";
56
+ import "./chunk-PKUD2SVX.js";
52
57
  import {
53
58
  WorkflowConfigStore,
54
59
  deriveIssueWorkspaceKeyFromIdentifier,
@@ -59,8 +64,9 @@ import {
59
64
  parseRecentEvents,
60
65
  readJsonFile,
61
66
  safeReadDir
62
- } from "./chunk-RGCSM2KZ.js";
67
+ } from "./chunk-77H5ED5L.js";
63
68
  import {
69
+ configFilePath,
64
70
  daemonPidPath,
65
71
  httpStatusPath,
66
72
  loadActiveProjectConfig,
@@ -68,11 +74,15 @@ import {
68
74
  writeJsonFile
69
75
  } from "./chunk-YZP5N5XP.js";
70
76
 
77
+ // src/commands/repo.ts
78
+ import { existsSync } from "fs";
79
+
71
80
  // src/commands/logs.ts
72
81
  import { readFile, readdir, stat } from "fs/promises";
73
82
  import { join, resolve } from "path";
74
83
  import { createReadStream } from "fs";
75
84
  import { createInterface } from "readline";
85
+ var LOG_LEVELS = ["error", "warn", "info"];
76
86
  function parseLogsArgs(args) {
77
87
  const parsed = { follow: false };
78
88
  for (let i = 0; i < args.length; i += 1) {
@@ -101,6 +111,17 @@ function parseLogsArgs(args) {
101
111
  }
102
112
  var handler = async (args, options) => {
103
113
  const parsed = parseLogsArgs(args);
114
+ const level = parseLogLevel(parsed.level);
115
+ if (parsed.level && !level) {
116
+ process.stderr.write(
117
+ `Unknown --level "${parsed.level}". Valid values: ${LOG_LEVELS.join(
118
+ ", "
119
+ )}.
120
+ `
121
+ );
122
+ process.exitCode = 1;
123
+ return;
124
+ }
104
125
  const runtimeRoot = resolve(options.configDir);
105
126
  if (parsed.run) {
106
127
  const eventsPath = parsed.projectId ? join(
@@ -120,14 +141,19 @@ var handler = async (args, options) => {
120
141
  try {
121
142
  const content = await readFile(eventsPath, "utf8");
122
143
  const lines = content.trim().split("\n").filter(Boolean);
144
+ let matchedEvents2 = 0;
123
145
  for (const line of lines) {
124
146
  const event = JSON.parse(line);
125
147
  if (parsed.projectId && getProjectId(event) !== parsed.projectId)
126
148
  continue;
127
- if (parsed.level && getLevel(event) !== parsed.level) continue;
149
+ if (level && getLevel(event) !== level) continue;
128
150
  if (parsed.issue && getIssueIdentifier(event) !== parsed.issue)
129
151
  continue;
130
152
  process.stdout.write(formatEvent(event) + "\n");
153
+ matchedEvents2 += 1;
154
+ }
155
+ if (level && matchedEvents2 === 0) {
156
+ process.stderr.write(noMatchedEventsMessage(level));
131
157
  }
132
158
  } catch {
133
159
  process.stderr.write(`No events found for run: ${parsed.run}
@@ -180,6 +206,7 @@ var handler = async (args, options) => {
180
206
  }
181
207
  const runRoots = parsed.projectId ? [join(runtimeRoot, "projects", parsed.projectId, "runs")] : await listProjectRunRoots(runtimeRoot);
182
208
  let foundRuns = false;
209
+ let matchedEvents = 0;
183
210
  const events = [];
184
211
  try {
185
212
  for (const runsDir of runRoots) {
@@ -197,10 +224,11 @@ var handler = async (args, options) => {
197
224
  const event = JSON.parse(line);
198
225
  if (parsed.projectId && getProjectId(event) !== parsed.projectId)
199
226
  continue;
200
- if (parsed.level && getLevel(event) !== parsed.level) continue;
227
+ if (level && getLevel(event) !== level) continue;
201
228
  if (parsed.issue && getIssueIdentifier(event) !== parsed.issue)
202
229
  continue;
203
230
  events.push(event);
231
+ matchedEvents += 1;
204
232
  }
205
233
  } catch {
206
234
  }
@@ -212,6 +240,10 @@ var handler = async (args, options) => {
212
240
  process.stderr.write("No runs found. Start the orchestrator first.\n");
213
241
  return;
214
242
  }
243
+ if (level && matchedEvents === 0) {
244
+ process.stderr.write(noMatchedEventsMessage(level));
245
+ return;
246
+ }
215
247
  events.sort(
216
248
  (left, right) => String(left.at ?? "").localeCompare(String(right.at ?? ""))
217
249
  );
@@ -233,7 +265,28 @@ function getProjectId(event) {
233
265
  return typeof event.projectId === "string" ? event.projectId : void 0;
234
266
  }
235
267
  function getLevel(event) {
236
- return typeof event.level === "string" ? event.level : void 0;
268
+ switch (event.event) {
269
+ case "run-failed":
270
+ case "turn_failed":
271
+ case "worker-error":
272
+ case "hook-failed":
273
+ return "error";
274
+ case "run-suppressed":
275
+ case "run-retried":
276
+ return "warn";
277
+ default:
278
+ return "info";
279
+ }
280
+ }
281
+ function parseLogLevel(level) {
282
+ if (!level) {
283
+ return void 0;
284
+ }
285
+ return LOG_LEVELS.includes(level) ? level : void 0;
286
+ }
287
+ function noMatchedEventsMessage(level) {
288
+ return `No events matched the provided filters (--level ${level}).
289
+ `;
237
290
  }
238
291
  function getIssueIdentifier(event) {
239
292
  return typeof event.issueIdentifier === "string" ? event.issueIdentifier : "";
@@ -429,20 +482,22 @@ function parseRepoExplainFlags(args) {
429
482
  var handler3 = async (args, options) => {
430
483
  const parsed = parseRepoExplainFlags(args);
431
484
  if (parsed.error) {
432
- process.stderr.write(`${parsed.error}
433
- `);
434
- process.stderr.write(
435
- "Usage: gh-symphony repo explain <owner/repo#number> [--workflow <path>]\n"
436
- );
437
- process.exitCode = 2;
485
+ writeCliError({
486
+ code: "invalid_arguments",
487
+ message: parsed.error,
488
+ usage: "Usage: gh-symphony repo explain <owner/repo#number> [--workflow <path>]",
489
+ json: options.json,
490
+ exitCode: 2
491
+ });
438
492
  return;
439
493
  }
440
494
  const projectConfig = await loadActiveProjectConfig(options.configDir);
441
495
  if (!projectConfig) {
442
- process.stderr.write(
443
- "No repository runtime configured. Run 'gh-symphony repo init' in the target repository.\n"
444
- );
445
- process.exitCode = 1;
496
+ writeCliError({
497
+ code: "missing_repository_runtime_config",
498
+ message: "No repository runtime configured. Run 'gh-symphony repo init' in the target repository.",
499
+ json: options.json
500
+ });
446
501
  return;
447
502
  }
448
503
  const identifier = parsed.identifier;
@@ -458,14 +513,12 @@ var handler3 = async (args, options) => {
458
513
  token = getGhToken();
459
514
  } catch (error) {
460
515
  if (error instanceof GhAuthError) {
461
- process.stderr.write(
462
- `Error: GitHub authentication is required for repo explain. ${error.message}
463
- `
464
- );
465
- process.stderr.write(
466
- "Run 'gh auth login --scopes repo,read:org,project' or set GITHUB_GRAPHQL_TOKEN, then re-run this command.\n"
467
- );
468
- process.exitCode = 2;
516
+ writeCliError({
517
+ code: "github_auth_required",
518
+ message: `Error: GitHub authentication is required for repo explain. ${error.message} Run 'gh auth login --scopes repo,read:org,project' or set GITHUB_GRAPHQL_TOKEN, then re-run this command.`,
519
+ json: options.json,
520
+ exitCode: 2
521
+ });
469
522
  return;
470
523
  }
471
524
  throw error;
@@ -513,12 +566,12 @@ var handler3 = async (args, options) => {
513
566
  });
514
567
  } catch (error) {
515
568
  if (error instanceof RepoExplainWorkflowError) {
516
- process.stderr.write(`Error: ${error.message}
517
- `);
518
- process.stderr.write(
519
- "Hint: pass --workflow <path-to-WORKFLOW.md> or run 'gh-symphony workflow preview --file <path>' to verify the workflow file.\n"
520
- );
521
- process.exitCode = 2;
569
+ writeCliError({
570
+ code: "workflow_load_failed",
571
+ message: `Error: ${error.message} Hint: pass --workflow <path-to-WORKFLOW.md> or run 'gh-symphony workflow preview --file <path>' to verify the workflow file.`,
572
+ json: options.json,
573
+ exitCode: 2
574
+ });
522
575
  return;
523
576
  }
524
577
  throw error;
@@ -723,22 +776,31 @@ function parseRunArgs(args) {
723
776
  var handler4 = async (args, options) => {
724
777
  const parsed = parseRunArgs(args);
725
778
  if (parsed.error) {
726
- process.stderr.write(`${parsed.error}
727
- `);
728
- process.exitCode = 2;
779
+ writeCliError({
780
+ code: "invalid_arguments",
781
+ message: parsed.error,
782
+ json: options.json,
783
+ exitCode: 2
784
+ });
729
785
  return;
730
786
  }
731
787
  if (!parsed.issue) {
732
- process.stderr.write("Usage: gh-symphony repo run <owner/repo#number>\n");
733
- process.exitCode = 2;
788
+ writeCliError({
789
+ code: "invalid_arguments",
790
+ message: "Issue identifier argument missing",
791
+ usage: "Usage: gh-symphony repo run <owner/repo#number>",
792
+ json: options.json,
793
+ exitCode: 2
794
+ });
734
795
  return;
735
796
  }
736
797
  const projectConfig = await resolveManagedProjectConfig({
737
798
  configDir: options.configDir,
738
- requestedProjectId: parsed.projectId
799
+ requestedProjectId: parsed.projectId,
800
+ json: options.json
739
801
  });
740
802
  if (!projectConfig) {
741
- handleMissingManagedProjectConfig();
803
+ handleMissingManagedProjectConfig({ json: options.json });
742
804
  return;
743
805
  }
744
806
  const runtimeRoot = resolveRuntimeRoot(options.configDir);
@@ -749,19 +811,19 @@ var handler4 = async (args, options) => {
749
811
  ).map((repository) => `${repository.owner}/${repository.name}`);
750
812
  const configuredRepoSet = new Set(configuredRepos);
751
813
  if (configuredRepoSet.size === 0) {
752
- process.stderr.write(
753
- "No repository is configured in this project. Run 'gh-symphony repo init' from the target repository first.\n"
754
- );
755
- process.exitCode = 1;
814
+ writeCliError({
815
+ code: "repository_not_configured",
816
+ message: "No repository is configured in this project. Run 'gh-symphony repo init' from the target repository first.",
817
+ json: options.json
818
+ });
756
819
  return;
757
820
  }
758
821
  if (repoSpec && !configuredRepoSet.has(repoSpec)) {
759
- process.stderr.write(
760
- `Repository "${repoSpec}" is not configured in this project.
761
- Configured repo: ${configuredRepos.join(", ")}
762
- `
763
- );
764
- process.exitCode = 1;
822
+ writeCliError({
823
+ code: "repository_mismatch",
824
+ message: `Repository "${repoSpec}" is not configured in this project. Configured repo: ${configuredRepos.join(", ")}`,
825
+ json: options.json
826
+ });
765
827
  return;
766
828
  }
767
829
  process.stdout.write(`Dispatching issue: ${parsed.issue}
@@ -1924,10 +1986,9 @@ var handler5 = async (args, options) => {
1924
1986
  const runtimeRoot = resolveRuntimeRoot(options.configDir);
1925
1987
  const projectId = projectConfig.projectId;
1926
1988
  let logLevel;
1989
+ const requestedLogLevel = parsed.logLevel ?? (options.verbose ? "verbose" : process.env.SYMPHONY_LOG_LEVEL);
1927
1990
  try {
1928
- logLevel = resolveOrchestratorLogLevel(
1929
- parsed.logLevel ?? process.env.SYMPHONY_LOG_LEVEL
1930
- );
1991
+ logLevel = resolveOrchestratorLogLevel(requestedLogLevel);
1931
1992
  } catch (error) {
1932
1993
  process.stderr.write(
1933
1994
  `${error instanceof Error ? error.message : "Unsupported log level"}
@@ -1946,7 +2007,7 @@ var handler5 = async (args, options) => {
1946
2007
  await startDaemon(
1947
2008
  options,
1948
2009
  projectId,
1949
- parsed.logLevel,
2010
+ parsed.logLevel ?? (options.verbose ? "verbose" : void 0),
1950
2011
  parsed.httpPort,
1951
2012
  parsed.webPort,
1952
2013
  parsed.assignedOnly === true
@@ -2089,7 +2150,7 @@ var handler5 = async (args, options) => {
2089
2150
  if (httpServer) {
2090
2151
  logLine(
2091
2152
  cyan("\u25A1"),
2092
- parsed.webPort !== void 0 ? `Web dashboard listening on ${httpServer.url}` : `HTTP dashboard listening on ${httpServer.url}`
2153
+ parsed.webPort !== void 0 ? `Web dashboard listening on ${httpServer.url}` : `HTTP status API listening on ${httpServer.url}`
2093
2154
  );
2094
2155
  }
2095
2156
  logLine(
@@ -2108,7 +2169,7 @@ var handler5 = async (args, options) => {
2108
2169
  if (httpServer) {
2109
2170
  logLine(
2110
2171
  cyan("\u25A1"),
2111
- parsed.webPort !== void 0 ? "One-shot tick completed; web dashboard remains available until Ctrl+C" : "One-shot tick completed; HTTP dashboard remains available until Ctrl+C"
2172
+ parsed.webPort !== void 0 ? "One-shot tick completed; web dashboard remains available until Ctrl+C" : "One-shot tick completed; HTTP status API remains available until Ctrl+C"
2112
2173
  );
2113
2174
  if (shuttingDown) {
2114
2175
  break;
@@ -2244,6 +2305,7 @@ async function startDaemon(options, projectId, logLevel, httpPort, webPort, assi
2244
2305
  process.argv[1],
2245
2306
  "repo",
2246
2307
  "start",
2308
+ ...options.verbose ? ["--verbose"] : [],
2247
2309
  ...assignedOnly ? ["--assigned-only"] : [],
2248
2310
  ...httpPort !== void 0 ? ["--http", String(httpPort)] : [],
2249
2311
  ...webPort !== void 0 ? ["--web", String(webPort)] : [],
@@ -2659,18 +2721,22 @@ var handler6 = async (args, options) => {
2659
2721
  }
2660
2722
  const parsed = parseStatusArgs(args);
2661
2723
  if (parsed.error) {
2662
- process.stderr.write(`${parsed.error}
2663
- `);
2664
- process.stderr.write("Usage: gh-symphony repo status [--watch]\n");
2665
- process.exitCode = 2;
2724
+ writeCliError({
2725
+ code: "invalid_arguments",
2726
+ message: parsed.error,
2727
+ usage: "Usage: gh-symphony repo status [--watch]",
2728
+ json: options.json,
2729
+ exitCode: 2
2730
+ });
2666
2731
  return;
2667
2732
  }
2668
2733
  const projectConfig = await resolveManagedProjectConfig({
2669
2734
  configDir: options.configDir,
2670
- requestedProjectId: void 0
2735
+ requestedProjectId: void 0,
2736
+ json: options.json
2671
2737
  });
2672
2738
  if (!projectConfig) {
2673
- handleMissingManagedProjectConfig();
2739
+ handleMissingManagedProjectConfig({ json: options.json });
2674
2740
  return;
2675
2741
  }
2676
2742
  const runtimeRoot = resolveRuntimeRoot(options.configDir);
@@ -2735,8 +2801,11 @@ var handler6 = async (args, options) => {
2735
2801
  );
2736
2802
  }
2737
2803
  } else {
2738
- process.stderr.write("Unable to read status snapshot.\n");
2739
- process.exitCode = 1;
2804
+ writeCliError({
2805
+ code: "status_snapshot_unavailable",
2806
+ message: "Unable to read status snapshot.",
2807
+ json: options.json
2808
+ });
2740
2809
  }
2741
2810
  };
2742
2811
  var status_default = handler6;
@@ -2830,6 +2899,7 @@ var handler7 = async (args, options) => {
2830
2899
  var stop_default = handler7;
2831
2900
 
2832
2901
  // src/commands/repo.ts
2902
+ var DOCKER_DEFAULT_CONFIG_DIR = "/var/lib/gh-symphony";
2833
2903
  var handler8 = async (args, options) => {
2834
2904
  const [subcommand, ...rest] = args;
2835
2905
  switch (subcommand) {
@@ -2873,11 +2943,21 @@ var handler8 = async (args, options) => {
2873
2943
  };
2874
2944
  var repo_default = handler8;
2875
2945
  function repoOptions(options) {
2946
+ const repoRuntimeRoot = resolveRepoRuntimeRoot();
2876
2947
  return {
2877
2948
  ...options,
2878
- configDir: resolveRepoRuntimeRoot()
2949
+ configDir: shouldUseConfigDirOverride(options, repoRuntimeRoot) ? options.configDir : repoRuntimeRoot
2879
2950
  };
2880
2951
  }
2952
+ function shouldUseConfigDirOverride(options, repoRuntimeRoot) {
2953
+ if (!options.configDirOverride) {
2954
+ return false;
2955
+ }
2956
+ if (options.configDirSource === "env" && options.configDir === DOCKER_DEFAULT_CONFIG_DIR && existsSync(configFilePath(repoRuntimeRoot))) {
2957
+ return false;
2958
+ }
2959
+ return true;
2960
+ }
2881
2961
  async function repoInit(args, options) {
2882
2962
  if (rejectRemovedProjectId(args)) {
2883
2963
  return;
@@ -2886,14 +2966,13 @@ async function repoInit(args, options) {
2886
2966
  try {
2887
2967
  flags = parseRepoRuntimeFlags(args);
2888
2968
  } catch (error) {
2889
- process.stderr.write(
2890
- `${error instanceof Error ? error.message : "Invalid arguments"}
2891
- `
2892
- );
2893
- process.stderr.write(
2894
- "Usage: gh-symphony repo init [--repo-dir <path>] [--workflow-file <path>]\n"
2895
- );
2896
- process.exitCode = 2;
2969
+ writeCliError({
2970
+ code: "invalid_arguments",
2971
+ message: error instanceof Error ? error.message : "Invalid arguments",
2972
+ usage: "Usage: gh-symphony repo init [--repo-dir <path>] [--workflow-file <path>]",
2973
+ json: options.json,
2974
+ exitCode: 2
2975
+ });
2897
2976
  return;
2898
2977
  }
2899
2978
  try {
@@ -2910,16 +2989,17 @@ async function repoInit(args, options) {
2910
2989
  ].join("\n") + "\n"
2911
2990
  );
2912
2991
  } catch (error) {
2913
- process.stderr.write(
2914
- `${error instanceof Error ? error.message : "Repository initialization failed."}
2915
- `
2916
- );
2917
- process.exitCode = 1;
2992
+ writeCliError({
2993
+ code: error instanceof MissingWorkflowFileError ? "missing_workflow_file" : "repository_initialization_failed",
2994
+ message: error instanceof Error ? error.message : "Repository initialization failed.",
2995
+ json: options.json
2996
+ });
2918
2997
  }
2919
2998
  }
2920
2999
  function formatRepoSpec(repo) {
2921
3000
  return `${repo.owner}/${repo.name}`;
2922
3001
  }
2923
3002
  export {
2924
- repo_default as default
3003
+ repo_default as default,
3004
+ repoOptions
2925
3005
  };