@gh-symphony/cli 0.1.3 → 0.2.0

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,7 +15,7 @@ import {
15
15
  formatClaudePreflightText,
16
16
  resolveClaudeCommandBinary,
17
17
  runClaudePreflight
18
- } from "./chunk-WCOIVNHH.js";
18
+ } from "./chunk-Q3UEPUE3.js";
19
19
 
20
20
  // src/mapping/smart-defaults.ts
21
21
  var ROLE_PATTERNS = [
@@ -952,6 +952,22 @@ function generateReferenceWorkflow(input) {
952
952
  lines.push(" blocker_check_states: [{first active state}]");
953
953
  }
954
954
  lines.push("");
955
+ lines.push("# Linear tracker example:");
956
+ lines.push("# tracker:");
957
+ lines.push("# kind: linear");
958
+ lines.push("# endpoint: https://api.linear.app/graphql");
959
+ lines.push("# api_key: $LINEAR_API_KEY");
960
+ lines.push("# project_slug: symphony-0c79b11b75ea");
961
+ lines.push("# active_states:");
962
+ lines.push("# - Todo");
963
+ lines.push("# - In Progress");
964
+ lines.push("# terminal_states:");
965
+ lines.push("# - Done");
966
+ lines.push("# - Canceled");
967
+ lines.push("# - Duplicate");
968
+ lines.push("# Linear uses repository-local polling; gh-symphony does not provide");
969
+ lines.push("# a Linear webhook setup command.");
970
+ lines.push("");
955
971
  lines.push("polling:");
956
972
  lines.push(" interval_ms: 30000");
957
973
  lines.push("");
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  workflow_init_default
4
- } from "./chunk-2YF7PQUC.js";
4
+ } from "./chunk-F46FTZJE.js";
5
5
  import {
6
6
  fetchGithubProjectIssueByRepositoryAndNumber,
7
- inspectManagedProjectSelection
8
- } from "./chunk-HQ7A3C7K.js";
7
+ inspectManagedProjectSelection,
8
+ resolveTrackerAdapter
9
+ } from "./chunk-CTTFIZYG.js";
9
10
  import {
10
11
  GitHubApiError,
11
12
  createClient,
@@ -18,7 +19,10 @@ import {
18
19
  buildPromptVariables,
19
20
  parseWorkflowMarkdown,
20
21
  renderPrompt
21
- } from "./chunk-WCOIVNHH.js";
22
+ } from "./chunk-Q3UEPUE3.js";
23
+ import {
24
+ loadActiveProjectConfig
25
+ } from "./chunk-WOVNN5NW.js";
22
26
 
23
27
  // src/commands/workflow.ts
24
28
  import { readFile } from "fs/promises";
@@ -65,7 +69,9 @@ var workflowCommandDependencies = {
65
69
  fetchLiveIssue: fetchGithubProjectIssueByRepositoryAndNumber,
66
70
  getGitHubProjectDetail: getProjectDetail,
67
71
  getGitHubTokenWithSource: getGhTokenWithSource,
72
+ loadActiveProjectConfig,
68
73
  resolveManagedProjectSelection: inspectManagedProjectSelection,
74
+ resolveTrackerAdapter,
69
75
  validateGitHubToken
70
76
  };
71
77
  function setWorkflowCommandDependenciesForTest(overrides) {
@@ -76,7 +82,9 @@ function resetWorkflowCommandDependenciesForTest() {
76
82
  workflowCommandDependencies.fetchLiveIssue = fetchGithubProjectIssueByRepositoryAndNumber;
77
83
  workflowCommandDependencies.getGitHubProjectDetail = getProjectDetail;
78
84
  workflowCommandDependencies.getGitHubTokenWithSource = getGhTokenWithSource;
85
+ workflowCommandDependencies.loadActiveProjectConfig = loadActiveProjectConfig;
79
86
  workflowCommandDependencies.resolveManagedProjectSelection = inspectManagedProjectSelection;
87
+ workflowCommandDependencies.resolveTrackerAdapter = resolveTrackerAdapter;
80
88
  workflowCommandDependencies.validateGitHubToken = validateGitHubToken;
81
89
  }
82
90
  function parseWorkflowArgs(args) {
@@ -140,6 +148,9 @@ function parsePreviewFlags(args) {
140
148
  if (!value || value.startsWith("-")) {
141
149
  throw new Error("Option '--issue' argument missing");
142
150
  }
151
+ if (flags.issue) {
152
+ throw new Error("Only one preview issue identifier can be provided.");
153
+ }
143
154
  flags.issue = value;
144
155
  i += 1;
145
156
  break;
@@ -162,6 +173,10 @@ function parsePreviewFlags(args) {
162
173
  if (arg?.startsWith("-")) {
163
174
  throw new Error(`Unknown option '${arg}'`);
164
175
  }
176
+ if (flags.issue) {
177
+ throw new Error("Only one preview issue identifier can be provided.");
178
+ }
179
+ flags.issue = arg;
165
180
  break;
166
181
  }
167
182
  }
@@ -185,7 +200,9 @@ Commands:
185
200
  Options:
186
201
  workflow init [--non-interactive] [--project <id>] [--output <path>] [--skip-skills] [--skip-context] [--dry-run]
187
202
  workflow validate [--file <path>]
188
- workflow preview [--file <path>] [--issue <owner/repo#number>] [--project-id <projectId>] [--sample <json>] [--attempt <n>]
203
+ workflow preview [issue] [--file <path>] [--issue <owner/repo#number|ENG-123>] [--project-id <projectId>] [--sample <json>] [--attempt <n>]
204
+
205
+ Linear workflows use polling through the configured tracker adapter. No Linear webhook setup command is provided.
189
206
  `);
190
207
  }
191
208
  async function loadWorkflowMarkdown(workflowPath) {
@@ -472,6 +489,58 @@ async function loadLiveIssue(issueReference, projectId, options) {
472
489
  sampleSource: `live:${trackedIssue.identifier}`
473
490
  };
474
491
  }
492
+ var LINEAR_IDENTIFIER_PATTERN = /^[A-Z][A-Z0-9]*-\d+$/;
493
+ function isLinearIssueIdentifier(value) {
494
+ return LINEAR_IDENTIFIER_PATTERN.test(value.trim().toUpperCase());
495
+ }
496
+ async function loadLinearIssue(issueIdentifier, workflow, options) {
497
+ const projectConfig = await workflowCommandDependencies.loadActiveProjectConfig(
498
+ options.configDir
499
+ );
500
+ if (!projectConfig?.repository) {
501
+ throw new Error(
502
+ "Linear live issue preview requires a repository runtime initialized with 'gh-symphony repo init'."
503
+ );
504
+ }
505
+ const workflowProjectSlug = workflow.tracker.projectSlug?.trim();
506
+ if (!workflowProjectSlug) {
507
+ throw new Error(
508
+ 'Linear live issue preview requires WORKFLOW.md field "tracker.project_slug".'
509
+ );
510
+ }
511
+ if (!workflow.tracker.apiKey?.trim()) {
512
+ throw new Error(
513
+ 'Linear live issue preview requires WORKFLOW.md field "tracker.api_key" to resolve, for example "$LINEAR_API_KEY".'
514
+ );
515
+ }
516
+ if (projectConfig.tracker.adapter !== "linear" || projectConfig.tracker.bindingId !== workflowProjectSlug) {
517
+ throw new Error(
518
+ `Linear live issue preview requires an active repository runtime initialized for project "${workflowProjectSlug}". Run 'gh-symphony repo init' from this repository, then re-run the preview.`
519
+ );
520
+ }
521
+ const orchestratorProject = {
522
+ projectId: projectConfig.projectId,
523
+ slug: projectConfig.slug,
524
+ workspaceDir: projectConfig.workspaceDir,
525
+ repository: projectConfig.repository,
526
+ tracker: projectConfig.tracker
527
+ };
528
+ const trackerAdapter = workflowCommandDependencies.resolveTrackerAdapter(projectConfig.tracker);
529
+ const [issue] = await trackerAdapter.fetchIssueStatesByIds(
530
+ orchestratorProject,
531
+ [issueIdentifier.trim().toUpperCase()],
532
+ { token: workflow.tracker.apiKey }
533
+ );
534
+ if (!issue) {
535
+ throw new Error(
536
+ `Linear issue ${issueIdentifier} was not found in project "${workflow.tracker.projectSlug}".`
537
+ );
538
+ }
539
+ return {
540
+ issue,
541
+ sampleSource: `live:${issue.identifier}`
542
+ };
543
+ }
475
544
  function validateWorkflow(workflowPath, markdown) {
476
545
  const workflow = parseWorkflowMarkdown(markdown);
477
546
  const promptFreshVariables = buildPromptVariables(SAMPLE_ISSUE, {
@@ -586,12 +655,12 @@ async function runPreview(args, options) {
586
655
  }
587
656
  const { workflowPath, markdown } = await loadWorkflowMarkdown(flags.file);
588
657
  const workflow = parseWorkflowMarkdown(markdown);
589
- if (flags.issue && workflow.tracker.kind !== "github-project") {
658
+ if (flags.issue && workflow.tracker.kind !== "github-project" && !(workflow.tracker.kind === "linear" && isLinearIssueIdentifier(flags.issue))) {
590
659
  throw new Error(
591
- "Live issue preview requires 'tracker.kind: github-project' in WORKFLOW.md."
660
+ "Live issue preview requires 'tracker.kind: github-project' with owner/repo#number or 'tracker.kind: linear' with a Linear identifier such as ENG-123."
592
661
  );
593
662
  }
594
- const { issue, sampleSource } = flags.issue ? await loadLiveIssue(flags.issue, flags.projectId, options) : await loadSampleIssue(flags.sample);
663
+ const { issue, sampleSource } = flags.issue ? workflow.tracker.kind === "linear" ? await loadLinearIssue(flags.issue, workflow, options) : await loadLiveIssue(flags.issue, flags.projectId, options) : await loadSampleIssue(flags.sample);
595
664
  const renderedPrompt = renderIssueWorkflowPreview({
596
665
  workflow,
597
666
  issue,