@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.
- package/README.md +72 -77
- package/dist/{chunk-HMLBBZNY.js → chunk-2YF7PQUC.js} +16 -71
- package/dist/{chunk-IWFX2FMA.js → chunk-6I753NYO.js} +4 -1
- package/dist/{chunk-2TSM3INR.js → chunk-HQ7A3C7K.js} +575 -12
- package/dist/{chunk-36KYEDEO.js → chunk-MVRF7BES.js} +1 -10
- package/dist/{workflow-L3KT6HB7.js → chunk-NESHTYXQ.js} +27 -19
- package/dist/{chunk-2UW7NQLX.js → chunk-PEZUBHWJ.js} +1 -1
- package/dist/chunk-PG332ZS4.js +238 -0
- package/dist/{chunk-EEQQWTXS.js → chunk-WCOIVNHH.js} +213 -82
- package/dist/{chunk-QIRE2VXS.js → chunk-WOVNN5NW.js} +16 -17
- package/dist/{chunk-C67H3OUL.js → chunk-Z3NZOPLZ.js} +0 -81
- package/dist/{config-cmd-Z3A7V6NC.js → config-cmd-2ADPUYWA.js} +1 -1
- package/dist/{doctor-EJUMPBMW.js → doctor-2AXHIEAP.js} +464 -40
- package/dist/index.js +340 -294
- package/dist/{chunk-PUDXVBSN.js → repo-SUXYT4OK.js} +6272 -2996
- package/dist/{setup-TZJSM3QV.js → setup-UBHOMXUG.js} +57 -92
- package/dist/{upgrade-O33S2SJK.js → upgrade-355SQJ5P.js} +2 -2
- package/dist/{version-CW54Q7BK.js → version-4ILSDZQH.js} +1 -1
- package/dist/worker-entry.js +10 -5
- package/dist/workflow-S6YSZPQT.js +22 -0
- package/package.json +4 -4
- package/dist/chunk-DDL4BWSL.js +0 -146
- package/dist/chunk-DFLXHNYQ.js +0 -482
- package/dist/chunk-E7HYEEZD.js +0 -1318
- package/dist/chunk-GDE6FYN4.js +0 -26
- package/dist/chunk-GSX2FV3M.js +0 -103
- package/dist/chunk-ZHOKYUO3.js +0 -1047
- package/dist/init-54HMKNYI.js +0 -38
- package/dist/logs-GTZ4U5JE.js +0 -188
- package/dist/project-RMYMZSFV.js +0 -25
- package/dist/recover-LTLKMTRX.js +0 -133
- package/dist/repo-WI7GF6XQ.js +0 -749
- package/dist/run-IHN3ZL35.js +0 -122
- package/dist/start-RTAHQMR2.js +0 -19
- package/dist/status-F4D52OVK.js +0 -12
- 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
|
-
|
|
4
|
-
} from "./chunk-
|
|
3
|
+
workflow_init_default
|
|
4
|
+
} from "./chunk-2YF7PQUC.js";
|
|
5
5
|
import {
|
|
6
|
-
fetchGithubProjectIssueByRepositoryAndNumber
|
|
7
|
-
|
|
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-
|
|
16
|
+
} from "./chunk-Z3NZOPLZ.js";
|
|
16
17
|
import {
|
|
17
18
|
buildPromptVariables,
|
|
18
19
|
parseWorkflowMarkdown,
|
|
19
20
|
renderPrompt
|
|
20
|
-
} from "./chunk-
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
665
|
+
setWorkflowCommandDependenciesForTest,
|
|
661
666
|
resetWorkflowCommandDependenciesForTest,
|
|
662
|
-
|
|
667
|
+
parseIssueReference,
|
|
668
|
+
readGitHubProjectBinding,
|
|
669
|
+
renderIssueWorkflowPreview,
|
|
670
|
+
workflow_default
|
|
663
671
|
};
|
|
@@ -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
|
+
};
|