@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.
package/README.md CHANGED
@@ -107,6 +107,25 @@ You can further customize the agent's behavior by editing `WORKFLOW.md` — this
107
107
 
108
108
  > Currently supported runtimes: **Codex**, **Claude Code**
109
109
 
110
+ ### Linear Tracker Repositories
111
+
112
+ For Linear, configure the tracker in `WORKFLOW.md` and initialize the repository runtime from the target GitHub repository:
113
+
114
+ ```yaml
115
+ tracker:
116
+ kind: linear
117
+ api_key: $LINEAR_API_KEY
118
+ project_slug: symphony-0c79b11b75ea
119
+ ```
120
+
121
+ `gh-symphony repo init` validates `tracker.project_slug` and resolves `tracker.api_key`, so `LINEAR_API_KEY` must be set before initialization. Linear aliases such as `tracker.project_id`, `projectId`, `project_id`, and `teamId` are rejected, and `.gh-symphony/config.json` is not a Linear source of truth.
122
+
123
+ Linear runs are polling-only. There is no webhook setup command. Put state transition, workpad comment, and PR handoff policy in `WORKFLOW.md`; see `docs/examples/linear-WORKFLOW.md` in the repository for a complete example. Preview a Linear issue prompt with:
124
+
125
+ ```bash
126
+ gh-symphony workflow preview ENG-123
127
+ ```
128
+
110
129
  ### Repository `.env` Mapping
111
130
 
112
131
  If your hooks or worker runs need staging hosts, database URLs, Playwright base URLs, or other runtime-only values, store them in the repository runtime directory instead of hardcoding them in `WORKFLOW.md`.
@@ -1,14 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ DEFAULT_LINEAR_GRAPHQL_URL,
3
4
  buildAgentInputRequiredReason,
4
5
  createGitHubGraphQLMcpServerEntry,
6
+ createLinearGraphQLMcpServerEntry,
5
7
  extractEnvForCodex,
6
8
  readAgentCredentialCache,
7
9
  readEnvFile,
8
10
  resolveGitHubGraphQLToken,
9
11
  shouldReuseAgentCredentialCache,
10
12
  writeAgentCredentialCache
11
- } from "./chunk-WCOIVNHH.js";
13
+ } from "./chunk-Q3UEPUE3.js";
12
14
 
13
15
  // ../runtime-codex/src/runtime.ts
14
16
  import { spawn } from "child_process";
@@ -77,6 +79,35 @@ function createGitHubGraphQLToolDefinition(config) {
77
79
  }
78
80
  };
79
81
  }
82
+ function createLinearGraphQLToolDefinition(config) {
83
+ const entry = createLinearGraphQLMcpServerEntry(config);
84
+ return {
85
+ name: "linear_graphql",
86
+ description: "Execute a single Linear GraphQL query or mutation for the active Linear issue using runtime-managed auth.",
87
+ command: entry.command,
88
+ args: entry.args,
89
+ env: entry.env,
90
+ inputSchema: {
91
+ type: "object",
92
+ properties: {
93
+ query: {
94
+ type: "string",
95
+ description: "Single GraphQL query or mutation document."
96
+ },
97
+ variables: {
98
+ type: "object",
99
+ description: "Variables for the GraphQL document."
100
+ },
101
+ operationName: {
102
+ type: "string",
103
+ description: "Optional GraphQL operation name."
104
+ }
105
+ },
106
+ required: ["query"],
107
+ additionalProperties: false
108
+ }
109
+ };
110
+ }
80
111
  function asRecord(value) {
81
112
  return value != null && typeof value === "object" ? value : {};
82
113
  }
@@ -272,7 +303,15 @@ function resolvePreparedAgentEnvironment(workingDirectory, env) {
272
303
  };
273
304
  }
274
305
  function buildCodexRuntimePlan(config) {
275
- const tool = createGitHubGraphQLToolDefinition(config);
306
+ const githubTool = createGitHubGraphQLToolDefinition(config);
307
+ const tools = [
308
+ githubTool,
309
+ ...config.enableLinearGraphqlTool ? [
310
+ createLinearGraphQLToolDefinition({
311
+ linearGraphqlUrl: config.linearGraphqlUrl ?? DEFAULT_LINEAR_GRAPHQL_URL
312
+ })
313
+ ] : []
314
+ ];
276
315
  const gitCredentialHelper = createGitCredentialHelperEnvironment(config);
277
316
  const shellCmd = (() => {
278
317
  const cmd = config.agentCommand ?? "codex app-server";
@@ -282,6 +321,21 @@ function buildCodexRuntimePlan(config) {
282
321
  config.workingDirectory,
283
322
  config.agentEnv
284
323
  );
324
+ const linearGraphqlEnv = config.enableLinearGraphqlTool ? {
325
+ LINEAR_GRAPHQL_TOOL_NAME: "linear_graphql",
326
+ LINEAR_GRAPHQL_URL: config.linearGraphqlUrl ?? DEFAULT_LINEAR_GRAPHQL_URL,
327
+ ...config.linearAuthorization ? {
328
+ LINEAR_AUTHORIZATION: config.linearAuthorization
329
+ } : {},
330
+ ...config.linearApiKey ? {
331
+ LINEAR_API_KEY: config.linearApiKey
332
+ } : {}
333
+ } : {
334
+ LINEAR_GRAPHQL_TOOL_NAME: "",
335
+ LINEAR_GRAPHQL_URL: void 0,
336
+ LINEAR_API_KEY: void 0,
337
+ LINEAR_AUTHORIZATION: void 0
338
+ };
285
339
  return {
286
340
  cwd: config.workingDirectory,
287
341
  command: "bash",
@@ -292,13 +346,17 @@ function buildCodexRuntimePlan(config) {
292
346
  ...config.agentEnv,
293
347
  CODEX_PROJECT_ID: config.projectId,
294
348
  GITHUB_PROJECT_ID: config.githubProjectId ?? "",
295
- GITHUB_GRAPHQL_TOOL_NAME: tool.name,
296
- GITHUB_GRAPHQL_TOOL_COMMAND: [tool.command, ...tool.args].join(" "),
349
+ GITHUB_GRAPHQL_TOOL_NAME: githubTool.name,
350
+ GITHUB_GRAPHQL_TOOL_COMMAND: [
351
+ githubTool.command,
352
+ ...githubTool.args
353
+ ].join(" "),
354
+ ...linearGraphqlEnv,
297
355
  ...agentEnv,
298
356
  ...gitCredentialHelper,
299
- ...tool.env
357
+ ...Object.assign({}, ...tools.map((tool) => tool.env))
300
358
  },
301
- tools: [tool]
359
+ tools
302
360
  };
303
361
  }
304
362
  function launchCodexAppServer(plan, spawnImpl = spawn) {
@@ -426,7 +484,10 @@ async function resolveAgentRuntimeEnvironment(config, dependencies = {}, adapter
426
484
  }
427
485
  const now = dependencies.now ?? /* @__PURE__ */ new Date();
428
486
  const readFileImpl = dependencies.readFileImpl ?? readFile;
429
- const cachedCredentials = config.agentCredentialCachePath ? await readAgentCredentialCache(config.agentCredentialCachePath, readFileImpl) : null;
487
+ const cachedCredentials = config.agentCredentialCachePath ? await readAgentCredentialCache(
488
+ config.agentCredentialCachePath,
489
+ readFileImpl
490
+ ) : null;
430
491
  if (cachedCredentials && shouldReuseAgentCredentialCache(cachedCredentials, now)) {
431
492
  return resolveRuntimeCredentials(config, cachedCredentials, adapter);
432
493
  }
@@ -460,7 +521,10 @@ async function resolveAgentRuntimeEnvironment(config, dependencies = {}, adapter
460
521
  return resolvedEnv;
461
522
  }
462
523
  function resolveRuntimeCredentials(config, brokerResponse, adapter) {
463
- return adapter ? adapter.resolveCredentials(brokerResponse) : resolvePreparedAgentEnvironment(config.workingDirectory, brokerResponse.env);
524
+ return adapter ? adapter.resolveCredentials(brokerResponse) : resolvePreparedAgentEnvironment(
525
+ config.workingDirectory,
526
+ brokerResponse.env
527
+ );
464
528
  }
465
529
  async function stageCodexHome(config, dependencies = {}) {
466
530
  const codexHomeDir = resolveStagedCodexHome(config.workingDirectory);
@@ -524,6 +588,10 @@ function resolveLocalRuntimeLaunchConfig(env = process.env) {
524
588
  agentCredentialCachePath: env.AGENT_CREDENTIAL_CACHE_PATH,
525
589
  githubProjectId: env.GITHUB_PROJECT_ID,
526
590
  githubGraphqlApiUrl: env.GITHUB_GRAPHQL_API_URL,
591
+ enableLinearGraphqlTool: env.SYMPHONY_TRACKER_KIND === "linear",
592
+ linearApiKey: env.LINEAR_API_KEY,
593
+ linearAuthorization: env.LINEAR_AUTHORIZATION,
594
+ linearGraphqlUrl: env.LINEAR_GRAPHQL_URL,
527
595
  agentCommand: env.SYMPHONY_AGENT_COMMAND
528
596
  };
529
597
  }
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-6I753NYO.js";
5
5
  import {
6
6
  parseWorkflowMarkdown
7
- } from "./chunk-WCOIVNHH.js";
7
+ } from "./chunk-Q3UEPUE3.js";
8
8
  import {
9
9
  saveGlobalConfig,
10
10
  saveProjectConfig
@@ -58,6 +58,7 @@ async function initRepoRuntime(flags) {
58
58
  await migrateLegacyRuntime(runtimeRoot);
59
59
  const workflowPath = resolve(repoDir, flags.workflowFile ?? "WORKFLOW.md");
60
60
  const workflow = parseWorkflowMarkdown(await readFile(workflowPath, "utf8"));
61
+ validateRepoInitWorkflow(workflow);
61
62
  const repository = resolveRepository(repoDir);
62
63
  const trackerAdapter = workflow.tracker.kind ?? "github-project";
63
64
  const trackerBindingId = workflow.tracker.projectId ?? workflow.tracker.projectSlug ?? "";
@@ -112,6 +113,16 @@ async function initRepoRuntime(flags) {
112
113
  repository
113
114
  };
114
115
  }
116
+ function validateRepoInitWorkflow(workflow) {
117
+ if (workflow.tracker.kind !== "linear") {
118
+ return;
119
+ }
120
+ if (!workflow.tracker.apiKey?.trim()) {
121
+ throw new Error(
122
+ 'Linear tracker repo init requires WORKFLOW.md field "tracker.api_key" to reference a resolvable environment variable such as "$LINEAR_API_KEY".'
123
+ );
124
+ }
125
+ }
115
126
  async function migrateLegacyRuntime(runtimeRoot) {
116
127
  const projectsDir = join(runtimeRoot, "projects");
117
128
  const projectIds = await readDirectoryNames(projectsDir);