@gh-symphony/cli 0.0.21 → 0.0.22
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 +36 -0
- package/dist/{chunk-SXGT7LOF.js → chunk-2TSM3INR.js} +26 -1
- package/dist/{chunk-A67CMOYE.js → chunk-2UW7NQLX.js} +1 -1
- package/dist/{chunk-MVRF7BES.js → chunk-36KYEDEO.js} +10 -1
- package/dist/{chunk-C7G7RJ4G.js → chunk-DDL4BWSL.js} +1 -1
- package/dist/{chunk-XN5ABWZ6.js → chunk-DFLXHNYQ.js} +26 -30
- package/dist/{chunk-KY6WKH66.js → chunk-E7HYEEZD.js} +70 -52
- package/dist/{chunk-QEONJ5DZ.js → chunk-EEQQWTXS.js} +1288 -92
- package/dist/chunk-GDE6FYN4.js +26 -0
- package/dist/{chunk-Y6TYJMNT.js → chunk-GSX2FV3M.js} +10 -16
- package/dist/{chunk-JN3TQVFV.js → chunk-HMLBBZNY.js} +11 -2
- package/dist/{chunk-5NV3LSAJ.js → chunk-IWFX2FMA.js} +5 -1
- package/dist/{chunk-MYVJ6HK4.js → chunk-PUDXVBSN.js} +706 -376
- package/dist/{chunk-ROGRTUFI.js → chunk-QIRE2VXS.js} +14 -3
- package/dist/{chunk-S6VIK4FF.js → chunk-ZHOKYUO3.js} +337 -13
- package/dist/{config-cmd-DNXNL26Z.js → config-cmd-Z3A7V6NC.js} +1 -1
- package/dist/{doctor-4HBRICHP.js → doctor-EJUMPBMW.js} +4 -4
- package/dist/index.js +88 -21
- package/dist/{init-HZ3JEDGQ.js → init-54HMKNYI.js} +3 -3
- package/dist/{logs-6JKKYDGJ.js → logs-GTZ4U5JE.js} +2 -2
- package/dist/project-RMYMZSFV.js +25 -0
- package/dist/{recover-L3MJHHDA.js → recover-LTLKMTRX.js} +7 -7
- package/dist/repo-WI7GF6XQ.js +749 -0
- package/dist/{run-XJQ6BF7U.js → run-IHN3ZL35.js} +21 -9
- package/dist/{setup-B2SVLW2R.js → setup-TZJSM3QV.js} +14 -13
- package/dist/start-RTAHQMR2.js +19 -0
- package/dist/status-F4D52OVK.js +12 -0
- package/dist/stop-MDKMJPVR.js +10 -0
- package/dist/{upgrade-OJXPZRYE.js → upgrade-O33S2SJK.js} +2 -2
- package/dist/{version-TBDCTKDO.js → version-CW54Q7BK.js} +1 -1
- package/dist/worker-entry.js +369 -13
- package/dist/{workflow-BLJH2HC3.js → workflow-L3KT6HB7.js} +5 -5
- package/package.json +3 -3
- package/dist/project-25NQ4J4Y.js +0 -24
- package/dist/repo-TDCWQR6P.js +0 -379
- package/dist/start-I2CC7BLW.js +0 -18
- package/dist/status-QSCFVGRQ.js +0 -11
- package/dist/stop-7MFCBQVW.js +0 -9
|
@@ -54,9 +54,15 @@ async function saveGlobalConfig(configDir, config) {
|
|
|
54
54
|
await writeJsonFile(configFilePath(configDir), config);
|
|
55
55
|
}
|
|
56
56
|
async function loadProjectConfig(configDir, projectId) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
const config = await readJsonFile(projectConfigPath(configDir, projectId));
|
|
58
|
+
if (!config) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const repository = config.repository ?? firstConfiguredRepository(config);
|
|
62
|
+
return {
|
|
63
|
+
...config,
|
|
64
|
+
...repository ? { repository } : {}
|
|
65
|
+
};
|
|
60
66
|
}
|
|
61
67
|
async function saveProjectConfig(configDir, projectId, config) {
|
|
62
68
|
await writeJsonFile(projectConfigPath(configDir, projectId), config);
|
|
@@ -91,6 +97,11 @@ function isFileMissing(error) {
|
|
|
91
97
|
error && typeof error === "object" && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")
|
|
92
98
|
);
|
|
93
99
|
}
|
|
100
|
+
function firstConfiguredRepository(config) {
|
|
101
|
+
return config.repositories?.find(
|
|
102
|
+
(repository) => typeof repository.owner === "string" && repository.owner.length > 0 && typeof repository.name === "string" && repository.name.length > 0
|
|
103
|
+
);
|
|
104
|
+
}
|
|
94
105
|
|
|
95
106
|
export {
|
|
96
107
|
resolveConfigDir,
|
|
@@ -4,34 +4,55 @@ import {
|
|
|
4
4
|
generateProjectId,
|
|
5
5
|
warnIfProjectDiscoveryPartial,
|
|
6
6
|
writeConfig
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-HMLBBZNY.js";
|
|
8
8
|
import {
|
|
9
9
|
start_default
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-E7HYEEZD.js";
|
|
11
|
+
import {
|
|
12
|
+
explainIssueDispatch,
|
|
13
|
+
isActiveRunRecordStatus,
|
|
14
|
+
parseIssueIdentifier,
|
|
15
|
+
resolveTrackerAdapter
|
|
16
|
+
} from "./chunk-PUDXVBSN.js";
|
|
17
|
+
import {
|
|
18
|
+
findGithubProjectIssue
|
|
19
|
+
} from "./chunk-2TSM3INR.js";
|
|
11
20
|
import {
|
|
12
21
|
GhAuthError,
|
|
13
22
|
GitHubScopeError,
|
|
14
23
|
checkRequiredScopes,
|
|
15
24
|
createClient,
|
|
16
25
|
discoverUserProjects,
|
|
26
|
+
getGhToken,
|
|
17
27
|
getGhTokenWithSource,
|
|
18
28
|
getProjectDetail,
|
|
19
29
|
listUserProjects,
|
|
20
30
|
resolveGitHubAuth,
|
|
21
31
|
validateToken
|
|
22
32
|
} from "./chunk-C67H3OUL.js";
|
|
33
|
+
import {
|
|
34
|
+
WorkflowConfigStore
|
|
35
|
+
} from "./chunk-EEQQWTXS.js";
|
|
23
36
|
import {
|
|
24
37
|
status_default
|
|
25
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-DFLXHNYQ.js";
|
|
26
39
|
import {
|
|
27
|
-
|
|
28
|
-
|
|
40
|
+
bold,
|
|
41
|
+
green,
|
|
42
|
+
red,
|
|
43
|
+
stripAnsi,
|
|
44
|
+
yellow
|
|
45
|
+
} from "./chunk-36KYEDEO.js";
|
|
29
46
|
import {
|
|
30
47
|
resolveRuntimeRoot
|
|
31
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-IWFX2FMA.js";
|
|
32
49
|
import {
|
|
33
50
|
stop_default
|
|
34
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-GSX2FV3M.js";
|
|
52
|
+
import {
|
|
53
|
+
handleMissingManagedProjectConfig,
|
|
54
|
+
resolveManagedProjectConfig
|
|
55
|
+
} from "./chunk-DDL4BWSL.js";
|
|
35
56
|
import {
|
|
36
57
|
daemonPidPath,
|
|
37
58
|
httpStatusPath,
|
|
@@ -39,14 +60,15 @@ import {
|
|
|
39
60
|
loadProjectConfig,
|
|
40
61
|
projectConfigDir,
|
|
41
62
|
saveGlobalConfig
|
|
42
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-QIRE2VXS.js";
|
|
43
64
|
|
|
44
65
|
// src/commands/project.ts
|
|
45
66
|
import * as p from "@clack/prompts";
|
|
46
67
|
import { execFile as execFileCallback } from "child_process";
|
|
47
68
|
import { promisify } from "util";
|
|
48
|
-
import { readFile } from "fs/promises";
|
|
69
|
+
import { readdir, readFile } from "fs/promises";
|
|
49
70
|
import { join } from "path";
|
|
71
|
+
import { resolve } from "path";
|
|
50
72
|
var execFile = promisify(execFileCallback);
|
|
51
73
|
var KNOWN_REQUIRED_SCOPES = ["repo", "read:org", "project"];
|
|
52
74
|
function formatProjectRepoSummary(selectedRepos, totalLinked) {
|
|
@@ -113,6 +135,47 @@ function parseProjectAddFlags(args) {
|
|
|
113
135
|
}
|
|
114
136
|
return flags;
|
|
115
137
|
}
|
|
138
|
+
function parseProjectExplainFlags(args) {
|
|
139
|
+
const parsed = {};
|
|
140
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
141
|
+
const arg = args[i];
|
|
142
|
+
if (arg === "--project" || arg === "--project-id") {
|
|
143
|
+
const value = args[i + 1];
|
|
144
|
+
if (!value || value.startsWith("-")) {
|
|
145
|
+
parsed.error = `Option '${arg}' argument missing`;
|
|
146
|
+
return parsed;
|
|
147
|
+
}
|
|
148
|
+
parsed.projectId = value;
|
|
149
|
+
i += 1;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (arg === "--workflow" || arg === "--workflow-path") {
|
|
153
|
+
const value = args[i + 1];
|
|
154
|
+
if (!value || value.startsWith("-")) {
|
|
155
|
+
parsed.error = `Option '${arg}' argument missing`;
|
|
156
|
+
return parsed;
|
|
157
|
+
}
|
|
158
|
+
parsed.workflowPath = value;
|
|
159
|
+
i += 1;
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (arg?.startsWith("-")) {
|
|
163
|
+
parsed.error = `Unknown option '${arg}'`;
|
|
164
|
+
return parsed;
|
|
165
|
+
}
|
|
166
|
+
if (parsed.identifier) {
|
|
167
|
+
parsed.error = "Only one issue identifier can be explained at a time";
|
|
168
|
+
return parsed;
|
|
169
|
+
}
|
|
170
|
+
parsed.identifier = arg;
|
|
171
|
+
}
|
|
172
|
+
if (!parsed.identifier) {
|
|
173
|
+
parsed.error = "Issue identifier argument missing";
|
|
174
|
+
} else if (!parseIssueIdentifier(parsed.identifier)) {
|
|
175
|
+
parsed.error = "Issue identifier must use the form <owner>/<repo>#<number>";
|
|
176
|
+
}
|
|
177
|
+
return parsed;
|
|
178
|
+
}
|
|
116
179
|
var handler = async (args, options) => {
|
|
117
180
|
const [subcommand, ...rest] = args;
|
|
118
181
|
switch (subcommand) {
|
|
@@ -137,9 +200,12 @@ var handler = async (args, options) => {
|
|
|
137
200
|
case "status":
|
|
138
201
|
await status_default(rest, options);
|
|
139
202
|
return;
|
|
203
|
+
case "explain":
|
|
204
|
+
await projectExplain(rest, options);
|
|
205
|
+
return;
|
|
140
206
|
default:
|
|
141
207
|
process.stdout.write(
|
|
142
|
-
"Usage: gh-symphony project <add|list|remove|start|stop|switch|status>\n"
|
|
208
|
+
"Usage: gh-symphony project <add|list|remove|start|stop|switch|status|explain>\n"
|
|
143
209
|
);
|
|
144
210
|
}
|
|
145
211
|
};
|
|
@@ -237,9 +303,260 @@ async function readPersistedSnapshot(configDir, projectId) {
|
|
|
237
303
|
async function fetchProjectSnapshot(configDir, projectId) {
|
|
238
304
|
return readPersistedSnapshot(configDir, projectId);
|
|
239
305
|
}
|
|
306
|
+
async function projectExplain(args, options) {
|
|
307
|
+
const parsed = parseProjectExplainFlags(args);
|
|
308
|
+
if (parsed.error) {
|
|
309
|
+
process.stderr.write(`${parsed.error}
|
|
310
|
+
`);
|
|
311
|
+
process.stderr.write(
|
|
312
|
+
"Usage: gh-symphony project explain <owner/repo#number> [--project-id <project-id>] [--workflow <path>]\n"
|
|
313
|
+
);
|
|
314
|
+
process.exitCode = 2;
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const projectConfig = await resolveManagedProjectConfig({
|
|
318
|
+
configDir: options.configDir,
|
|
319
|
+
requestedProjectId: parsed.projectId
|
|
320
|
+
});
|
|
321
|
+
if (!projectConfig) {
|
|
322
|
+
handleMissingManagedProjectConfig();
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const identifier = parsed.identifier;
|
|
326
|
+
const parsedIdentifier = parseIssueIdentifier(identifier);
|
|
327
|
+
const fallbackRepository = {
|
|
328
|
+
owner: parsedIdentifier.owner,
|
|
329
|
+
name: parsedIdentifier.name,
|
|
330
|
+
cloneUrl: `https://github.com/${parsedIdentifier.owner}/${parsedIdentifier.name}.git`
|
|
331
|
+
};
|
|
332
|
+
const workflowRepository = projectConfig.repository ?? fallbackRepository;
|
|
333
|
+
let token;
|
|
334
|
+
try {
|
|
335
|
+
token = getGhToken();
|
|
336
|
+
} catch (error) {
|
|
337
|
+
if (error instanceof GhAuthError) {
|
|
338
|
+
process.stderr.write(
|
|
339
|
+
`Error: GitHub authentication is required for project explain. ${error.message}
|
|
340
|
+
`
|
|
341
|
+
);
|
|
342
|
+
process.stderr.write(
|
|
343
|
+
"Run 'gh auth login --scopes repo,read:org,project' or set GITHUB_GRAPHQL_TOKEN, then re-run this command.\n"
|
|
344
|
+
);
|
|
345
|
+
process.exitCode = 2;
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
throw error;
|
|
349
|
+
}
|
|
350
|
+
const trackerAdapter = resolveTrackerAdapter(projectConfig.tracker);
|
|
351
|
+
const orchestratorProject = {
|
|
352
|
+
...projectConfig,
|
|
353
|
+
repository: workflowRepository
|
|
354
|
+
};
|
|
355
|
+
const trackerDependencies = {
|
|
356
|
+
token,
|
|
357
|
+
projectItemsCache: createProjectItemsCache()
|
|
358
|
+
};
|
|
359
|
+
const runtimeRoot = join(
|
|
360
|
+
resolveRuntimeRoot(options.configDir),
|
|
361
|
+
"projects",
|
|
362
|
+
projectConfig.projectId
|
|
363
|
+
);
|
|
364
|
+
const issuesPromise = trackerAdapter.listIssues(
|
|
365
|
+
orchestratorProject,
|
|
366
|
+
trackerDependencies
|
|
367
|
+
);
|
|
368
|
+
const issuePromise = projectConfig.tracker.adapter === "github-project" ? findGithubProjectIssue(
|
|
369
|
+
orchestratorProject,
|
|
370
|
+
identifier,
|
|
371
|
+
trackerDependencies
|
|
372
|
+
) : issuesPromise.then(
|
|
373
|
+
(issues2) => issues2.find(
|
|
374
|
+
(candidate) => candidate.identifier.trim().toLowerCase() === identifier.trim().toLowerCase()
|
|
375
|
+
) ?? null
|
|
376
|
+
);
|
|
377
|
+
const [issues, issue, issueRecords, runs, snapshot] = await Promise.all([
|
|
378
|
+
issuesPromise,
|
|
379
|
+
issuePromise,
|
|
380
|
+
readJsonFile(join(runtimeRoot, "issues.json")),
|
|
381
|
+
readRuns(runtimeRoot, projectConfig.projectId),
|
|
382
|
+
readPersistedSnapshot(options.configDir, projectConfig.projectId)
|
|
383
|
+
]);
|
|
384
|
+
let workflow;
|
|
385
|
+
try {
|
|
386
|
+
workflow = await loadExplainWorkflow({
|
|
387
|
+
explicitWorkflowPath: parsed.workflowPath,
|
|
388
|
+
repository: workflowRepository,
|
|
389
|
+
runs
|
|
390
|
+
});
|
|
391
|
+
} catch (error) {
|
|
392
|
+
if (error instanceof ProjectExplainWorkflowError) {
|
|
393
|
+
process.stderr.write(`Error: ${error.message}
|
|
394
|
+
`);
|
|
395
|
+
process.stderr.write(
|
|
396
|
+
"Hint: pass --workflow <path-to-WORKFLOW.md> or run 'gh-symphony workflow preview --file <path>' to verify the workflow file.\n"
|
|
397
|
+
);
|
|
398
|
+
process.exitCode = 2;
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
throw error;
|
|
402
|
+
}
|
|
403
|
+
const activeRunCount = runs.filter(
|
|
404
|
+
(run) => isActiveRunRecordStatus(run.status)
|
|
405
|
+
).length;
|
|
406
|
+
const report = explainIssueDispatch({
|
|
407
|
+
identifier,
|
|
408
|
+
issue,
|
|
409
|
+
projectRepository: projectConfig.repository ?? null,
|
|
410
|
+
allIssues: issues,
|
|
411
|
+
lifecycle: workflow.lifecycle,
|
|
412
|
+
issueRecords: issueRecords ?? [],
|
|
413
|
+
runs,
|
|
414
|
+
activeRunCount,
|
|
415
|
+
maxConcurrentAgents: workflow.maxConcurrentAgents,
|
|
416
|
+
maxConcurrentAgentsByState: workflow.maxConcurrentAgentsByState
|
|
417
|
+
});
|
|
418
|
+
const enrichedReport = {
|
|
419
|
+
...report,
|
|
420
|
+
project: {
|
|
421
|
+
id: projectConfig.projectId,
|
|
422
|
+
slug: projectConfig.slug,
|
|
423
|
+
tracker: projectConfig.tracker,
|
|
424
|
+
lastTickAt: snapshot?.lastTickAt ?? null,
|
|
425
|
+
health: snapshot?.health ?? null
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
if (options.json) {
|
|
429
|
+
process.stdout.write(JSON.stringify(enrichedReport, null, 2) + "\n");
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
process.stdout.write(renderProjectExplainReport(report, options.noColor));
|
|
433
|
+
}
|
|
434
|
+
var ProjectExplainWorkflowError = class extends Error {
|
|
435
|
+
constructor(message) {
|
|
436
|
+
super(message);
|
|
437
|
+
this.name = "ProjectExplainWorkflowError";
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
async function loadExplainWorkflow(input) {
|
|
441
|
+
const workflowPaths = resolveExplainWorkflowCandidates(input);
|
|
442
|
+
if (workflowPaths.length === 0) {
|
|
443
|
+
throw new ProjectExplainWorkflowError(
|
|
444
|
+
"No WORKFLOW.md path could be resolved from --workflow, the configured repository path, or previous run records."
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
const failures = [];
|
|
448
|
+
for (const workflowPath of workflowPaths) {
|
|
449
|
+
try {
|
|
450
|
+
const resolution = await new WorkflowConfigStore().load(workflowPath);
|
|
451
|
+
return {
|
|
452
|
+
lifecycle: resolution.lifecycle,
|
|
453
|
+
maxConcurrentAgents: resolution.workflow.agent.maxConcurrentAgents,
|
|
454
|
+
maxConcurrentAgentsByState: resolution.workflow.agent.maxConcurrentAgentsByState
|
|
455
|
+
};
|
|
456
|
+
} catch (error) {
|
|
457
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
458
|
+
failures.push(`${workflowPath}: ${message}`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
throw new ProjectExplainWorkflowError(
|
|
462
|
+
`Unable to load WORKFLOW.md for project explain. Checked: ${failures.join("; ")}`
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
function resolveExplainWorkflowCandidates(input) {
|
|
466
|
+
const paths = [];
|
|
467
|
+
if (input.explicitWorkflowPath) {
|
|
468
|
+
paths.push(resolve(input.explicitWorkflowPath));
|
|
469
|
+
}
|
|
470
|
+
if (input.repository.path) {
|
|
471
|
+
paths.push(join(resolve(input.repository.path), "WORKFLOW.md"));
|
|
472
|
+
}
|
|
473
|
+
const newestRuns = [...input.runs].sort(
|
|
474
|
+
(left, right) => (Date.parse(right.updatedAt) || 0) - (Date.parse(left.updatedAt) || 0)
|
|
475
|
+
);
|
|
476
|
+
for (const run of newestRuns) {
|
|
477
|
+
if (run.workflowPath) {
|
|
478
|
+
paths.push(resolve(run.workflowPath));
|
|
479
|
+
}
|
|
480
|
+
if (run.workingDirectory) {
|
|
481
|
+
paths.push(join(resolve(run.workingDirectory), "WORKFLOW.md"));
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return [...new Set(paths)];
|
|
485
|
+
}
|
|
486
|
+
function createProjectItemsCache() {
|
|
487
|
+
const entries = /* @__PURE__ */ new Map();
|
|
488
|
+
return {
|
|
489
|
+
getOrLoad(key, load) {
|
|
490
|
+
const cached = entries.get(key);
|
|
491
|
+
if (cached) {
|
|
492
|
+
return cached;
|
|
493
|
+
}
|
|
494
|
+
const pending = load().catch((error) => {
|
|
495
|
+
entries.delete(key);
|
|
496
|
+
throw error;
|
|
497
|
+
});
|
|
498
|
+
entries.set(key, pending);
|
|
499
|
+
return pending;
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
async function readRuns(runtimeRoot, projectId) {
|
|
504
|
+
let runIds;
|
|
505
|
+
try {
|
|
506
|
+
runIds = await readdir(join(runtimeRoot, "runs"));
|
|
507
|
+
} catch {
|
|
508
|
+
return [];
|
|
509
|
+
}
|
|
510
|
+
const runs = await Promise.all(
|
|
511
|
+
runIds.map(
|
|
512
|
+
(runId) => readJsonFile(
|
|
513
|
+
join(runtimeRoot, "runs", runId, "run.json")
|
|
514
|
+
)
|
|
515
|
+
)
|
|
516
|
+
);
|
|
517
|
+
return runs.filter(
|
|
518
|
+
(run) => run !== null && run.projectId === projectId
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
async function readJsonFile(path) {
|
|
522
|
+
try {
|
|
523
|
+
return JSON.parse(await readFile(path, "utf8"));
|
|
524
|
+
} catch {
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
function renderProjectExplainReport(report, noColor) {
|
|
529
|
+
const apply = noColor ? (value) => stripAnsi(value) : (value) => value;
|
|
530
|
+
const lines = [
|
|
531
|
+
apply(bold(`Issue dispatch explanation: ${report.issue.identifier}`)),
|
|
532
|
+
report.summary,
|
|
533
|
+
"",
|
|
534
|
+
`State: ${report.issue.state ?? "unknown"}`,
|
|
535
|
+
`Repository: ${report.issue.repository}`,
|
|
536
|
+
"",
|
|
537
|
+
"Checks:"
|
|
538
|
+
];
|
|
539
|
+
for (const check of report.checks) {
|
|
540
|
+
const marker = check.status === "pass" ? green("\u2713") : check.status === "warn" ? yellow("!") : red("\u2717");
|
|
541
|
+
lines.push(` ${apply(marker)} ${check.message}`);
|
|
542
|
+
if (check.hint) {
|
|
543
|
+
lines.push(` Hint: ${check.hint}`);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
lines.push("");
|
|
547
|
+
lines.push("Related commands:");
|
|
548
|
+
lines.push(" gh-symphony workflow preview");
|
|
549
|
+
lines.push(" gh-symphony doctor");
|
|
550
|
+
lines.push(" gh-symphony project status");
|
|
551
|
+
lines.push(" gh-symphony logs --issue " + report.issue.identifier);
|
|
552
|
+
return lines.join("\n") + "\n";
|
|
553
|
+
}
|
|
240
554
|
async function readHttpEndpoint(configDir, projectId) {
|
|
241
555
|
try {
|
|
242
|
-
const content = await readFile(
|
|
556
|
+
const content = await readFile(
|
|
557
|
+
httpStatusPath(configDir, projectId),
|
|
558
|
+
"utf8"
|
|
559
|
+
);
|
|
243
560
|
const state = JSON.parse(content);
|
|
244
561
|
return typeof state.endpoint === "string" && state.endpoint.length > 0 ? state.endpoint : null;
|
|
245
562
|
} catch {
|
|
@@ -251,7 +568,12 @@ async function readProcessUptime(pid) {
|
|
|
251
568
|
return "-";
|
|
252
569
|
}
|
|
253
570
|
try {
|
|
254
|
-
const { stdout } = await execFile("ps", [
|
|
571
|
+
const { stdout } = await execFile("ps", [
|
|
572
|
+
"-o",
|
|
573
|
+
"etime=",
|
|
574
|
+
"-p",
|
|
575
|
+
String(pid)
|
|
576
|
+
]);
|
|
255
577
|
const seconds = parsePsElapsedTime(stdout);
|
|
256
578
|
return seconds === null ? "-" : formatDuration(seconds);
|
|
257
579
|
} catch {
|
|
@@ -660,7 +982,9 @@ async function projectRemove(args, options) {
|
|
|
660
982
|
process.exitCode = 1;
|
|
661
983
|
return;
|
|
662
984
|
}
|
|
663
|
-
const updatedProjects = global.projects.filter(
|
|
985
|
+
const updatedProjects = global.projects.filter(
|
|
986
|
+
(entry) => entry !== projectId
|
|
987
|
+
);
|
|
664
988
|
if (updatedProjects.length === global.projects.length) {
|
|
665
989
|
process.stderr.write(`Project "${projectId}" not found.
|
|
666
990
|
`);
|
|
@@ -19,14 +19,14 @@ import {
|
|
|
19
19
|
resolveClaudeCommandBinary,
|
|
20
20
|
resolveRuntimeCommandBinary,
|
|
21
21
|
runClaudePreflight
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-EEQQWTXS.js";
|
|
23
23
|
import {
|
|
24
24
|
resolveRuntimeRoot
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-IWFX2FMA.js";
|
|
26
26
|
import {
|
|
27
27
|
inspectManagedProjectSelection
|
|
28
|
-
} from "./chunk-
|
|
29
|
-
import "./chunk-
|
|
28
|
+
} from "./chunk-DDL4BWSL.js";
|
|
29
|
+
import "./chunk-QIRE2VXS.js";
|
|
30
30
|
|
|
31
31
|
// src/commands/doctor.ts
|
|
32
32
|
import { constants } from "fs";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
resolveConfigDir
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-QIRE2VXS.js";
|
|
5
5
|
|
|
6
6
|
// src/index.ts
|
|
7
7
|
import { realpathSync } from "fs";
|
|
@@ -278,21 +278,21 @@ ${bashFunction}complete -F _gh_symphony_completion gh-symphony
|
|
|
278
278
|
|
|
279
279
|
// src/index.ts
|
|
280
280
|
var COMMANDS = {
|
|
281
|
-
workflow: () => import("./workflow-
|
|
282
|
-
init: () => import("./init-
|
|
283
|
-
setup: () => import("./setup-
|
|
284
|
-
doctor: () => import("./doctor-
|
|
285
|
-
upgrade: () => import("./upgrade-
|
|
286
|
-
start: () => import("./start-
|
|
287
|
-
stop: () => import("./stop-
|
|
288
|
-
status: () => import("./status-
|
|
289
|
-
run: () => import("./run-
|
|
290
|
-
recover: () => import("./recover-
|
|
291
|
-
logs: () => import("./logs-
|
|
292
|
-
project: () => import("./project-
|
|
293
|
-
repo: () => import("./repo-
|
|
294
|
-
config: () => import("./config-cmd-
|
|
295
|
-
version: () => import("./version-
|
|
281
|
+
workflow: () => import("./workflow-L3KT6HB7.js"),
|
|
282
|
+
init: () => import("./init-54HMKNYI.js"),
|
|
283
|
+
setup: () => import("./setup-TZJSM3QV.js"),
|
|
284
|
+
doctor: () => import("./doctor-EJUMPBMW.js"),
|
|
285
|
+
upgrade: () => import("./upgrade-O33S2SJK.js"),
|
|
286
|
+
start: () => import("./start-RTAHQMR2.js"),
|
|
287
|
+
stop: () => import("./stop-MDKMJPVR.js"),
|
|
288
|
+
status: () => import("./status-F4D52OVK.js"),
|
|
289
|
+
run: () => import("./run-IHN3ZL35.js"),
|
|
290
|
+
recover: () => import("./recover-LTLKMTRX.js"),
|
|
291
|
+
logs: () => import("./logs-GTZ4U5JE.js"),
|
|
292
|
+
project: () => import("./project-RMYMZSFV.js"),
|
|
293
|
+
repo: () => import("./repo-WI7GF6XQ.js"),
|
|
294
|
+
config: () => import("./config-cmd-Z3A7V6NC.js"),
|
|
295
|
+
version: () => import("./version-CW54Q7BK.js")
|
|
296
296
|
};
|
|
297
297
|
function addGlobalOptions(command) {
|
|
298
298
|
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");
|
|
@@ -356,7 +356,10 @@ function createProgram() {
|
|
|
356
356
|
new Command().name("gh-symphony").description("AI Coding Agent Orchestrator").exitOverride().helpOption("-h, --help", "Show help").addHelpCommand("help [command]", "Show help for command").showHelpAfterError("(run with --help for usage)").option("-V, --version", "Show version")
|
|
357
357
|
);
|
|
358
358
|
addGlobalOptions(
|
|
359
|
-
program.command("init", { hidden: true }).description("Alias for 'gh-symphony workflow init'").option("--non-interactive", "Run without prompts").option("--project <id>", "GitHub Project ID or URL").option("--output <path>", "Write WORKFLOW.md to a custom path").option(
|
|
359
|
+
program.command("init", { hidden: true }).description("Alias for 'gh-symphony workflow init'").option("--non-interactive", "Run without prompts").option("--project <id>", "GitHub Project ID or URL").option("--output <path>", "Write WORKFLOW.md to a custom path").option(
|
|
360
|
+
"--runtime <kind>",
|
|
361
|
+
"Runtime preset: codex-app-server or claude-print"
|
|
362
|
+
).option("--skip-skills", "Skip runtime skill generation").option("--skip-context", "Skip .gh-symphony/context.yaml generation").option("--dry-run", "Preview generated files without writing them").allowExcessArguments(false)
|
|
360
363
|
).action(async function() {
|
|
361
364
|
markInvoked();
|
|
362
365
|
const values = this.optsWithGlobals();
|
|
@@ -382,7 +385,10 @@ function createProgram() {
|
|
|
382
385
|
);
|
|
383
386
|
});
|
|
384
387
|
addGlobalOptions(
|
|
385
|
-
workflow.command("init").description("Generate WORKFLOW.md and workflow support files").option("--non-interactive", "Run without prompts").option("--project <id>", "GitHub Project ID or URL").option("--output <path>", "Write WORKFLOW.md to a custom path").option(
|
|
388
|
+
workflow.command("init").description("Generate WORKFLOW.md and workflow support files").option("--non-interactive", "Run without prompts").option("--project <id>", "GitHub Project ID or URL").option("--output <path>", "Write WORKFLOW.md to a custom path").option(
|
|
389
|
+
"--runtime <kind>",
|
|
390
|
+
"Runtime preset: codex-app-server or claude-print"
|
|
391
|
+
).option("--skip-skills", "Skip runtime skill generation").option("--skip-context", "Skip .gh-symphony/context.yaml generation").option("--dry-run", "Preview generated files without writing them").allowExcessArguments(false)
|
|
386
392
|
).action(async function() {
|
|
387
393
|
markInvoked();
|
|
388
394
|
const values = this.optsWithGlobals();
|
|
@@ -459,7 +465,7 @@ function createProgram() {
|
|
|
459
465
|
).option(
|
|
460
466
|
"--web [port]",
|
|
461
467
|
"Expose the control plane web dashboard and API over HTTP"
|
|
462
|
-
).option("--log-level <level>", "Orchestrator lifecycle log level").
|
|
468
|
+
).option("--log-level <level>", "Orchestrator lifecycle log level").addOption(new Option("--project-id <projectId>").hideHelp()).addOption(new Option("--project <projectId>").hideHelp()).allowExcessArguments(false)
|
|
463
469
|
).action(async function() {
|
|
464
470
|
markInvoked();
|
|
465
471
|
const values = this.optsWithGlobals();
|
|
@@ -473,7 +479,7 @@ function createProgram() {
|
|
|
473
479
|
await invokeHandler("start", args, values);
|
|
474
480
|
});
|
|
475
481
|
addGlobalOptions(
|
|
476
|
-
program.command("stop").description("Stop the background orchestrator").option("--force", "Force stop with SIGKILL").
|
|
482
|
+
program.command("stop").description("Stop the background orchestrator").option("--force", "Force stop with SIGKILL").addOption(new Option("--project-id <projectId>").hideHelp()).addOption(new Option("--project <projectId>").hideHelp()).allowExcessArguments(false)
|
|
477
483
|
).action(async function() {
|
|
478
484
|
markInvoked();
|
|
479
485
|
const values = this.optsWithGlobals();
|
|
@@ -483,7 +489,7 @@ function createProgram() {
|
|
|
483
489
|
await invokeHandler("stop", args, values);
|
|
484
490
|
});
|
|
485
491
|
addGlobalOptions(
|
|
486
|
-
program.command("status").description("Show orchestrator status").option("-w, --watch", "Watch status continuously").
|
|
492
|
+
program.command("status").description("Show orchestrator status").option("-w, --watch", "Watch status continuously").addOption(new Option("--project-id <projectId>").hideHelp()).addOption(new Option("--project <projectId>").hideHelp()).allowExcessArguments(false)
|
|
487
493
|
).action(async function() {
|
|
488
494
|
markInvoked();
|
|
489
495
|
const values = this.optsWithGlobals();
|
|
@@ -612,6 +618,16 @@ function createProgram() {
|
|
|
612
618
|
pushOption(args, "--watch", values.watch);
|
|
613
619
|
await invokeHandler("project", args, values);
|
|
614
620
|
});
|
|
621
|
+
addGlobalOptions(
|
|
622
|
+
project.command("explain").description("Explain why a project issue is not dispatching").argument("<issue>", "Issue identifier, for example owner/repo#123").option("--project-id <projectId>", "Project identifier").option("--workflow <path>", "Path to the WORKFLOW.md file to evaluate").addOption(new Option("--project <projectId>").hideHelp()).allowExcessArguments(false)
|
|
623
|
+
).action(async function(issue) {
|
|
624
|
+
markInvoked();
|
|
625
|
+
const values = this.optsWithGlobals();
|
|
626
|
+
const args = ["explain", issue];
|
|
627
|
+
pushOption(args, "--project-id", resolveProjectId(values));
|
|
628
|
+
pushOption(args, "--workflow", values.workflow);
|
|
629
|
+
await invokeHandler("project", args, values);
|
|
630
|
+
});
|
|
615
631
|
const repo = addGlobalOptions(
|
|
616
632
|
program.command("repo").description("Manage repositories in the active project")
|
|
617
633
|
);
|
|
@@ -629,6 +645,57 @@ function createProgram() {
|
|
|
629
645
|
this.optsWithGlobals()
|
|
630
646
|
);
|
|
631
647
|
});
|
|
648
|
+
addGlobalOptions(
|
|
649
|
+
repo.command("init").description("Initialize gh-symphony for the current repository").option("--repo-dir <path>", "Repository directory").option("--workflow-file <path>", "Use a custom WORKFLOW.md path").addOption(new Option("--project-id <projectId>").hideHelp()).addOption(new Option("--project <projectId>").hideHelp()).allowExcessArguments(false)
|
|
650
|
+
).action(async function() {
|
|
651
|
+
markInvoked();
|
|
652
|
+
const values = this.optsWithGlobals();
|
|
653
|
+
const args = ["init"];
|
|
654
|
+
pushOption(args, "--repo-dir", values.repoDir);
|
|
655
|
+
pushOption(args, "--workflow-file", values.workflowFile);
|
|
656
|
+
pushOption(args, "--project-id", resolveProjectId(values));
|
|
657
|
+
await invokeHandler("repo", args, values);
|
|
658
|
+
});
|
|
659
|
+
addGlobalOptions(
|
|
660
|
+
repo.command("start").description("Start the orchestrator for the current repository").option("-d, --daemon", "Start in daemon mode").option("--once", "Run a single orchestration tick and exit").option(
|
|
661
|
+
"--http [port]",
|
|
662
|
+
"Expose dashboard and refresh endpoints over HTTP"
|
|
663
|
+
).option(
|
|
664
|
+
"--web [port]",
|
|
665
|
+
"Expose the control plane web dashboard and API over HTTP"
|
|
666
|
+
).option("--log-level <level>", "Orchestrator lifecycle log level").addOption(new Option("--project-id <projectId>").hideHelp()).addOption(new Option("--project <projectId>").hideHelp()).allowExcessArguments(false)
|
|
667
|
+
).action(async function() {
|
|
668
|
+
markInvoked();
|
|
669
|
+
const values = this.optsWithGlobals();
|
|
670
|
+
const args = ["start"];
|
|
671
|
+
pushOption(args, "--project-id", resolveProjectId(values));
|
|
672
|
+
pushOption(args, "--daemon", values.daemon);
|
|
673
|
+
pushOption(args, "--once", values.once);
|
|
674
|
+
pushOption(args, "--http", values.http);
|
|
675
|
+
pushOption(args, "--web", values.web);
|
|
676
|
+
pushOption(args, "--log-level", values.logLevel);
|
|
677
|
+
await invokeHandler("repo", args, values);
|
|
678
|
+
});
|
|
679
|
+
addGlobalOptions(
|
|
680
|
+
repo.command("status").description("Show current repository orchestrator status").option("-w, --watch", "Watch status continuously").addOption(new Option("--project-id <projectId>").hideHelp()).addOption(new Option("--project <projectId>").hideHelp()).allowExcessArguments(false)
|
|
681
|
+
).action(async function() {
|
|
682
|
+
markInvoked();
|
|
683
|
+
const values = this.optsWithGlobals();
|
|
684
|
+
const args = ["status"];
|
|
685
|
+
pushOption(args, "--project-id", resolveProjectId(values));
|
|
686
|
+
pushOption(args, "--watch", values.watch);
|
|
687
|
+
await invokeHandler("repo", args, values);
|
|
688
|
+
});
|
|
689
|
+
addGlobalOptions(
|
|
690
|
+
repo.command("stop").description("Stop the current repository background orchestrator").option("--force", "Force stop with SIGKILL").addOption(new Option("--project-id <projectId>").hideHelp()).addOption(new Option("--project <projectId>").hideHelp()).allowExcessArguments(false)
|
|
691
|
+
).action(async function() {
|
|
692
|
+
markInvoked();
|
|
693
|
+
const values = this.optsWithGlobals();
|
|
694
|
+
const args = ["stop"];
|
|
695
|
+
pushOption(args, "--project-id", resolveProjectId(values));
|
|
696
|
+
pushOption(args, "--force", values.force);
|
|
697
|
+
await invokeHandler("repo", args, values);
|
|
698
|
+
});
|
|
632
699
|
addGlobalOptions(
|
|
633
700
|
repo.command("add").description("Add a repository").argument("<owner/name>", "Repository spec").allowExcessArguments(false)
|
|
634
701
|
).action(async function(repoSpec) {
|
|
@@ -15,10 +15,10 @@ import {
|
|
|
15
15
|
writeConfig,
|
|
16
16
|
writeEcosystem,
|
|
17
17
|
writeWorkflowPlan
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-HMLBBZNY.js";
|
|
19
19
|
import "./chunk-C67H3OUL.js";
|
|
20
|
-
import "./chunk-
|
|
21
|
-
import "./chunk-
|
|
20
|
+
import "./chunk-EEQQWTXS.js";
|
|
21
|
+
import "./chunk-QIRE2VXS.js";
|
|
22
22
|
export {
|
|
23
23
|
abortIfCancelled,
|
|
24
24
|
buildAutomaticStateMappings,
|