@gh-symphony/cli 0.4.1 → 0.4.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.
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-3IRPSPAF.js";
5
5
  import {
6
6
  parseWorkflowMarkdown
7
- } from "./chunk-YKC6CGD6.js";
7
+ } from "./chunk-LJUEOVAQ.js";
8
8
  import {
9
9
  saveGlobalConfig,
10
10
  saveProjectConfig
@@ -30,7 +30,7 @@ import {
30
30
  resolveWorkflowRuntimeTimeouts,
31
31
  safeReadDir,
32
32
  scheduleRetryAt
33
- } from "./chunk-YKC6CGD6.js";
33
+ } from "./chunk-LJUEOVAQ.js";
34
34
  import {
35
35
  loadGlobalConfig,
36
36
  loadProjectConfig
@@ -359,7 +359,7 @@ function parseBlock(lines, startIndex, indent) {
359
359
  );
360
360
  }
361
361
  collectionType = "array";
362
- const itemText = trimmed.slice(2).trim();
362
+ const itemText = stripYamlInlineComment(trimmed.slice(2)).trim();
363
363
  if (itemText === "|" || itemText === "|-") {
364
364
  const [multiline, nextIndex3] = parseMultilineScalar(
365
365
  lines,
@@ -396,7 +396,9 @@ function parseBlock(lines, startIndex, indent) {
396
396
  throw new Error(`Invalid workflow front matter key "${rawKey}".`);
397
397
  }
398
398
  const key = parsedKey;
399
- const remainder = trimmed.slice(separatorIndex + 1).trim();
399
+ const remainder = stripYamlInlineComment(
400
+ trimmed.slice(separatorIndex + 1)
401
+ ).trim();
400
402
  if (remainder === "|" || remainder === "|-") {
401
403
  const [multiline, nextIndex2] = parseMultilineScalar(
402
404
  lines,
@@ -465,6 +467,7 @@ function findMappingSeparator(value) {
465
467
  return -1;
466
468
  }
467
469
  function parseScalar(value) {
470
+ value = stripYamlInlineComment(value).trim();
468
471
  if (value === "null") return null;
469
472
  if (value === "true") return true;
470
473
  if (value === "false") return false;
@@ -489,6 +492,30 @@ function parseScalar(value) {
489
492
  }
490
493
  return value;
491
494
  }
495
+ function stripYamlInlineComment(value) {
496
+ let quote = null;
497
+ for (let index = 0; index < value.length; index += 1) {
498
+ const char = value[index];
499
+ if (quote) {
500
+ if (quote === '"' && char === "\\") {
501
+ index += 1;
502
+ continue;
503
+ }
504
+ if (char === quote) {
505
+ quote = null;
506
+ }
507
+ continue;
508
+ }
509
+ if (char === '"' || char === "'") {
510
+ quote = char;
511
+ continue;
512
+ }
513
+ if (char === "#" && (index === 0 || /\s/.test(value[index - 1] ?? ""))) {
514
+ return value.slice(0, index).trimEnd();
515
+ }
516
+ }
517
+ return value;
518
+ }
492
519
  function parseInlineArray(value) {
493
520
  const inner = value.slice(1, -1).trim();
494
521
  if (!inner) {
@@ -16,7 +16,7 @@ import {
16
16
  formatClaudePreflightText,
17
17
  resolveClaudeCommandBinary,
18
18
  runClaudePreflight
19
- } from "./chunk-YKC6CGD6.js";
19
+ } from "./chunk-LJUEOVAQ.js";
20
20
 
21
21
  // src/mapping/smart-defaults.ts
22
22
  var ROLE_PATTERNS = [
@@ -10,7 +10,7 @@ import {
10
10
  resolveGitHubGraphQLToken,
11
11
  shouldReuseAgentCredentialCache,
12
12
  writeAgentCredentialCache
13
- } from "./chunk-YKC6CGD6.js";
13
+ } from "./chunk-LJUEOVAQ.js";
14
14
 
15
15
  // ../runtime-codex/src/runtime.ts
16
16
  import { spawn } from "child_process";
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  workflow_init_default
4
- } from "./chunk-RU5GQG6A.js";
4
+ } from "./chunk-OVE4KOBD.js";
5
5
  import {
6
6
  fetchGithubProjectIssueByRepositoryAndNumber,
7
7
  inspectManagedProjectSelection,
8
8
  resolveTrackerAdapter
9
- } from "./chunk-BBUCDKSL.js";
9
+ } from "./chunk-HHBXGE23.js";
10
10
  import {
11
11
  GitHubApiError,
12
12
  createClient,
@@ -19,7 +19,7 @@ import {
19
19
  buildPromptVariables,
20
20
  parseWorkflowMarkdown,
21
21
  renderPrompt
22
- } from "./chunk-YKC6CGD6.js";
22
+ } from "./chunk-LJUEOVAQ.js";
23
23
  import {
24
24
  loadActiveProjectConfig
25
25
  } from "./chunk-YZP5N5XP.js";
@@ -5,14 +5,14 @@ import {
5
5
  parseIssueReference,
6
6
  readGitHubProjectBinding,
7
7
  renderIssueWorkflowPreview
8
- } from "./chunk-3EFLX75C.js";
9
- import "./chunk-RU5GQG6A.js";
8
+ } from "./chunk-QHMT2V3X.js";
9
+ import "./chunk-OVE4KOBD.js";
10
10
  import {
11
11
  fetchGithubProjectIssueByRepositoryAndNumber,
12
12
  fetchGithubProjectIssues,
13
13
  inspectManagedProjectSelection
14
- } from "./chunk-BBUCDKSL.js";
15
- import "./chunk-GOR4JGJT.js";
14
+ } from "./chunk-HHBXGE23.js";
15
+ import "./chunk-QBBJMCNC.js";
16
16
  import {
17
17
  resolveRuntimeRoot
18
18
  } from "./chunk-3IRPSPAF.js";
@@ -40,7 +40,7 @@ import {
40
40
  resolveClaudeCommandBinary,
41
41
  resolveRuntimeCommandBinary,
42
42
  runClaudePreflight
43
- } from "./chunk-YKC6CGD6.js";
43
+ } from "./chunk-LJUEOVAQ.js";
44
44
  import {
45
45
  configFilePath,
46
46
  orchestratorLogPath,
@@ -766,6 +766,172 @@ function buildGithubTrackerConfig(input) {
766
766
  timeoutMs: typeof settings?.timeoutMs === "number" ? settings.timeoutMs : void 0
767
767
  };
768
768
  }
769
+ async function checkLinearTrackerResolution(input) {
770
+ const tracker = input.projectConfig.projectConfig.tracker;
771
+ const projectSlug = readStringSetting(tracker.settings, "projectSlug")?.trim();
772
+ const activeStates = readLinearActiveStates(tracker.settings);
773
+ const pickupLabels = readLinearPickupLabels(tracker.settings);
774
+ if (!projectSlug) {
775
+ return failCheck(
776
+ "linear_tracker_resolution",
777
+ "Linear tracker resolution",
778
+ "Linear tracker resolution could not run because the project slug is missing.",
779
+ "Run 'gh-symphony repo init' with WORKFLOW.md field 'tracker.project_slug' configured, then re-run 'gh-symphony doctor'.",
780
+ {
781
+ reason: "missing_project_slug",
782
+ adapter: tracker.adapter
783
+ }
784
+ );
785
+ }
786
+ if (activeStates.length === 0) {
787
+ return failCheck(
788
+ "linear_tracker_resolution",
789
+ "Linear tracker resolution",
790
+ "Linear tracker resolution could not run because no active states are configured.",
791
+ "Add at least one active state to WORKFLOW.md under 'tracker.active_states', run 'gh-symphony repo init' again, then re-run 'gh-symphony doctor'.",
792
+ {
793
+ reason: "missing_active_states",
794
+ projectSlug
795
+ }
796
+ );
797
+ }
798
+ const token = process.env.LINEAR_API_KEY?.trim();
799
+ if (!token) {
800
+ return failCheck(
801
+ "linear_tracker_resolution",
802
+ "Linear tracker resolution",
803
+ "Linear tracker resolution could not run because LINEAR_API_KEY is not set.",
804
+ "Set LINEAR_API_KEY in the environment and re-run 'gh-symphony doctor'.",
805
+ {
806
+ reason: "missing_token",
807
+ projectSlug,
808
+ activeStates,
809
+ pickupLabels
810
+ }
811
+ );
812
+ }
813
+ try {
814
+ const project = await fetchLinearProjectBySlug({
815
+ endpoint: tracker.apiUrl?.trim() || "https://api.linear.app/graphql",
816
+ projectSlug,
817
+ token,
818
+ fetchImpl: input.deps.fetchImpl
819
+ });
820
+ if (!project) {
821
+ return failCheck(
822
+ "linear_tracker_resolution",
823
+ "Linear tracker resolution",
824
+ `Linear project "${projectSlug}" was not found or is not accessible.`,
825
+ "Confirm LINEAR_API_KEY can read the configured Linear project, then re-run 'gh-symphony doctor'.",
826
+ {
827
+ reason: "api_error",
828
+ projectSlug,
829
+ activeStates,
830
+ pickupLabels
831
+ }
832
+ );
833
+ }
834
+ return passCheck(
835
+ "linear_tracker_resolution",
836
+ "Linear tracker resolution",
837
+ `Resolved Linear project "${project.name}" (${project.slugId}). ${formatLinearConfigSummary(activeStates, pickupLabels)}`,
838
+ {
839
+ projectId: project.id,
840
+ projectSlug: project.slugId,
841
+ activeStates,
842
+ pickupLabels
843
+ }
844
+ );
845
+ } catch (error) {
846
+ return failCheck(
847
+ "linear_tracker_resolution",
848
+ "Linear tracker resolution",
849
+ `Failed to resolve configured Linear project "${projectSlug}".`,
850
+ "Confirm LINEAR_API_KEY, tracker endpoint, project slug, and network access, then re-run 'gh-symphony doctor'.",
851
+ {
852
+ reason: "api_error",
853
+ projectSlug,
854
+ activeStates,
855
+ pickupLabels,
856
+ error: formatSmokeError(error)
857
+ }
858
+ );
859
+ }
860
+ }
861
+ function readStringSetting(settings, key) {
862
+ const value = settings?.[key];
863
+ return typeof value === "string" ? value : null;
864
+ }
865
+ function readLinearActiveStates(settings) {
866
+ const value = settings?.activeStates;
867
+ if (Array.isArray(value)) {
868
+ return value.filter(
869
+ (state) => typeof state === "string" && state.trim().length > 0
870
+ );
871
+ }
872
+ if (typeof value === "string") {
873
+ return value.split(/\r?\n/).map((state) => state.trim()).filter((state) => state.length > 0);
874
+ }
875
+ return [];
876
+ }
877
+ function readLinearPickupLabels(settings) {
878
+ const value = settings?.pickupLabels;
879
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
880
+ return { include: [], exclude: [] };
881
+ }
882
+ return {
883
+ include: readStringArraySetting(value, "include"),
884
+ exclude: readStringArraySetting(value, "exclude")
885
+ };
886
+ }
887
+ function readStringArraySetting(value, key) {
888
+ const field = value[key];
889
+ return Array.isArray(field) ? field.filter(
890
+ (item) => typeof item === "string" && item.trim().length > 0
891
+ ) : [];
892
+ }
893
+ function formatLinearConfigSummary(activeStates, pickupLabels) {
894
+ const labelSummary = pickupLabels.include.length > 0 || pickupLabels.exclude.length > 0 ? `Pickup labels include=[${pickupLabels.include.join(", ") || "none"}], exclude=[${pickupLabels.exclude.join(", ") || "none"}].` : "Pickup label filtering is not configured.";
895
+ return `Active states: ${activeStates.join(", ")}. ${labelSummary}`;
896
+ }
897
+ async function fetchLinearProjectBySlug(input) {
898
+ const response = await input.fetchImpl(input.endpoint, {
899
+ method: "POST",
900
+ headers: {
901
+ "Content-Type": "application/json",
902
+ Authorization: input.token
903
+ },
904
+ body: JSON.stringify({
905
+ query: `query SymphonyLinearDoctorProject($slug: String!) {
906
+ projects(filter: { slugId: { eq: $slug } }, first: 1) {
907
+ nodes {
908
+ id
909
+ name
910
+ slugId
911
+ }
912
+ }
913
+ }`,
914
+ variables: { slug: input.projectSlug }
915
+ })
916
+ });
917
+ if (!response.ok) {
918
+ throw new Error(`Linear GraphQL request failed with HTTP ${response.status}.`);
919
+ }
920
+ const payload = await response.json();
921
+ if (payload.errors?.length) {
922
+ const message = payload.errors.map((error) => error.message).filter(Boolean).join("; ") || "Unknown Linear GraphQL error";
923
+ throw new Error(`Linear GraphQL request failed: ${message}`);
924
+ }
925
+ const project = payload.data?.projects?.nodes?.[0];
926
+ if (!project || typeof project.id !== "string" || typeof project.name !== "string" || typeof project.slugId !== "string") {
927
+ return null;
928
+ }
929
+ return {
930
+ id: project.id,
931
+ name: project.name,
932
+ slugId: project.slugId
933
+ };
934
+ }
769
935
  async function buildPriorityMappingChecks(input) {
770
936
  if (input.workflow.status !== "pass") {
771
937
  return [];
@@ -1475,7 +1641,14 @@ ${DOCTOR_USAGE}`);
1475
1641
  )
1476
1642
  );
1477
1643
  }
1478
- if (resolvedProjectConfig.kind === "resolved" && !auth) {
1644
+ if (resolvedProjectConfig.kind === "resolved" && resolvedProjectConfig.projectConfig.tracker.adapter === "linear") {
1645
+ checks.push(
1646
+ await checkLinearTrackerResolution({
1647
+ projectConfig: resolvedProjectConfig,
1648
+ deps
1649
+ })
1650
+ );
1651
+ } else if (resolvedProjectConfig.kind === "resolved" && !auth) {
1479
1652
  checks.push(
1480
1653
  failCheck(
1481
1654
  "github_project_resolution",
@@ -2069,6 +2242,19 @@ async function runDoctorFixes(report, deps, options) {
2069
2242
  )
2070
2243
  );
2071
2244
  break;
2245
+ case "linear_tracker_resolution":
2246
+ steps.push(
2247
+ remediationStep(
2248
+ `remediate_${check.id}`,
2249
+ check.id,
2250
+ check.title,
2251
+ "manual",
2252
+ check.remediation ?? "Fix the Linear tracker configuration or credentials.",
2253
+ void 0,
2254
+ check.details
2255
+ )
2256
+ );
2257
+ break;
2072
2258
  }
2073
2259
  }
2074
2260
  return steps;
package/dist/index.js CHANGED
@@ -417,13 +417,13 @@ function createRemovedCommandHandler(message) {
417
417
 
418
418
  // src/index.ts
419
419
  var COMMANDS = {
420
- workflow: () => import("./workflow-URPIYFYQ.js"),
421
- setup: () => import("./setup-AADOLSW5.js"),
422
- doctor: () => import("./doctor-3WDODAKW.js"),
423
- upgrade: () => import("./upgrade-KTTYQQFB.js"),
424
- repo: () => import("./repo-3JLOCGRJ.js"),
420
+ workflow: () => import("./workflow-R3G7IA3Z.js"),
421
+ setup: () => import("./setup-4ZBHGOXT.js"),
422
+ doctor: () => import("./doctor-KQNUOPYV.js"),
423
+ upgrade: () => import("./upgrade-LQG3QBLC.js"),
424
+ repo: () => import("./repo-2NS2AU3D.js"),
425
425
  config: () => import("./config-cmd-OIVIUKG7.js"),
426
- version: () => import("./version-HXAMSXVG.js")
426
+ version: () => import("./version-TNOQD3SF.js")
427
427
  };
428
428
  function addGlobalOptions(command) {
429
429
  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");
@@ -17,7 +17,7 @@ import {
17
17
  import {
18
18
  initRepoRuntime,
19
19
  parseRepoRuntimeFlags
20
- } from "./chunk-MUN7QSFF.js";
20
+ } from "./chunk-34YCGQD2.js";
21
21
  import {
22
22
  OrchestratorService,
23
23
  acquireProjectLock,
@@ -33,8 +33,8 @@ import {
33
33
  resolveOrchestratorLogLevel,
34
34
  resolveTrackerAdapter,
35
35
  runCli
36
- } from "./chunk-BBUCDKSL.js";
37
- import "./chunk-GOR4JGJT.js";
36
+ } from "./chunk-HHBXGE23.js";
37
+ import "./chunk-QBBJMCNC.js";
38
38
  import {
39
39
  resolveRepoRuntimeRoot,
40
40
  resolveRuntimeRoot
@@ -58,7 +58,7 @@ import {
58
58
  parseRecentEvents,
59
59
  readJsonFile,
60
60
  safeReadDir
61
- } from "./chunk-YKC6CGD6.js";
61
+ } from "./chunk-LJUEOVAQ.js";
62
62
  import {
63
63
  daemonPidPath,
64
64
  httpStatusPath,
@@ -16,10 +16,10 @@ import {
16
16
  warnDeprecatedSkipContext,
17
17
  writeEcosystem,
18
18
  writeWorkflowPlan
19
- } from "./chunk-RU5GQG6A.js";
19
+ } from "./chunk-OVE4KOBD.js";
20
20
  import {
21
21
  initRepoRuntime
22
- } from "./chunk-MUN7QSFF.js";
22
+ } from "./chunk-34YCGQD2.js";
23
23
  import "./chunk-3IRPSPAF.js";
24
24
  import {
25
25
  GhAuthError,
@@ -33,7 +33,7 @@ import {
33
33
  listUserProjects,
34
34
  validateToken
35
35
  } from "./chunk-SMNIGNS3.js";
36
- import "./chunk-YKC6CGD6.js";
36
+ import "./chunk-LJUEOVAQ.js";
37
37
  import "./chunk-YZP5N5XP.js";
38
38
 
39
39
  // src/commands/setup.ts
@@ -16,8 +16,8 @@ function execFileAsync(file, args, execFileImpl = execFileCallback) {
16
16
  });
17
17
  }
18
18
  function resolveCurrentCliVersion() {
19
- if ("0.4.1".length > 0) {
20
- return "0.4.1";
19
+ if ("0.4.3".length > 0) {
20
+ return "0.4.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.4.1";
5
+ const version = "0.4.3";
6
6
  if (options.json) {
7
7
  process.stdout.write(JSON.stringify({ version }) + "\n");
8
8
  } else {
@@ -6,7 +6,7 @@ import {
6
6
  normalizeCodexRuntimeEvents,
7
7
  prepareCodexRuntimePlan,
8
8
  resolveLocalRuntimeLaunchConfig
9
- } from "./chunk-GOR4JGJT.js";
9
+ } from "./chunk-QBBJMCNC.js";
10
10
  import {
11
11
  DEFAULT_AGENT_INPUT_REQUIRED_REASON,
12
12
  classifySessionExit,
@@ -17,7 +17,7 @@ import {
17
17
  resolveClaudeCommandBinary,
18
18
  resolveWorkflowRuntimeCommand,
19
19
  runClaudePreflight
20
- } from "./chunk-YKC6CGD6.js";
20
+ } from "./chunk-LJUEOVAQ.js";
21
21
 
22
22
  // ../worker/src/index.ts
23
23
  import { spawn as spawn2 } from "child_process";
@@ -6,12 +6,12 @@ import {
6
6
  resetWorkflowCommandDependenciesForTest,
7
7
  setWorkflowCommandDependenciesForTest,
8
8
  workflow_default
9
- } from "./chunk-3EFLX75C.js";
10
- import "./chunk-RU5GQG6A.js";
11
- import "./chunk-BBUCDKSL.js";
12
- import "./chunk-GOR4JGJT.js";
9
+ } from "./chunk-QHMT2V3X.js";
10
+ import "./chunk-OVE4KOBD.js";
11
+ import "./chunk-HHBXGE23.js";
12
+ import "./chunk-QBBJMCNC.js";
13
13
  import "./chunk-SMNIGNS3.js";
14
- import "./chunk-YKC6CGD6.js";
14
+ import "./chunk-LJUEOVAQ.js";
15
15
  import "./chunk-YZP5N5XP.js";
16
16
  export {
17
17
  workflow_default as default,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gh-symphony/cli",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "license": "MIT",
5
5
  "author": "hojinzs",
6
6
  "description": "Interactive CLI for GitHub Symphony orchestration",
@@ -42,11 +42,11 @@
42
42
  "devDependencies": {
43
43
  "tsup": "^8.5.1",
44
44
  "@gh-symphony/core": "0.0.14",
45
- "@gh-symphony/control-plane": "0.0.15",
46
45
  "@gh-symphony/dashboard": "0.0.14",
46
+ "@gh-symphony/control-plane": "0.0.15",
47
47
  "@gh-symphony/orchestrator": "0.0.14",
48
- "@gh-symphony/runtime-claude": "0.0.14",
49
48
  "@gh-symphony/tracker-github": "0.0.14",
49
+ "@gh-symphony/runtime-claude": "0.0.14",
50
50
  "@gh-symphony/worker": "0.0.14"
51
51
  },
52
52
  "scripts": {