@gh-symphony/cli 0.0.22 → 0.1.3

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.
Files changed (36) hide show
  1. package/README.md +72 -77
  2. package/dist/{chunk-HMLBBZNY.js → chunk-2YF7PQUC.js} +16 -71
  3. package/dist/{chunk-IWFX2FMA.js → chunk-6I753NYO.js} +4 -1
  4. package/dist/{chunk-2TSM3INR.js → chunk-HQ7A3C7K.js} +575 -12
  5. package/dist/{chunk-36KYEDEO.js → chunk-MVRF7BES.js} +1 -10
  6. package/dist/{workflow-L3KT6HB7.js → chunk-NESHTYXQ.js} +27 -19
  7. package/dist/{chunk-2UW7NQLX.js → chunk-PEZUBHWJ.js} +1 -1
  8. package/dist/chunk-PG332ZS4.js +238 -0
  9. package/dist/{chunk-EEQQWTXS.js → chunk-WCOIVNHH.js} +213 -82
  10. package/dist/{chunk-QIRE2VXS.js → chunk-WOVNN5NW.js} +16 -17
  11. package/dist/{chunk-C67H3OUL.js → chunk-Z3NZOPLZ.js} +0 -81
  12. package/dist/{config-cmd-Z3A7V6NC.js → config-cmd-2ADPUYWA.js} +1 -1
  13. package/dist/{doctor-EJUMPBMW.js → doctor-2AXHIEAP.js} +464 -40
  14. package/dist/index.js +340 -294
  15. package/dist/{chunk-PUDXVBSN.js → repo-SUXYT4OK.js} +6272 -2996
  16. package/dist/{setup-TZJSM3QV.js → setup-UBHOMXUG.js} +57 -92
  17. package/dist/{upgrade-O33S2SJK.js → upgrade-355SQJ5P.js} +2 -2
  18. package/dist/{version-CW54Q7BK.js → version-4ILSDZQH.js} +1 -1
  19. package/dist/worker-entry.js +10 -5
  20. package/dist/workflow-S6YSZPQT.js +22 -0
  21. package/package.json +4 -4
  22. package/dist/chunk-DDL4BWSL.js +0 -146
  23. package/dist/chunk-DFLXHNYQ.js +0 -482
  24. package/dist/chunk-E7HYEEZD.js +0 -1318
  25. package/dist/chunk-GDE6FYN4.js +0 -26
  26. package/dist/chunk-GSX2FV3M.js +0 -103
  27. package/dist/chunk-ZHOKYUO3.js +0 -1047
  28. package/dist/init-54HMKNYI.js +0 -38
  29. package/dist/logs-GTZ4U5JE.js +0 -188
  30. package/dist/project-RMYMZSFV.js +0 -25
  31. package/dist/recover-LTLKMTRX.js +0 -133
  32. package/dist/repo-WI7GF6XQ.js +0 -749
  33. package/dist/run-IHN3ZL35.js +0 -122
  34. package/dist/start-RTAHQMR2.js +0 -19
  35. package/dist/status-F4D52OVK.js +0 -12
  36. package/dist/stop-MDKMJPVR.js +0 -10
@@ -1,26 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- promptProjectRegistrationOptions,
4
- renderProjectRegistrationSummary
5
- } from "./chunk-ZHOKYUO3.js";
6
2
  import {
7
3
  abortIfCancelled,
8
4
  buildAutomaticStateMappings,
9
- generateProjectId,
10
5
  planWorkflowArtifacts,
11
6
  promptStateMappings,
12
7
  renderDryRunPreview,
13
8
  resolvePriorityField,
14
9
  resolveStatusField,
15
10
  validateStateMapping,
16
- writeConfig,
17
11
  writeEcosystem,
18
12
  writeWorkflowPlan
19
- } from "./chunk-HMLBBZNY.js";
20
- import "./chunk-E7HYEEZD.js";
21
- import "./chunk-PUDXVBSN.js";
22
- import "./chunk-2UW7NQLX.js";
23
- import "./chunk-2TSM3INR.js";
13
+ } from "./chunk-2YF7PQUC.js";
14
+ import {
15
+ initRepoRuntime
16
+ } from "./chunk-PG332ZS4.js";
17
+ import "./chunk-6I753NYO.js";
24
18
  import {
25
19
  GhAuthError,
26
20
  GitHubScopeError,
@@ -31,19 +25,13 @@ import {
31
25
  getProjectDetail,
32
26
  listUserProjects,
33
27
  validateToken
34
- } from "./chunk-C67H3OUL.js";
35
- import "./chunk-EEQQWTXS.js";
36
- import "./chunk-DFLXHNYQ.js";
37
- import "./chunk-36KYEDEO.js";
38
- import "./chunk-IWFX2FMA.js";
39
- import "./chunk-GSX2FV3M.js";
40
- import "./chunk-GDE6FYN4.js";
41
- import "./chunk-DDL4BWSL.js";
42
- import "./chunk-QIRE2VXS.js";
28
+ } from "./chunk-Z3NZOPLZ.js";
29
+ import "./chunk-WCOIVNHH.js";
30
+ import "./chunk-WOVNN5NW.js";
43
31
 
44
32
  // src/commands/setup.ts
45
33
  import * as p from "@clack/prompts";
46
- import { join, resolve } from "path";
34
+ import { resolve } from "path";
47
35
  var KNOWN_REQUIRED_SCOPES = ["repo", "read:org", "project"];
48
36
  function parseSetupFlags(args) {
49
37
  const flags = {
@@ -58,14 +46,6 @@ function parseSetupFlags(args) {
58
46
  case "--non-interactive":
59
47
  flags.nonInteractive = true;
60
48
  break;
61
- case "--project":
62
- flags.project = next;
63
- i += 1;
64
- break;
65
- case "--workspace-dir":
66
- flags.workspaceDir = next;
67
- i += 1;
68
- break;
69
49
  case "--assigned-only":
70
50
  flags.assignedOnly = true;
71
51
  break;
@@ -79,6 +59,12 @@ function parseSetupFlags(args) {
79
59
  case "--skip-context":
80
60
  flags.skipContext = true;
81
61
  break;
62
+ default:
63
+ if (arg?.startsWith("-")) {
64
+ throw new Error(
65
+ `Unknown option '${arg}'. Removed project/workspace flags are no longer supported; run 'gh-symphony setup' from inside the target repository. Supported flags: --non-interactive, --assigned-only, --output, --skip-skills, --skip-context.`
66
+ );
67
+ }
82
68
  }
83
69
  }
84
70
  return flags;
@@ -98,26 +84,19 @@ Then re-run: ${retryCommand}`,
98
84
  "Fix missing scope"
99
85
  );
100
86
  }
101
- async function resolveProjectDetail(client, projectArg) {
87
+ async function resolveProjectDetail(client) {
102
88
  const projects = await listUserProjects(client);
103
89
  if (projects.length === 0) {
104
90
  throw new Error(
105
91
  "No GitHub Projects found. Create a project first and re-run setup."
106
92
  );
107
93
  }
108
- if (projectArg) {
109
- const match = projects.find(
110
- (project) => project.id === projectArg || project.url === projectArg
111
- );
112
- if (!match) {
113
- throw new Error(`Project not found: ${projectArg}`);
114
- }
115
- return getProjectDetail(client, match.id);
116
- }
117
94
  if (projects.length === 1) {
118
95
  return getProjectDetail(client, projects[0].id);
119
96
  }
120
- throw new Error("Error: --project is required when multiple projects exist.");
97
+ throw new Error(
98
+ "Error: non-interactive setup requires exactly one GitHub Project. Use 'gh-symphony workflow init' for project selection, then run 'gh-symphony repo init'."
99
+ );
121
100
  }
122
101
  async function selectProjectSummary(client) {
123
102
  const projects = await listUserProjects(client);
@@ -139,22 +118,29 @@ async function selectProjectSummary(client) {
139
118
  );
140
119
  return projects.find((project) => project.id === selectedProjectId);
141
120
  }
142
- function formatRepoSummary(projectDetail, selectedRepos) {
143
- return selectedRepos.length === projectDetail.linkedRepositories.length ? `${selectedRepos.map((repo) => `${repo.owner}/${repo.name}`).join(", ")} (all ${selectedRepos.length} linked)` : `${selectedRepos.map((repo) => `${repo.owner}/${repo.name}`).join(", ")} (${selectedRepos.length} of ${projectDetail.linkedRepositories.length} linked)`;
144
- }
145
121
  function printNonInteractiveSummary(input) {
146
122
  process.stdout.write(
147
123
  [
148
124
  `GitHub Project ${input.githubProjectTitle} (${input.githubProjectId})`,
149
- `Managed project ${input.projectId}`,
125
+ `Repository ${input.repository}`,
150
126
  `WORKFLOW.md ${input.workflowPath}`,
151
- `Workspace root ${input.workspaceDir}`,
152
- "Ready. Run 'gh-symphony start' to begin orchestration."
127
+ `Runtime ${input.runtimeDir}`,
128
+ "Ready. Run 'gh-symphony repo start' to begin orchestration."
153
129
  ].map((line) => ` ${line}`).join("\n") + "\n"
154
130
  );
155
131
  }
156
132
  var handler = async (args, options) => {
157
- const flags = parseSetupFlags(args);
133
+ let flags;
134
+ try {
135
+ flags = parseSetupFlags(args);
136
+ } catch (error) {
137
+ process.stderr.write(
138
+ `${error instanceof Error ? error.message : "Invalid setup arguments"}
139
+ `
140
+ );
141
+ process.exitCode = 2;
142
+ return;
143
+ }
158
144
  if (flags.nonInteractive) {
159
145
  await runNonInteractive(flags, options);
160
146
  return;
@@ -193,7 +179,7 @@ async function runNonInteractive(flags, options) {
193
179
  }
194
180
  let projectDetail;
195
181
  try {
196
- projectDetail = await resolveProjectDetail(client, flags.project);
182
+ projectDetail = await resolveProjectDetail(client);
197
183
  } catch (error) {
198
184
  process.stderr.write(
199
185
  `${error instanceof Error ? error.message : "Unknown error"}
@@ -248,13 +234,9 @@ Run setup without --non-interactive for manual mapping.
248
234
  skipSkills: flags.skipSkills,
249
235
  skipContext: flags.skipContext
250
236
  });
251
- const projectId = generateProjectId(projectDetail.title, projectDetail.id);
252
- const workspaceDir = flags.workspaceDir ?? join(options.configDir, "workspaces");
253
- await writeConfig(options.configDir, {
254
- projectId,
255
- project: projectDetail,
256
- repos: projectDetail.linkedRepositories,
257
- workspaceDir,
237
+ const runtime = await initRepoRuntime({
238
+ repoDir: process.cwd(),
239
+ workflowFile: workflowPath,
258
240
  assignedOnly: flags.assignedOnly
259
241
  });
260
242
  if (options.json) {
@@ -262,21 +244,22 @@ Run setup without --non-interactive for manual mapping.
262
244
  JSON.stringify({
263
245
  status: "created",
264
246
  output: workflowPath,
265
- projectId,
247
+ runtimeDir: runtime.configDir,
248
+ repository: `${runtime.repository.owner}/${runtime.repository.name}`,
266
249
  githubProjectId: projectDetail.id
267
250
  }) + "\n"
268
251
  );
269
252
  return;
270
253
  }
271
254
  printNonInteractiveSummary({
272
- projectId,
273
255
  githubProjectTitle: projectDetail.title,
274
256
  githubProjectId: projectDetail.id,
275
257
  workflowPath,
276
- workspaceDir
258
+ runtimeDir: runtime.configDir,
259
+ repository: `${runtime.repository.owner}/${runtime.repository.name}`
277
260
  });
278
261
  }
279
- async function runInteractive(flags, options) {
262
+ async function runInteractive(flags, _options) {
280
263
  p.intro("gh-symphony \u2014 One-command Setup");
281
264
  const authSpinner = p.spinner();
282
265
  authSpinner.start("Checking gh CLI authentication...");
@@ -333,13 +316,6 @@ async function runInteractive(flags, options) {
333
316
  process.exitCode = 1;
334
317
  return;
335
318
  }
336
- if (projectDetail.linkedRepositories.length === 0) {
337
- p.log.error(
338
- "No linked repositories found in this project. Add issues from repositories to the project first."
339
- );
340
- process.exitCode = 1;
341
- return;
342
- }
343
319
  const statusField = resolveStatusField(projectDetail);
344
320
  if (!statusField) {
345
321
  p.log.error(
@@ -387,16 +363,12 @@ async function runInteractive(flags, options) {
387
363
  }
388
364
  return priorityResolution.ambiguous.find((field) => field.id === selectedId) ?? null;
389
365
  })() : priorityResolution.field;
390
- const {
391
- assignedOnly: promptAssignedOnly,
392
- selectedRepos,
393
- workspaceDir
394
- } = await promptProjectRegistrationOptions({
395
- projectDetail,
396
- defaultWorkspaceDir: flags.workspaceDir ?? join(options.configDir, "workspaces"),
397
- assignedOnlyMessage: `${priorityResolution.ambiguous.length > 0 ? "Step 4/4" : "Step 3/3"} \u2014 Only process issues assigned to the authenticated GitHub user?`,
398
- assignedOnlyInitialValue: flags.assignedOnly
399
- });
366
+ const promptAssignedOnly = await abortIfCancelled(
367
+ p.confirm({
368
+ message: `${priorityResolution.ambiguous.length > 0 ? "Step 4/4" : "Step 3/3"} \u2014 Only process issues assigned to the authenticated GitHub user?`,
369
+ initialValue: flags.assignedOnly ?? false
370
+ })
371
+ );
400
372
  const assignedOnly = flags.assignedOnly || promptAssignedOnly;
401
373
  const workflowPath = resolve(flags.output ?? "WORKFLOW.md");
402
374
  const { workflowPlan, ecosystemPlan } = await planWorkflowArtifacts({
@@ -410,16 +382,12 @@ async function runInteractive(flags, options) {
410
382
  skipSkills: flags.skipSkills,
411
383
  skipContext: flags.skipContext
412
384
  });
413
- const projectId = generateProjectId(projectDetail.title, projectDetail.id);
414
385
  p.note(
415
386
  [
416
- renderProjectRegistrationSummary({
417
- login,
418
- projectTitle: projectDetail.title,
419
- repoSummary: formatRepoSummary(projectDetail, selectedRepos),
420
- assignedOnly,
421
- workspaceDir
422
- }),
387
+ `GitHub Project: ${projectDetail.title}`,
388
+ `Authenticated: ${login}`,
389
+ `Repository: current working directory`,
390
+ `Assigned: ${assignedOnly ? `Only issues assigned to ${login}` : "All project issues"}`,
423
391
  "",
424
392
  renderDryRunPreview(workflowPath, workflowPlan, ecosystemPlan).trimEnd()
425
393
  ].join("\n"),
@@ -446,14 +414,12 @@ async function runInteractive(flags, options) {
446
414
  skipSkills: flags.skipSkills,
447
415
  skipContext: flags.skipContext
448
416
  });
449
- await writeConfig(options.configDir, {
450
- projectId,
451
- project: projectDetail,
452
- repos: selectedRepos,
453
- workspaceDir,
417
+ const runtime = await initRepoRuntime({
418
+ repoDir: process.cwd(),
419
+ workflowFile: workflowPath,
454
420
  assignedOnly
455
421
  });
456
- writeSpinner.stop("Setup saved.");
422
+ writeSpinner.stop(`Setup saved for ${runtime.repository.owner}/${runtime.repository.name}.`);
457
423
  } catch (error) {
458
424
  writeSpinner.stop("Setup failed.");
459
425
  p.log.error(error instanceof Error ? error.message : "Unknown error");
@@ -461,8 +427,7 @@ async function runInteractive(flags, options) {
461
427
  return;
462
428
  }
463
429
  p.outro(
464
- `Project "${projectId}" is ready.
465
- Run 'gh-symphony start' to begin orchestration.`
430
+ "Repository runtime is ready.\n Run 'gh-symphony repo start' to begin orchestration."
466
431
  );
467
432
  }
468
433
  export {
@@ -16,8 +16,8 @@ function execFileAsync(file, args, execFileImpl = execFileCallback) {
16
16
  });
17
17
  }
18
18
  function resolveCurrentCliVersion() {
19
- if ("0.0.22".length > 0) {
20
- return "0.0.22";
19
+ if ("0.1.3".length > 0) {
20
+ return "0.1.3";
21
21
  }
22
22
  const pkg = JSON.parse(
23
23
  readFileSync(new URL("../../package.json", import.meta.url), "utf8")
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/commands/version.ts
4
4
  var handler = async (_args, options) => {
5
- const version = "0.0.22";
5
+ const version = "0.1.3";
6
6
  if (options.json) {
7
7
  process.stdout.write(JSON.stringify({ version }) + "\n");
8
8
  } else {
@@ -6,19 +6,18 @@ import {
6
6
  normalizeCodexRuntimeEvents,
7
7
  prepareCodexRuntimePlan,
8
8
  resolveLocalRuntimeLaunchConfig
9
- } from "./chunk-2UW7NQLX.js";
9
+ } from "./chunk-PEZUBHWJ.js";
10
10
  import {
11
11
  DEFAULT_AGENT_INPUT_REQUIRED_REASON,
12
12
  classifySessionExit,
13
13
  createClaudePrintRuntimeAdapter,
14
14
  extractEnvForClaude,
15
15
  formatClaudePreflightText,
16
- isClaudeRuntimeCommand,
17
16
  parseWorkflowMarkdown,
18
17
  resolveClaudeCommandBinary,
19
18
  resolveWorkflowRuntimeCommand,
20
19
  runClaudePreflight
21
- } from "./chunk-EEQQWTXS.js";
20
+ } from "./chunk-WCOIVNHH.js";
22
21
 
23
22
  // ../worker/src/index.ts
24
23
  import { spawn as spawn2 } from "child_process";
@@ -295,6 +294,9 @@ function resolveWorkerRuntimeRoute(workflow) {
295
294
  }
296
295
  return "runtime-adapter";
297
296
  }
297
+ function resolveClaudePreflightAuthMode(workflow) {
298
+ return workflow.runtime?.isolation.bare === true ? "api-key-required" : "local-or-api-key";
299
+ }
298
300
 
299
301
  // ../worker/src/thread-resume.ts
300
302
  var DEFAULT_CONTINUATION_GUIDANCE = "Continue working on the issue. Review your progress and complete any remaining tasks.";
@@ -729,12 +731,15 @@ async function startAssignedRun() {
729
731
  launcherEnv
730
732
  );
731
733
  const route = resolveWorkerRuntimeRoute(workflow);
732
- if (route === "runtime-adapter" && workflow.runtime?.kind === "claude-print" && isClaudeRuntimeCommand(resolveWorkflowRuntimeCommand(workflow))) {
734
+ const runtimeCommand = resolveWorkflowRuntimeCommand(workflow);
735
+ const claudeCommand = resolveClaudeCommandBinary(runtimeCommand);
736
+ if (route === "runtime-adapter" && workflow.runtime?.kind === "claude-print" && claudeCommand) {
733
737
  const hasGitHubGraphqlToken = typeof launcherEnv.GITHUB_GRAPHQL_TOKEN === "string" && launcherEnv.GITHUB_GRAPHQL_TOKEN.trim().length > 0;
734
738
  const preflight = await runClaudePreflight({
735
739
  cwd: launcherEnv.WORKING_DIRECTORY,
736
740
  env: launcherEnv,
737
- command: resolveClaudeCommandBinary(workflow.codex.command) ?? void 0,
741
+ command: claudeCommand,
742
+ authMode: resolveClaudePreflightAuthMode(workflow),
738
743
  includeGhAuth: !hasGitHubGraphqlToken
739
744
  });
740
745
  process.stderr.write(
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ parseIssueReference,
4
+ readGitHubProjectBinding,
5
+ renderIssueWorkflowPreview,
6
+ resetWorkflowCommandDependenciesForTest,
7
+ setWorkflowCommandDependenciesForTest,
8
+ workflow_default
9
+ } from "./chunk-NESHTYXQ.js";
10
+ import "./chunk-2YF7PQUC.js";
11
+ import "./chunk-HQ7A3C7K.js";
12
+ import "./chunk-Z3NZOPLZ.js";
13
+ import "./chunk-WCOIVNHH.js";
14
+ import "./chunk-WOVNN5NW.js";
15
+ export {
16
+ workflow_default as default,
17
+ parseIssueReference,
18
+ readGitHubProjectBinding,
19
+ renderIssueWorkflowPreview,
20
+ resetWorkflowCommandDependenciesForTest,
21
+ setWorkflowCommandDependenciesForTest
22
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gh-symphony/cli",
3
- "version": "0.0.22",
3
+ "version": "0.1.3",
4
4
  "license": "MIT",
5
5
  "author": "hojinzs",
6
6
  "description": "Interactive CLI for GitHub Symphony orchestration",
@@ -41,13 +41,13 @@
41
41
  },
42
42
  "devDependencies": {
43
43
  "tsup": "^8.5.1",
44
- "@gh-symphony/core": "0.0.14",
45
44
  "@gh-symphony/control-plane": "0.0.15",
46
45
  "@gh-symphony/dashboard": "0.0.14",
46
+ "@gh-symphony/core": "0.0.14",
47
47
  "@gh-symphony/orchestrator": "0.0.14",
48
+ "@gh-symphony/worker": "0.0.14",
48
49
  "@gh-symphony/runtime-claude": "0.0.14",
49
- "@gh-symphony/tracker-github": "0.0.14",
50
- "@gh-symphony/worker": "0.0.14"
50
+ "@gh-symphony/tracker-github": "0.0.14"
51
51
  },
52
52
  "scripts": {
53
53
  "build": "tsup",
@@ -1,146 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- loadGlobalConfig,
4
- loadProjectConfig
5
- } from "./chunk-QIRE2VXS.js";
6
-
7
- // src/project-selection.ts
8
- import * as p from "@clack/prompts";
9
- function isInteractiveTerminal() {
10
- return process.stdin.isTTY === true && process.stdout.isTTY === true;
11
- }
12
- function explicitProjectRequiredMessage() {
13
- return "Multiple projects are configured. Re-run with --project-id in non-interactive environments.\n";
14
- }
15
- async function inspectManagedProjectSelection(input) {
16
- if (input.requestedProjectId) {
17
- const projectConfig = await loadProjectConfig(
18
- input.configDir,
19
- input.requestedProjectId
20
- );
21
- if (!projectConfig) {
22
- return {
23
- kind: "requested_project_missing",
24
- projectId: input.requestedProjectId,
25
- message: `Project "${input.requestedProjectId}" is not configured. Run 'gh-symphony project add' or choose an existing project.`
26
- };
27
- }
28
- return {
29
- kind: "resolved",
30
- projectId: input.requestedProjectId,
31
- projectConfig
32
- };
33
- }
34
- const global = await loadGlobalConfig(input.configDir);
35
- if (!global) {
36
- return {
37
- kind: "missing_global_config",
38
- message: "No CLI configuration found. Run 'gh-symphony project add' first."
39
- };
40
- }
41
- const projectIds = global.projects ?? [];
42
- if (projectIds.length === 0) {
43
- return {
44
- kind: "no_projects",
45
- message: "No managed projects are configured. Run 'gh-symphony project add' first."
46
- };
47
- }
48
- if (projectIds.length > 1 && !isInteractiveTerminal()) {
49
- return {
50
- kind: "multiple_projects_require_selection",
51
- message: explicitProjectRequiredMessage().trimEnd()
52
- };
53
- }
54
- if (global.activeProject) {
55
- const projectConfig = await loadProjectConfig(
56
- input.configDir,
57
- global.activeProject
58
- );
59
- if (!projectConfig) {
60
- return {
61
- kind: "active_project_missing",
62
- projectId: global.activeProject,
63
- message: `Active project "${global.activeProject}" is configured in config.json but its project config is missing. Re-run 'gh-symphony project add' or 'gh-symphony project switch'.`
64
- };
65
- }
66
- return {
67
- kind: "resolved",
68
- projectId: global.activeProject,
69
- projectConfig
70
- };
71
- }
72
- if (projectIds.length === 1) {
73
- const projectId = projectIds[0];
74
- const projectConfig = await loadProjectConfig(input.configDir, projectId);
75
- if (!projectConfig) {
76
- return {
77
- kind: "configured_project_missing",
78
- projectId,
79
- message: `Configured project "${projectId}" is missing its project config file. Re-run 'gh-symphony project add'.`
80
- };
81
- }
82
- return {
83
- kind: "resolved",
84
- projectId,
85
- projectConfig
86
- };
87
- }
88
- return {
89
- kind: "multiple_projects_require_selection",
90
- message: "Multiple projects are configured and no active project is set. Run 'gh-symphony project switch' or re-run with --project-id."
91
- };
92
- }
93
- async function resolveManagedProjectConfig(input) {
94
- if (input.requestedProjectId) {
95
- return loadProjectConfig(input.configDir, input.requestedProjectId);
96
- }
97
- const global = await loadGlobalConfig(input.configDir);
98
- const projectIds = global?.projects ?? [];
99
- if (projectIds.length === 0) {
100
- return null;
101
- }
102
- if (projectIds.length === 1) {
103
- return loadProjectConfig(input.configDir, projectIds[0]);
104
- }
105
- if (!isInteractiveTerminal()) {
106
- process.stderr.write(explicitProjectRequiredMessage());
107
- process.exitCode = 1;
108
- return null;
109
- }
110
- const projects = await Promise.all(
111
- projectIds.map(async (projectId) => ({
112
- projectId,
113
- config: await loadProjectConfig(input.configDir, projectId)
114
- }))
115
- );
116
- const selected = await p.select({
117
- message: "Select a project:",
118
- options: projects.map(({ projectId, config }) => ({
119
- value: projectId,
120
- label: config?.displayName ?? config?.slug ?? projectId,
121
- hint: projectId === global?.activeProject ? "current" : config && config.displayName && config.displayName !== projectId ? projectId : void 0
122
- })),
123
- maxItems: 10
124
- });
125
- if (p.isCancel(selected)) {
126
- p.cancel("Cancelled.");
127
- process.exitCode = 130;
128
- return null;
129
- }
130
- return loadProjectConfig(input.configDir, selected);
131
- }
132
- function handleMissingManagedProjectConfig() {
133
- if (process.exitCode) {
134
- return;
135
- }
136
- process.stderr.write(
137
- "No project configured. Run 'gh-symphony project add' first.\n"
138
- );
139
- process.exitCode = 1;
140
- }
141
-
142
- export {
143
- inspectManagedProjectSelection,
144
- resolveManagedProjectConfig,
145
- handleMissingManagedProjectConfig
146
- };