@gh-symphony/cli 0.4.2 → 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.
|
@@ -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" &&
|
|
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
|
@@ -419,11 +419,11 @@ function createRemovedCommandHandler(message) {
|
|
|
419
419
|
var COMMANDS = {
|
|
420
420
|
workflow: () => import("./workflow-R3G7IA3Z.js"),
|
|
421
421
|
setup: () => import("./setup-4ZBHGOXT.js"),
|
|
422
|
-
doctor: () => import("./doctor-
|
|
423
|
-
upgrade: () => import("./upgrade-
|
|
422
|
+
doctor: () => import("./doctor-KQNUOPYV.js"),
|
|
423
|
+
upgrade: () => import("./upgrade-LQG3QBLC.js"),
|
|
424
424
|
repo: () => import("./repo-2NS2AU3D.js"),
|
|
425
425
|
config: () => import("./config-cmd-OIVIUKG7.js"),
|
|
426
|
-
version: () => import("./version-
|
|
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");
|
|
@@ -16,8 +16,8 @@ function execFileAsync(file, args, execFileImpl = execFileCallback) {
|
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
18
|
function resolveCurrentCliVersion() {
|
|
19
|
-
if ("0.4.
|
|
20
|
-
return "0.4.
|
|
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")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gh-symphony/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "hojinzs",
|
|
6
6
|
"description": "Interactive CLI for GitHub Symphony orchestration",
|
|
@@ -41,12 +41,12 @@
|
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"tsup": "^8.5.1",
|
|
44
|
+
"@gh-symphony/core": "0.0.14",
|
|
44
45
|
"@gh-symphony/dashboard": "0.0.14",
|
|
45
46
|
"@gh-symphony/control-plane": "0.0.15",
|
|
46
47
|
"@gh-symphony/orchestrator": "0.0.14",
|
|
47
48
|
"@gh-symphony/tracker-github": "0.0.14",
|
|
48
49
|
"@gh-symphony/runtime-claude": "0.0.14",
|
|
49
|
-
"@gh-symphony/core": "0.0.14",
|
|
50
50
|
"@gh-symphony/worker": "0.0.14"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|