@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
@@ -51,14 +51,6 @@ function showCursor() {
51
51
  return "\x1B[?25h";
52
52
  }
53
53
 
54
- // src/format/repository.ts
55
- function formatRepositoryDisplay(snapshot, fallback = "repository") {
56
- if (snapshot.repository) {
57
- return `${snapshot.repository.owner}/${snapshot.repository.name}`;
58
- }
59
- return snapshot.slug ?? fallback;
60
- }
61
-
62
54
  export {
63
55
  bold,
64
56
  dim,
@@ -72,6 +64,5 @@ export {
72
64
  setNoColor,
73
65
  clearScreen,
74
66
  hideCursor,
75
- showCursor,
76
- formatRepositoryDisplay
67
+ showCursor
77
68
  };
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- init_default
4
- } from "./chunk-HMLBBZNY.js";
3
+ workflow_init_default
4
+ } from "./chunk-2YF7PQUC.js";
5
5
  import {
6
- fetchGithubProjectIssueByRepositoryAndNumber
7
- } from "./chunk-2TSM3INR.js";
6
+ fetchGithubProjectIssueByRepositoryAndNumber,
7
+ inspectManagedProjectSelection
8
+ } from "./chunk-HQ7A3C7K.js";
8
9
  import {
9
10
  GitHubApiError,
10
11
  createClient,
@@ -12,16 +13,12 @@ import {
12
13
  getGhTokenWithSource,
13
14
  getProjectDetail,
14
15
  validateGitHubToken
15
- } from "./chunk-C67H3OUL.js";
16
+ } from "./chunk-Z3NZOPLZ.js";
16
17
  import {
17
18
  buildPromptVariables,
18
19
  parseWorkflowMarkdown,
19
20
  renderPrompt
20
- } from "./chunk-EEQQWTXS.js";
21
- import {
22
- inspectManagedProjectSelection
23
- } from "./chunk-DDL4BWSL.js";
24
- import "./chunk-QIRE2VXS.js";
21
+ } from "./chunk-WCOIVNHH.js";
25
22
 
26
23
  // src/commands/workflow.ts
27
24
  import { readFile } from "fs/promises";
@@ -391,6 +388,14 @@ function readGitHubProjectBinding(projectConfig) {
391
388
  const settingsProjectId = projectConfig.tracker.settings?.projectId;
392
389
  return typeof settingsProjectId === "string" && settingsProjectId.trim().length > 0 ? settingsProjectId.trim() : null;
393
390
  }
391
+ function renderIssueWorkflowPreview(input) {
392
+ const variables = buildPromptVariables(input.issue, {
393
+ attempt: input.attempt
394
+ });
395
+ return renderPrompt(input.workflow.promptTemplate, variables, {
396
+ strict: true
397
+ });
398
+ }
394
399
  function formatAuthError(error) {
395
400
  return `GitHub authentication is required for live issue preview. ${error.message}`;
396
401
  }
@@ -406,7 +411,7 @@ async function loadLiveIssue(issueReference, projectId, options) {
406
411
  const githubProjectId = readGitHubProjectBinding(selection.projectConfig);
407
412
  if (!githubProjectId) {
408
413
  throw new Error(
409
- `Managed project "${selection.projectId}" is not bound to a GitHub Project. Re-run 'gh-symphony project add' and select a valid GitHub Project binding.`
414
+ `Managed project "${selection.projectId}" is not bound to a GitHub Project. Run 'gh-symphony workflow init' to select a valid GitHub Project binding, then run 'gh-symphony repo init'.`
410
415
  );
411
416
  }
412
417
  let auth;
@@ -439,7 +444,7 @@ async function loadLiveIssue(issueReference, projectId, options) {
439
444
  }
440
445
  if (!findLinkedRepository(detail, issue.owner, issue.name)) {
441
446
  throw new Error(
442
- `Repository ${issue.owner}/${issue.name} is not linked to the configured GitHub Project "${detail.title}". Run 'gh-symphony repo add ${issue.owner}/${issue.name}' or re-run 'gh-symphony project add' with the correct project binding.`
447
+ `Repository ${issue.owner}/${issue.name} is not linked to the configured GitHub Project "${detail.title}". Run 'gh-symphony setup' from inside the target repository, or run 'gh-symphony workflow init' followed by 'gh-symphony repo init'.`
443
448
  );
444
449
  }
445
450
  const trackedIssue = await workflowCommandDependencies.fetchLiveIssue(
@@ -587,12 +592,11 @@ async function runPreview(args, options) {
587
592
  );
588
593
  }
589
594
  const { issue, sampleSource } = flags.issue ? await loadLiveIssue(flags.issue, flags.projectId, options) : await loadSampleIssue(flags.sample);
590
- const variables = buildPromptVariables(issue, {
595
+ const renderedPrompt = renderIssueWorkflowPreview({
596
+ workflow,
597
+ issue,
591
598
  attempt: flags.attempt
592
599
  });
593
- const renderedPrompt = renderPrompt(workflow.promptTemplate, variables, {
594
- strict: true
595
- });
596
600
  if (options.json) {
597
601
  process.stdout.write(
598
602
  `${JSON.stringify(
@@ -639,7 +643,7 @@ var handler = async (args, options) => {
639
643
  try {
640
644
  switch (parsed.subcommand) {
641
645
  case "init":
642
- await init_default(parsed.args, options);
646
+ await workflow_init_default(parsed.args, options);
643
647
  return;
644
648
  case "validate":
645
649
  await runValidate(parsed.args, options);
@@ -656,8 +660,12 @@ var handler = async (args, options) => {
656
660
  }
657
661
  };
658
662
  var workflow_default = handler;
663
+
659
664
  export {
660
- workflow_default as default,
665
+ setWorkflowCommandDependenciesForTest,
661
666
  resetWorkflowCommandDependenciesForTest,
662
- setWorkflowCommandDependenciesForTest
667
+ parseIssueReference,
668
+ readGitHubProjectBinding,
669
+ renderIssueWorkflowPreview,
670
+ workflow_default
663
671
  };
@@ -8,7 +8,7 @@ import {
8
8
  resolveGitHubGraphQLToken,
9
9
  shouldReuseAgentCredentialCache,
10
10
  writeAgentCredentialCache
11
- } from "./chunk-EEQQWTXS.js";
11
+ } from "./chunk-WCOIVNHH.js";
12
12
 
13
13
  // ../runtime-codex/src/runtime.ts
14
14
  import { spawn } from "child_process";
@@ -0,0 +1,238 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ resolveRepoRuntimeRoot
4
+ } from "./chunk-6I753NYO.js";
5
+ import {
6
+ parseWorkflowMarkdown
7
+ } from "./chunk-WCOIVNHH.js";
8
+ import {
9
+ saveGlobalConfig,
10
+ saveProjectConfig
11
+ } from "./chunk-WOVNN5NW.js";
12
+
13
+ // src/repo-runtime.ts
14
+ import { execFileSync } from "child_process";
15
+ import {
16
+ mkdir,
17
+ readdir,
18
+ readFile,
19
+ rename,
20
+ rm,
21
+ stat,
22
+ writeFile
23
+ } from "fs/promises";
24
+ import { basename, dirname, join, resolve } from "path";
25
+ var INTERNAL_PROJECT_ID = "repository";
26
+ var RepoRuntimeMigrationError = class extends Error {
27
+ };
28
+ function parseRepoRuntimeFlags(args) {
29
+ const flags = { repoDir: process.cwd() };
30
+ for (let i = 0; i < args.length; i += 1) {
31
+ const arg = args[i];
32
+ const value = args[i + 1];
33
+ if (arg === "--repo-dir") {
34
+ if (!value || value.startsWith("-")) {
35
+ throw new Error("Option '--repo-dir' argument missing");
36
+ }
37
+ flags.repoDir = resolve(value);
38
+ i += 1;
39
+ continue;
40
+ }
41
+ if (arg === "--workflow-file") {
42
+ if (!value || value.startsWith("-")) {
43
+ throw new Error("Option '--workflow-file' argument missing");
44
+ }
45
+ flags.workflowFile = value;
46
+ i += 1;
47
+ continue;
48
+ }
49
+ if (arg?.startsWith("-")) {
50
+ throw new Error(`Unknown option '${arg}'`);
51
+ }
52
+ }
53
+ return flags;
54
+ }
55
+ async function initRepoRuntime(flags) {
56
+ const repoDir = resolve(flags.repoDir);
57
+ const runtimeRoot = resolveRepoRuntimeRoot(repoDir);
58
+ await migrateLegacyRuntime(runtimeRoot);
59
+ const workflowPath = resolve(repoDir, flags.workflowFile ?? "WORKFLOW.md");
60
+ const workflow = parseWorkflowMarkdown(await readFile(workflowPath, "utf8"));
61
+ const repository = resolveRepository(repoDir);
62
+ const trackerAdapter = workflow.tracker.kind ?? "github-project";
63
+ const trackerBindingId = workflow.tracker.projectId ?? workflow.tracker.projectSlug ?? "";
64
+ const trackerSettings = {
65
+ ...workflow.tracker.projectId ? { projectId: workflow.tracker.projectId } : {},
66
+ ...workflow.tracker.projectSlug ? { projectSlug: workflow.tracker.projectSlug } : {},
67
+ ...trackerAdapter === "linear" ? { activeStates: workflow.tracker.activeStates.join("\n") } : {},
68
+ repository: `${repository.owner}/${repository.name}`
69
+ };
70
+ if (flags.assignedOnly) {
71
+ trackerSettings.assignedOnly = true;
72
+ }
73
+ if (trackerAdapter === "file") {
74
+ if (!process.env.GH_SYMPHONY_FILE_TRACKER_ISSUES_PATH) {
75
+ throw new Error(
76
+ "File tracker repo init requires GH_SYMPHONY_FILE_TRACKER_ISSUES_PATH to point to the issues fixture."
77
+ );
78
+ }
79
+ trackerSettings.issuesPath = process.env.GH_SYMPHONY_FILE_TRACKER_ISSUES_PATH;
80
+ }
81
+ const projectConfig = {
82
+ projectId: INTERNAL_PROJECT_ID,
83
+ slug: basename(repoDir) || INTERNAL_PROJECT_ID,
84
+ displayName: `${repository.owner}/${repository.name}`,
85
+ workspaceDir: repoDir,
86
+ repository,
87
+ tracker: {
88
+ adapter: trackerAdapter,
89
+ bindingId: trackerBindingId,
90
+ ...workflow.tracker.endpoint ? { apiUrl: workflow.tracker.endpoint } : {},
91
+ settings: trackerSettings
92
+ }
93
+ };
94
+ await mkdir(runtimeRoot, { recursive: true });
95
+ await saveProjectConfig(runtimeRoot, INTERNAL_PROJECT_ID, projectConfig);
96
+ await saveGlobalConfig(runtimeRoot, {
97
+ activeProject: INTERNAL_PROJECT_ID,
98
+ projects: [INTERNAL_PROJECT_ID]
99
+ });
100
+ const orchestratorConfig = {
101
+ projectId: INTERNAL_PROJECT_ID,
102
+ slug: projectConfig.slug,
103
+ workspaceDir: repoDir,
104
+ repository,
105
+ tracker: projectConfig.tracker
106
+ };
107
+ await writeJsonFile(join(runtimeRoot, "project.json"), orchestratorConfig);
108
+ return {
109
+ configDir: runtimeRoot,
110
+ projectId: INTERNAL_PROJECT_ID,
111
+ workflowPath,
112
+ repository
113
+ };
114
+ }
115
+ async function migrateLegacyRuntime(runtimeRoot) {
116
+ const projectsDir = join(runtimeRoot, "projects");
117
+ const projectIds = await readDirectoryNames(projectsDir);
118
+ if (projectIds.length === 0) {
119
+ return;
120
+ }
121
+ if (projectIds.length === 1 && projectIds[0] === INTERNAL_PROJECT_ID && await pathExists(join(runtimeRoot, "project.json"))) {
122
+ return;
123
+ }
124
+ if (projectIds.length > 1) {
125
+ throw new RepoRuntimeMigrationError(
126
+ [
127
+ "Multiple legacy project runtime directories were found under .runtime/orchestrator/projects.",
128
+ `Found: ${projectIds.join(", ")}`,
129
+ "Automatic migration is only supported when exactly one project directory exists.",
130
+ "Manually keep the project directory you want to promote, archive or remove the others, then re-run 'gh-symphony repo init'."
131
+ ].join("\n")
132
+ );
133
+ }
134
+ const sourceDir = join(projectsDir, projectIds[0]);
135
+ const entries = await readdir(sourceDir);
136
+ for (const entry of entries) {
137
+ const target = join(runtimeRoot, entry);
138
+ if (await pathExists(target)) {
139
+ throw new RepoRuntimeMigrationError(
140
+ `Cannot promote legacy runtime data because '${entry}' already exists in .runtime/orchestrator. Move or remove it, then re-run 'gh-symphony repo init'.`
141
+ );
142
+ }
143
+ await rename(join(sourceDir, entry), target);
144
+ }
145
+ await stripProjectIdFromRunRecords(join(runtimeRoot, "runs"));
146
+ await rm(projectsDir, { recursive: true, force: true });
147
+ }
148
+ async function stripProjectIdFromRunRecords(runsDir) {
149
+ for (const runId of await readDirectoryNames(runsDir)) {
150
+ const runPath = join(runsDir, runId, "run.json");
151
+ const run = await readJsonFile(runPath);
152
+ if (!run || !("projectId" in run)) {
153
+ continue;
154
+ }
155
+ delete run.projectId;
156
+ await writeJsonFile(runPath, run);
157
+ }
158
+ }
159
+ async function readDirectoryNames(path) {
160
+ try {
161
+ const entries = await readdir(path, { withFileTypes: true });
162
+ return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
163
+ } catch (error) {
164
+ if (isMissing(error)) {
165
+ return [];
166
+ }
167
+ throw error;
168
+ }
169
+ }
170
+ function resolveRepository(repoDir) {
171
+ const remote = readGitOrigin(repoDir);
172
+ const cleanedRemote = remote.replace(/\.git$/, "");
173
+ const match = cleanedRemote.match(/github\.com[:/]([^/]+)\/([^/]+)$/) ?? cleanedRemote.match(/^([^/]+)\/([^/]+)$/);
174
+ if (!match) {
175
+ throw new Error(
176
+ "Unable to infer GitHub repository from git remote 'origin'. Run from a cloned GitHub repository or set origin to owner/name."
177
+ );
178
+ }
179
+ return {
180
+ owner: match[1],
181
+ name: match[2],
182
+ cloneUrl: remote.startsWith("http") ? remote : `https://github.com/${match[1]}/${match[2]}.git`,
183
+ path: repoDir
184
+ };
185
+ }
186
+ function readGitOrigin(repoDir) {
187
+ try {
188
+ return execFileSync(
189
+ "git",
190
+ ["-C", repoDir, "config", "--get", "remote.origin.url"],
191
+ {
192
+ encoding: "utf8",
193
+ stdio: ["ignore", "pipe", "ignore"]
194
+ }
195
+ ).trim();
196
+ } catch {
197
+ throw new Error(
198
+ "Unable to read git remote 'origin'. Run 'gh-symphony repo init' inside a cloned repository."
199
+ );
200
+ }
201
+ }
202
+ async function readJsonFile(path) {
203
+ try {
204
+ return JSON.parse(await readFile(path, "utf8"));
205
+ } catch (error) {
206
+ if (isMissing(error)) {
207
+ return null;
208
+ }
209
+ throw error;
210
+ }
211
+ }
212
+ async function writeJsonFile(path, value) {
213
+ await mkdir(dirname(path), { recursive: true });
214
+ const temporaryPath = `${path}.tmp`;
215
+ await writeFile(temporaryPath, JSON.stringify(value, null, 2) + "\n", "utf8");
216
+ await rename(temporaryPath, path);
217
+ }
218
+ async function pathExists(path) {
219
+ try {
220
+ await stat(path);
221
+ return true;
222
+ } catch (error) {
223
+ if (isMissing(error)) {
224
+ return false;
225
+ }
226
+ throw error;
227
+ }
228
+ }
229
+ function isMissing(error) {
230
+ return Boolean(
231
+ error && typeof error === "object" && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")
232
+ );
233
+ }
234
+
235
+ export {
236
+ parseRepoRuntimeFlags,
237
+ initRepoRuntime
238
+ };