@gh-symphony/cli 0.0.17 → 0.0.19
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 +105 -9
- package/dist/{chunk-EFMFGOWM.js → chunk-6CI3UUMH.js} +282 -57
- package/dist/chunk-C7G7RJ4G.js +146 -0
- package/dist/{chunk-MHIWAIVD.js → chunk-GKENCODJ.js} +141 -53
- package/dist/{project-557FE2GD.js → chunk-H2YXSYOZ.js} +108 -92
- package/dist/{chunk-TF3QNWNC.js → chunk-M3IFVLQS.js} +246 -212
- package/dist/{chunk-IWR4UQEJ.js → chunk-RN2PACNV.js} +350 -523
- package/dist/chunk-TILHWBP6.js +638 -0
- package/dist/{chunk-6HBZC3BE.js → chunk-XN5ABWZ6.js} +23 -5
- package/dist/{chunk-76QPITKI.js → chunk-Y6TYJMNT.js} +1 -1
- package/dist/{config-cmd-AZ7POMAA.js → config-cmd-DNXNL26Z.js} +3 -1
- package/dist/doctor-IYHCFXOZ.js +1126 -0
- package/dist/index.js +157 -19
- package/dist/init-KZT6YNOH.js +33 -0
- package/dist/{logs-6LNGT2GF.js → logs-6JKKYDGJ.js} +1 -1
- package/dist/project-DNALEWO3.js +22 -0
- package/dist/{recover-LVBI2TGH.js → recover-C3V2QAUB.js} +3 -3
- package/dist/repo-HDDE7OUI.js +321 -0
- package/dist/{run-WITYAYFZ.js → run-XI2S5Y4V.js} +3 -3
- package/dist/setup-K4CYYJBF.js +431 -0
- package/dist/{start-JUFKNL3N.js → start-M6IQGRFO.js} +5 -5
- package/dist/{status-3WK5BWRZ.js → status-QSCFVGRQ.js} +2 -2
- package/dist/{stop-AA3AP5M6.js → stop-7MFCBQVW.js} +2 -2
- package/dist/upgrade-F4VE4XBS.js +165 -0
- package/dist/{version-YVM2A25J.js → version-Y5RYNWMF.js} +1 -1
- package/dist/worker-entry.js +39 -11
- package/dist/workflow-TBIFY5MO.js +497 -0
- package/package.json +4 -4
- package/dist/chunk-JO3AXHQI.js +0 -130
- package/dist/chunk-TH5QPO3Y.js +0 -67
- package/dist/init-EZXQAXZM.js +0 -17
- package/dist/repo-R3XBIVAX.js +0 -121
package/dist/index.js
CHANGED
|
@@ -15,7 +15,10 @@ import {
|
|
|
15
15
|
|
|
16
16
|
// src/completion.ts
|
|
17
17
|
var TOP_LEVEL_COMMANDS = [
|
|
18
|
-
"
|
|
18
|
+
"workflow",
|
|
19
|
+
"setup",
|
|
20
|
+
"doctor",
|
|
21
|
+
"upgrade",
|
|
19
22
|
"start",
|
|
20
23
|
"stop",
|
|
21
24
|
"status",
|
|
@@ -44,6 +47,35 @@ var GLOBAL_OPTIONS = [
|
|
|
44
47
|
var GLOBAL_OPTIONS_WITH_VALUES = ["--config", "--config-dir"];
|
|
45
48
|
var COMMAND_OPTIONS = {
|
|
46
49
|
completion: ["bash", "zsh", "fish"],
|
|
50
|
+
workflow: ["init", "validate", "preview"],
|
|
51
|
+
"workflow:init": [
|
|
52
|
+
"--non-interactive",
|
|
53
|
+
"--project",
|
|
54
|
+
"--output",
|
|
55
|
+
"--skip-skills",
|
|
56
|
+
"--skip-context",
|
|
57
|
+
"--dry-run",
|
|
58
|
+
...GLOBAL_OPTIONS
|
|
59
|
+
],
|
|
60
|
+
"workflow:validate": ["--file", ...GLOBAL_OPTIONS],
|
|
61
|
+
"workflow:preview": [
|
|
62
|
+
"--file",
|
|
63
|
+
"--sample",
|
|
64
|
+
"--attempt",
|
|
65
|
+
...GLOBAL_OPTIONS
|
|
66
|
+
],
|
|
67
|
+
setup: [
|
|
68
|
+
"--non-interactive",
|
|
69
|
+
"--project",
|
|
70
|
+
"--workspace-dir",
|
|
71
|
+
"--assigned-only",
|
|
72
|
+
"--output",
|
|
73
|
+
"--skip-skills",
|
|
74
|
+
"--skip-context",
|
|
75
|
+
...GLOBAL_OPTIONS
|
|
76
|
+
],
|
|
77
|
+
doctor: ["--project-id", "--project", ...GLOBAL_OPTIONS],
|
|
78
|
+
upgrade: [...GLOBAL_OPTIONS],
|
|
47
79
|
start: ["--project-id", "--project", "--daemon", "-d", ...GLOBAL_OPTIONS],
|
|
48
80
|
stop: ["--project-id", "--project", "--force", ...GLOBAL_OPTIONS],
|
|
49
81
|
status: ["--project-id", "--project", "--watch", "-w", ...GLOBAL_OPTIONS],
|
|
@@ -85,10 +117,11 @@ var COMMAND_OPTIONS = {
|
|
|
85
117
|
"-w",
|
|
86
118
|
...GLOBAL_OPTIONS
|
|
87
119
|
],
|
|
88
|
-
repo: ["list", "add", "remove"],
|
|
120
|
+
repo: ["list", "add", "remove", "sync"],
|
|
89
121
|
"repo:list": [...GLOBAL_OPTIONS],
|
|
90
122
|
"repo:add": [...GLOBAL_OPTIONS],
|
|
91
123
|
"repo:remove": [...GLOBAL_OPTIONS],
|
|
124
|
+
"repo:sync": ["--dry-run", "--prune", ...GLOBAL_OPTIONS],
|
|
92
125
|
config: ["show", "set", "edit"],
|
|
93
126
|
"config:show": [...GLOBAL_OPTIONS],
|
|
94
127
|
"config:set": [...GLOBAL_OPTIONS],
|
|
@@ -107,7 +140,7 @@ function renderBashCasePatterns() {
|
|
|
107
140
|
return
|
|
108
141
|
;;`;
|
|
109
142
|
}
|
|
110
|
-
if (command === "project" || command === "repo" || command === "config") {
|
|
143
|
+
if (command === "workflow" || command === "project" || command === "repo" || command === "config") {
|
|
111
144
|
return ` ${command})
|
|
112
145
|
COMPREPLY=( $(compgen -W "${quoteWords(values)}" -- "$cur") )
|
|
113
146
|
return
|
|
@@ -148,6 +181,11 @@ function renderFishLines() {
|
|
|
148
181
|
`complete -c gh-symphony -f -n '__fish_seen_subcommand_from config' -a '${subcommand}'`
|
|
149
182
|
);
|
|
150
183
|
}
|
|
184
|
+
for (const subcommand of COMMAND_OPTIONS.workflow ?? []) {
|
|
185
|
+
lines.push(
|
|
186
|
+
`complete -c gh-symphony -f -n '__fish_seen_subcommand_from workflow' -a '${subcommand}'`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
151
189
|
for (const shell of COMMAND_OPTIONS.completion ?? []) {
|
|
152
190
|
lines.push(
|
|
153
191
|
`complete -c gh-symphony -f -n '__fish_seen_subcommand_from completion' -a '${shell}'`
|
|
@@ -217,7 +255,7 @@ _gh_symphony_completion() {
|
|
|
217
255
|
return
|
|
218
256
|
fi
|
|
219
257
|
|
|
220
|
-
if [[ "\${path}" == "project" || "\${path}" == "repo" || "\${path}" == "config" || "\${path}" == "completion" ]]; then
|
|
258
|
+
if [[ "\${path}" == "workflow" || "\${path}" == "project" || "\${path}" == "repo" || "\${path}" == "config" || "\${path}" == "completion" ]]; then
|
|
221
259
|
if [[ -n "\${GH_SYMPHONY_SUBCOMMAND}" ]]; then
|
|
222
260
|
path="\${path}:\${GH_SYMPHONY_SUBCOMMAND}"
|
|
223
261
|
fi
|
|
@@ -240,17 +278,21 @@ ${bashFunction}complete -F _gh_symphony_completion gh-symphony
|
|
|
240
278
|
|
|
241
279
|
// src/index.ts
|
|
242
280
|
var COMMANDS = {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
281
|
+
workflow: () => import("./workflow-TBIFY5MO.js"),
|
|
282
|
+
init: () => import("./init-KZT6YNOH.js"),
|
|
283
|
+
setup: () => import("./setup-K4CYYJBF.js"),
|
|
284
|
+
doctor: () => import("./doctor-IYHCFXOZ.js"),
|
|
285
|
+
upgrade: () => import("./upgrade-F4VE4XBS.js"),
|
|
286
|
+
start: () => import("./start-M6IQGRFO.js"),
|
|
287
|
+
stop: () => import("./stop-7MFCBQVW.js"),
|
|
288
|
+
status: () => import("./status-QSCFVGRQ.js"),
|
|
289
|
+
run: () => import("./run-XI2S5Y4V.js"),
|
|
290
|
+
recover: () => import("./recover-C3V2QAUB.js"),
|
|
291
|
+
logs: () => import("./logs-6JKKYDGJ.js"),
|
|
292
|
+
project: () => import("./project-DNALEWO3.js"),
|
|
293
|
+
repo: () => import("./repo-HDDE7OUI.js"),
|
|
294
|
+
config: () => import("./config-cmd-DNXNL26Z.js"),
|
|
295
|
+
version: () => import("./version-Y5RYNWMF.js")
|
|
254
296
|
};
|
|
255
297
|
function addGlobalOptions(command) {
|
|
256
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");
|
|
@@ -314,19 +356,104 @@ function createProgram() {
|
|
|
314
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")
|
|
315
357
|
);
|
|
316
358
|
addGlobalOptions(
|
|
317
|
-
program.command("init").description("
|
|
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("--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
|
+
).action(async function() {
|
|
361
|
+
markInvoked();
|
|
362
|
+
const values = this.optsWithGlobals();
|
|
363
|
+
const args = ["init"];
|
|
364
|
+
pushOption(args, "--non-interactive", values.nonInteractive);
|
|
365
|
+
pushOption(args, "--project", values.project);
|
|
366
|
+
pushOption(args, "--output", values.output);
|
|
367
|
+
pushOption(args, "--skip-skills", values.skipSkills);
|
|
368
|
+
pushOption(args, "--skip-context", values.skipContext);
|
|
369
|
+
pushOption(args, "--dry-run", values.dryRun);
|
|
370
|
+
await invokeHandler("workflow", args, values);
|
|
371
|
+
});
|
|
372
|
+
const workflow = addGlobalOptions(
|
|
373
|
+
program.command("workflow").description("Manage WORKFLOW.md authoring")
|
|
374
|
+
);
|
|
375
|
+
workflow.action(async function() {
|
|
376
|
+
markInvoked();
|
|
377
|
+
await invokeHandler(
|
|
378
|
+
"workflow",
|
|
379
|
+
[],
|
|
380
|
+
this.optsWithGlobals()
|
|
381
|
+
);
|
|
382
|
+
});
|
|
383
|
+
addGlobalOptions(
|
|
384
|
+
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("--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)
|
|
318
385
|
).action(async function() {
|
|
319
386
|
markInvoked();
|
|
320
|
-
|
|
387
|
+
const values = this.optsWithGlobals();
|
|
388
|
+
const args = ["init"];
|
|
389
|
+
pushOption(args, "--non-interactive", values.nonInteractive);
|
|
390
|
+
pushOption(args, "--project", values.project);
|
|
391
|
+
pushOption(args, "--output", values.output);
|
|
392
|
+
pushOption(args, "--skip-skills", values.skipSkills);
|
|
393
|
+
pushOption(args, "--skip-context", values.skipContext);
|
|
394
|
+
pushOption(args, "--dry-run", values.dryRun);
|
|
395
|
+
await invokeHandler("workflow", args, values);
|
|
321
396
|
});
|
|
322
397
|
addGlobalOptions(
|
|
323
|
-
|
|
398
|
+
workflow.command("validate").description("Parse and strictly validate a WORKFLOW.md file").option("--file <path>", "Validate a custom WORKFLOW.md path").allowExcessArguments(false)
|
|
399
|
+
).action(async function() {
|
|
400
|
+
markInvoked();
|
|
401
|
+
const values = this.optsWithGlobals();
|
|
402
|
+
const args = ["validate"];
|
|
403
|
+
pushOption(args, "--file", values.file);
|
|
404
|
+
await invokeHandler("workflow", args, values);
|
|
405
|
+
});
|
|
406
|
+
addGlobalOptions(
|
|
407
|
+
workflow.command("preview").description("Render the final worker prompt from a sample issue").option("--file <path>", "Read a custom WORKFLOW.md path").option("--sample <json>", "Read sample issue JSON from a file").option("--attempt <n>", "Render as retry attempt n").allowExcessArguments(false)
|
|
408
|
+
).action(async function() {
|
|
409
|
+
markInvoked();
|
|
410
|
+
const values = this.optsWithGlobals();
|
|
411
|
+
const args = ["preview"];
|
|
412
|
+
pushOption(args, "--file", values.file);
|
|
413
|
+
pushOption(args, "--sample", values.sample);
|
|
414
|
+
pushOption(args, "--attempt", values.attempt);
|
|
415
|
+
await invokeHandler("workflow", args, values);
|
|
416
|
+
});
|
|
417
|
+
addGlobalOptions(
|
|
418
|
+
program.command("setup").description("Run the one-command first-run setup flow").option("--non-interactive", "Run without prompts").option("--project <id>", "GitHub Project ID or URL").option("--workspace-dir <path>", "Workspace directory").option("--assigned-only", "Limit processing to assigned issues").option("--output <path>", "Write WORKFLOW.md to a custom path").option("--skip-skills", "Skip runtime skill generation").option("--skip-context", "Skip .gh-symphony/context.yaml generation").allowExcessArguments(false)
|
|
419
|
+
).action(async function() {
|
|
420
|
+
markInvoked();
|
|
421
|
+
const values = this.optsWithGlobals();
|
|
422
|
+
const args = [];
|
|
423
|
+
pushOption(args, "--non-interactive", values.nonInteractive);
|
|
424
|
+
pushOption(args, "--project", values.project);
|
|
425
|
+
pushOption(args, "--workspace-dir", values.workspaceDir);
|
|
426
|
+
pushOption(args, "--assigned-only", values.assignedOnly);
|
|
427
|
+
pushOption(args, "--output", values.output);
|
|
428
|
+
pushOption(args, "--skip-skills", values.skipSkills);
|
|
429
|
+
pushOption(args, "--skip-context", values.skipContext);
|
|
430
|
+
await invokeHandler("setup", args, values);
|
|
431
|
+
});
|
|
432
|
+
addGlobalOptions(
|
|
433
|
+
program.command("doctor").description("Run diagnostics and optional first-run remediation").option("--project-id <projectId>", "Project identifier").option("--fix", "Apply safe remediation steps and print manual follow-ups").addOption(new Option("--project <projectId>").hideHelp()).allowExcessArguments(false)
|
|
434
|
+
).action(async function() {
|
|
435
|
+
markInvoked();
|
|
436
|
+
const values = this.optsWithGlobals();
|
|
437
|
+
const args = [];
|
|
438
|
+
pushOption(args, "--project-id", resolveProjectId(values));
|
|
439
|
+
pushOption(args, "--fix", values.fix);
|
|
440
|
+
await invokeHandler("doctor", args, values);
|
|
441
|
+
});
|
|
442
|
+
addGlobalOptions(
|
|
443
|
+
program.command("upgrade").description("Upgrade the CLI to the latest published version").allowExcessArguments(false)
|
|
444
|
+
).action(async function() {
|
|
445
|
+
markInvoked();
|
|
446
|
+
await invokeHandler("upgrade", [], this.optsWithGlobals());
|
|
447
|
+
});
|
|
448
|
+
addGlobalOptions(
|
|
449
|
+
program.command("start").description("Start the orchestrator").option("-d, --daemon", "Start in daemon mode").option("--once", "Run a single orchestration tick and exit").option("--http [port]", "Expose dashboard and refresh endpoints over HTTP").option("--log-level <level>", "Orchestrator lifecycle log level").option("--project-id <projectId>", "Project identifier").addOption(new Option("--project <projectId>").hideHelp()).allowExcessArguments(false)
|
|
324
450
|
).action(async function() {
|
|
325
451
|
markInvoked();
|
|
326
452
|
const values = this.optsWithGlobals();
|
|
327
453
|
const args = [];
|
|
328
454
|
pushOption(args, "--project-id", resolveProjectId(values));
|
|
329
455
|
pushOption(args, "--daemon", values.daemon);
|
|
456
|
+
pushOption(args, "--once", values.once);
|
|
330
457
|
pushOption(args, "--http", values.http);
|
|
331
458
|
pushOption(args, "--log-level", values.logLevel);
|
|
332
459
|
await invokeHandler("start", args, values);
|
|
@@ -422,13 +549,14 @@ function createProgram() {
|
|
|
422
549
|
);
|
|
423
550
|
});
|
|
424
551
|
addGlobalOptions(
|
|
425
|
-
project.command("start").description("Start a specific project").option("-d, --daemon", "Start in daemon mode").option("--http [port]", "Expose dashboard and refresh endpoints over HTTP").option("--log-level <level>", "Orchestrator lifecycle log level").option("--project-id <projectId>", "Project identifier").addOption(new Option("--project <projectId>").hideHelp()).allowExcessArguments(false)
|
|
552
|
+
project.command("start").description("Start a specific project").option("-d, --daemon", "Start in daemon mode").option("--once", "Run a single orchestration tick and exit").option("--http [port]", "Expose dashboard and refresh endpoints over HTTP").option("--log-level <level>", "Orchestrator lifecycle log level").option("--project-id <projectId>", "Project identifier").addOption(new Option("--project <projectId>").hideHelp()).allowExcessArguments(false)
|
|
426
553
|
).action(async function() {
|
|
427
554
|
markInvoked();
|
|
428
555
|
const values = this.optsWithGlobals();
|
|
429
556
|
const args = ["start"];
|
|
430
557
|
pushOption(args, "--project-id", resolveProjectId(values));
|
|
431
558
|
pushOption(args, "--daemon", values.daemon);
|
|
559
|
+
pushOption(args, "--once", values.once);
|
|
432
560
|
pushOption(args, "--http", values.http);
|
|
433
561
|
pushOption(args, "--log-level", values.logLevel);
|
|
434
562
|
await invokeHandler("project", args, values);
|
|
@@ -500,6 +628,16 @@ function createProgram() {
|
|
|
500
628
|
this.optsWithGlobals()
|
|
501
629
|
);
|
|
502
630
|
});
|
|
631
|
+
addGlobalOptions(
|
|
632
|
+
repo.command("sync").description("Sync repositories from the active GitHub Project").option("--dry-run", "Preview repository changes without writing config").option("--prune", "Remove local repositories that are no longer linked").allowExcessArguments(false)
|
|
633
|
+
).action(async function() {
|
|
634
|
+
markInvoked();
|
|
635
|
+
const values = this.optsWithGlobals();
|
|
636
|
+
const args = ["sync"];
|
|
637
|
+
pushOption(args, "--dry-run", values.dryRun);
|
|
638
|
+
pushOption(args, "--prune", values.prune);
|
|
639
|
+
await invokeHandler("repo", args, values);
|
|
640
|
+
});
|
|
503
641
|
const config = addGlobalOptions(
|
|
504
642
|
program.command("config").description("Manage CLI configuration")
|
|
505
643
|
);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
abortIfCancelled,
|
|
4
|
+
buildAutomaticStateMappings,
|
|
5
|
+
buildDryRunJsonResult,
|
|
6
|
+
generateProjectId,
|
|
7
|
+
init_default,
|
|
8
|
+
planEcosystem,
|
|
9
|
+
planWorkflowArtifacts,
|
|
10
|
+
promptStateMappings,
|
|
11
|
+
renderDryRunPreview,
|
|
12
|
+
resolveStatusField,
|
|
13
|
+
writeConfig,
|
|
14
|
+
writeEcosystem,
|
|
15
|
+
writeWorkflowPlan
|
|
16
|
+
} from "./chunk-RN2PACNV.js";
|
|
17
|
+
import "./chunk-TILHWBP6.js";
|
|
18
|
+
import "./chunk-ROGRTUFI.js";
|
|
19
|
+
export {
|
|
20
|
+
abortIfCancelled,
|
|
21
|
+
buildAutomaticStateMappings,
|
|
22
|
+
buildDryRunJsonResult,
|
|
23
|
+
init_default as default,
|
|
24
|
+
generateProjectId,
|
|
25
|
+
planEcosystem,
|
|
26
|
+
planWorkflowArtifacts,
|
|
27
|
+
promptStateMappings,
|
|
28
|
+
renderDryRunPreview,
|
|
29
|
+
resolveStatusField,
|
|
30
|
+
writeConfig,
|
|
31
|
+
writeEcosystem,
|
|
32
|
+
writeWorkflowPlan
|
|
33
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
project_default,
|
|
4
|
+
promptProjectRegistrationOptions,
|
|
5
|
+
renderProjectRegistrationSummary
|
|
6
|
+
} from "./chunk-H2YXSYOZ.js";
|
|
7
|
+
import "./chunk-RN2PACNV.js";
|
|
8
|
+
import "./chunk-GKENCODJ.js";
|
|
9
|
+
import "./chunk-6CI3UUMH.js";
|
|
10
|
+
import "./chunk-M3IFVLQS.js";
|
|
11
|
+
import "./chunk-TILHWBP6.js";
|
|
12
|
+
import "./chunk-XN5ABWZ6.js";
|
|
13
|
+
import "./chunk-MVRF7BES.js";
|
|
14
|
+
import "./chunk-5NV3LSAJ.js";
|
|
15
|
+
import "./chunk-Y6TYJMNT.js";
|
|
16
|
+
import "./chunk-C7G7RJ4G.js";
|
|
17
|
+
import "./chunk-ROGRTUFI.js";
|
|
18
|
+
export {
|
|
19
|
+
project_default as default,
|
|
20
|
+
promptProjectRegistrationOptions,
|
|
21
|
+
renderProjectRegistrationSummary
|
|
22
|
+
};
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
runCli
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-6CI3UUMH.js";
|
|
5
|
+
import "./chunk-M3IFVLQS.js";
|
|
6
6
|
import {
|
|
7
7
|
resolveRuntimeRoot
|
|
8
8
|
} from "./chunk-5NV3LSAJ.js";
|
|
9
9
|
import {
|
|
10
10
|
handleMissingManagedProjectConfig,
|
|
11
11
|
resolveManagedProjectConfig
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-C7G7RJ4G.js";
|
|
13
13
|
import "./chunk-ROGRTUFI.js";
|
|
14
14
|
|
|
15
15
|
// src/commands/recover.ts
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
GitHubScopeError,
|
|
4
|
+
checkRequiredScopes,
|
|
5
|
+
createClient,
|
|
6
|
+
getGhToken,
|
|
7
|
+
getProjectDetail,
|
|
8
|
+
validateToken
|
|
9
|
+
} from "./chunk-TILHWBP6.js";
|
|
10
|
+
import {
|
|
11
|
+
loadActiveProjectConfig,
|
|
12
|
+
loadGlobalConfig,
|
|
13
|
+
saveProjectConfig
|
|
14
|
+
} from "./chunk-ROGRTUFI.js";
|
|
15
|
+
|
|
16
|
+
// src/commands/repo.ts
|
|
17
|
+
var handler = async (args, options) => {
|
|
18
|
+
const [subcommand, ...rest] = args;
|
|
19
|
+
switch (subcommand) {
|
|
20
|
+
case "list":
|
|
21
|
+
await repoList(options);
|
|
22
|
+
break;
|
|
23
|
+
case "add":
|
|
24
|
+
await repoAdd(rest, options);
|
|
25
|
+
break;
|
|
26
|
+
case "remove":
|
|
27
|
+
await repoRemove(rest, options);
|
|
28
|
+
break;
|
|
29
|
+
case "sync":
|
|
30
|
+
await repoSync(rest, options);
|
|
31
|
+
break;
|
|
32
|
+
default:
|
|
33
|
+
process.stderr.write(
|
|
34
|
+
"Usage: gh-symphony repo <list|add|remove|sync> [repo]\n"
|
|
35
|
+
);
|
|
36
|
+
process.exitCode = 2;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
var repo_default = handler;
|
|
40
|
+
function repoKey(repo) {
|
|
41
|
+
return `${repo.owner}/${repo.name}`.toLowerCase();
|
|
42
|
+
}
|
|
43
|
+
function toRepoConfigEntry(repo) {
|
|
44
|
+
return {
|
|
45
|
+
owner: repo.owner,
|
|
46
|
+
name: repo.name,
|
|
47
|
+
cloneUrl: repo.cloneUrl
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function parseRepoSyncFlags(args) {
|
|
51
|
+
const flags = { dryRun: false, prune: false };
|
|
52
|
+
for (const arg of args) {
|
|
53
|
+
if (arg === "--dry-run") {
|
|
54
|
+
flags.dryRun = true;
|
|
55
|
+
} else if (arg === "--prune") {
|
|
56
|
+
flags.prune = true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return flags;
|
|
60
|
+
}
|
|
61
|
+
function displayScopeError(error) {
|
|
62
|
+
const plural = error.requiredScopes.length === 1 ? "" : "s";
|
|
63
|
+
process.stderr.write(
|
|
64
|
+
`Token is missing required scope${plural}: ${error.requiredScopes.join(", ")}
|
|
65
|
+
`
|
|
66
|
+
);
|
|
67
|
+
const currentSet = new Set(error.currentScopes.map((scope) => scope.toLowerCase()));
|
|
68
|
+
const scopesToAdd = ["repo", "read:org", "project"].filter(
|
|
69
|
+
(scope) => !currentSet.has(scope)
|
|
70
|
+
);
|
|
71
|
+
const scopeArg = scopesToAdd.length > 0 ? scopesToAdd.join(",") : error.requiredScopes.join(",");
|
|
72
|
+
process.stderr.write(
|
|
73
|
+
`Run 'gh auth refresh --scopes ${scopeArg}' and try again.
|
|
74
|
+
`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
function formatRepoSpec(repo) {
|
|
78
|
+
return `${repo.owner}/${repo.name}`;
|
|
79
|
+
}
|
|
80
|
+
function sortRepos(repos) {
|
|
81
|
+
return [...repos].sort(
|
|
82
|
+
(left, right) => formatRepoSpec(left).localeCompare(formatRepoSpec(right))
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
function renderRepoGroup(label, repos) {
|
|
86
|
+
if (repos.length === 0) {
|
|
87
|
+
return [`${label}: none`];
|
|
88
|
+
}
|
|
89
|
+
return [label, ...sortRepos(repos).map((repo) => ` ${formatRepoSpec(repo)}`)];
|
|
90
|
+
}
|
|
91
|
+
function buildSyncedRepositories(currentRepos, linkedMap, linkedRepositories, prune) {
|
|
92
|
+
const retained = currentRepos.filter((repo) => linkedMap.has(repoKey(repo)) || !prune).map((repo) => {
|
|
93
|
+
const linked = linkedMap.get(repoKey(repo));
|
|
94
|
+
return linked ? toRepoConfigEntry(linked) : { ...repo };
|
|
95
|
+
});
|
|
96
|
+
const currentKeys = new Set(currentRepos.map((repo) => repoKey(repo)));
|
|
97
|
+
const additions = sortRepos(
|
|
98
|
+
linkedRepositories.filter((repo) => !currentKeys.has(repoKey(repo))).map(toRepoConfigEntry)
|
|
99
|
+
);
|
|
100
|
+
return [...retained, ...additions];
|
|
101
|
+
}
|
|
102
|
+
function writeRepoSummary(summary, options) {
|
|
103
|
+
if (options.json) {
|
|
104
|
+
process.stdout.write(JSON.stringify(summary, null, 2) + "\n");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
process.stdout.write(
|
|
108
|
+
[
|
|
109
|
+
`Repository sync ${summary.dryRun ? "preview" : "complete"} for ${summary.projectId}`,
|
|
110
|
+
`Mode: ${summary.prune ? "prune" : "additive"}`,
|
|
111
|
+
...renderRepoGroup("Added", summary.added),
|
|
112
|
+
...renderRepoGroup("Removed", summary.removed),
|
|
113
|
+
...renderRepoGroup("Unchanged", summary.unchanged),
|
|
114
|
+
summary.dryRun ? "No config changes written." : "Configuration updated."
|
|
115
|
+
].join("\n") + "\n"
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
async function repoList(options) {
|
|
119
|
+
const ws = await loadActiveProjectConfig(options.configDir);
|
|
120
|
+
if (!ws) {
|
|
121
|
+
process.stderr.write("No project configured.\n");
|
|
122
|
+
process.exitCode = 1;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (options.json) {
|
|
126
|
+
process.stdout.write(JSON.stringify(ws.repositories, null, 2) + "\n");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
process.stdout.write("Repositories:\n");
|
|
130
|
+
for (const repo of ws.repositories) {
|
|
131
|
+
process.stdout.write(` ${repo.owner}/${repo.name}
|
|
132
|
+
`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async function repoAdd(args, options) {
|
|
136
|
+
const [repoSpec] = args;
|
|
137
|
+
if (!repoSpec || !repoSpec.includes("/")) {
|
|
138
|
+
process.stderr.write("Usage: gh-symphony repo add <owner/name>\n");
|
|
139
|
+
process.exitCode = 2;
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const global = await loadGlobalConfig(options.configDir);
|
|
143
|
+
if (!global?.activeProject) {
|
|
144
|
+
process.stderr.write("No active project.\n");
|
|
145
|
+
process.exitCode = 1;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const ws = await loadActiveProjectConfig(options.configDir);
|
|
149
|
+
if (!ws) {
|
|
150
|
+
process.stderr.write("Project config missing.\n");
|
|
151
|
+
process.exitCode = 1;
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const [owner, name] = repoSpec.split("/");
|
|
155
|
+
if (!owner || !name) {
|
|
156
|
+
process.stderr.write("Invalid repo format. Use: owner/name\n");
|
|
157
|
+
process.exitCode = 2;
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (ws.repositories.some(
|
|
161
|
+
(r) => r.owner === owner && r.name === name
|
|
162
|
+
)) {
|
|
163
|
+
process.stdout.write(`Repository ${repoSpec} is already configured.
|
|
164
|
+
`);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
ws.repositories.push({
|
|
168
|
+
owner,
|
|
169
|
+
name,
|
|
170
|
+
cloneUrl: `https://github.com/${owner}/${name}.git`
|
|
171
|
+
});
|
|
172
|
+
await saveProjectConfig(options.configDir, global.activeProject, ws);
|
|
173
|
+
process.stdout.write(`Added repository: ${repoSpec}
|
|
174
|
+
`);
|
|
175
|
+
}
|
|
176
|
+
async function repoRemove(args, options) {
|
|
177
|
+
const [repoSpec] = args;
|
|
178
|
+
if (!repoSpec || !repoSpec.includes("/")) {
|
|
179
|
+
process.stderr.write("Usage: gh-symphony repo remove <owner/name>\n");
|
|
180
|
+
process.exitCode = 2;
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const global = await loadGlobalConfig(options.configDir);
|
|
184
|
+
if (!global?.activeProject) {
|
|
185
|
+
process.stderr.write("No active project.\n");
|
|
186
|
+
process.exitCode = 1;
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const ws = await loadActiveProjectConfig(options.configDir);
|
|
190
|
+
if (!ws) {
|
|
191
|
+
process.stderr.write("Project config missing.\n");
|
|
192
|
+
process.exitCode = 1;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const [owner, name] = repoSpec.split("/");
|
|
196
|
+
const idx = ws.repositories.findIndex(
|
|
197
|
+
(r) => r.owner === owner && r.name === name
|
|
198
|
+
);
|
|
199
|
+
if (idx === -1) {
|
|
200
|
+
process.stderr.write(`Repository ${repoSpec} is not configured.
|
|
201
|
+
`);
|
|
202
|
+
process.exitCode = 1;
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
ws.repositories.splice(idx, 1);
|
|
206
|
+
await saveProjectConfig(options.configDir, global.activeProject, ws);
|
|
207
|
+
process.stdout.write(`Removed repository: ${repoSpec}
|
|
208
|
+
`);
|
|
209
|
+
}
|
|
210
|
+
async function repoSync(args, options) {
|
|
211
|
+
const flags = parseRepoSyncFlags(args);
|
|
212
|
+
const global = await loadGlobalConfig(options.configDir);
|
|
213
|
+
if (!global?.activeProject) {
|
|
214
|
+
process.stderr.write("No active project.\n");
|
|
215
|
+
process.exitCode = 1;
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const ws = await loadActiveProjectConfig(options.configDir);
|
|
219
|
+
if (!ws) {
|
|
220
|
+
process.stderr.write("Project config missing.\n");
|
|
221
|
+
process.exitCode = 1;
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const projectBindingId = typeof ws.tracker.settings?.projectId === "string" ? ws.tracker.settings.projectId : ws.tracker.bindingId;
|
|
225
|
+
if (!projectBindingId) {
|
|
226
|
+
process.stderr.write(
|
|
227
|
+
"Active project is missing its GitHub Project binding. Re-run 'gh-symphony project add'.\n"
|
|
228
|
+
);
|
|
229
|
+
process.exitCode = 1;
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
let token;
|
|
233
|
+
try {
|
|
234
|
+
token = getGhToken();
|
|
235
|
+
} catch {
|
|
236
|
+
process.stderr.write(
|
|
237
|
+
"Error: GitHub token not found. Run 'gh auth login --scopes repo,read:org,project' or set GITHUB_GRAPHQL_TOKEN.\n"
|
|
238
|
+
);
|
|
239
|
+
process.exitCode = 1;
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const client = createClient(token);
|
|
243
|
+
try {
|
|
244
|
+
const viewer = await validateToken(client);
|
|
245
|
+
const scopeCheck = checkRequiredScopes(viewer.scopes);
|
|
246
|
+
if (!scopeCheck.valid) {
|
|
247
|
+
process.stderr.write(
|
|
248
|
+
`Error: Missing required PAT scopes: ${scopeCheck.missing.join(", ")}
|
|
249
|
+
`
|
|
250
|
+
);
|
|
251
|
+
process.exitCode = 1;
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
} catch {
|
|
255
|
+
process.stderr.write("Error: Invalid GitHub token.\n");
|
|
256
|
+
process.exitCode = 1;
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
let projectDetail;
|
|
260
|
+
try {
|
|
261
|
+
projectDetail = await getProjectDetail(client, projectBindingId);
|
|
262
|
+
} catch (error) {
|
|
263
|
+
if (error instanceof GitHubScopeError) {
|
|
264
|
+
displayScopeError(error);
|
|
265
|
+
} else {
|
|
266
|
+
process.stderr.write(
|
|
267
|
+
`${error instanceof Error ? error.message : "Failed to load linked repositories."}
|
|
268
|
+
`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
process.exitCode = 1;
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const currentRepos = ws.repositories;
|
|
275
|
+
const currentMap = new Map(
|
|
276
|
+
currentRepos.map((repo) => [repoKey(repo), repo])
|
|
277
|
+
);
|
|
278
|
+
const linkedMap = new Map(
|
|
279
|
+
projectDetail.linkedRepositories.map((repo) => [
|
|
280
|
+
repoKey(repo),
|
|
281
|
+
repo
|
|
282
|
+
])
|
|
283
|
+
);
|
|
284
|
+
const added = projectDetail.linkedRepositories.filter((repo) => !currentMap.has(repoKey(repo))).map(toRepoConfigEntry);
|
|
285
|
+
const removed = flags.prune ? currentRepos.filter((repo) => !linkedMap.has(repoKey(repo))).map((repo) => ({ ...repo })) : [];
|
|
286
|
+
const unchanged = flags.prune ? currentRepos.filter((repo) => linkedMap.has(repoKey(repo))).map((repo) => {
|
|
287
|
+
const linked = linkedMap.get(repoKey(repo));
|
|
288
|
+
return linked ? toRepoConfigEntry(linked) : { ...repo };
|
|
289
|
+
}) : currentRepos.map((repo) => {
|
|
290
|
+
const linked = linkedMap.get(repoKey(repo));
|
|
291
|
+
return linked ? toRepoConfigEntry(linked) : { ...repo };
|
|
292
|
+
});
|
|
293
|
+
const nextRepositories = buildSyncedRepositories(
|
|
294
|
+
currentRepos,
|
|
295
|
+
linkedMap,
|
|
296
|
+
projectDetail.linkedRepositories,
|
|
297
|
+
flags.prune
|
|
298
|
+
);
|
|
299
|
+
if (!flags.dryRun) {
|
|
300
|
+
await saveProjectConfig(options.configDir, global.activeProject, {
|
|
301
|
+
...ws,
|
|
302
|
+
repositories: nextRepositories
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
writeRepoSummary(
|
|
306
|
+
{
|
|
307
|
+
projectId: global.activeProject,
|
|
308
|
+
githubProjectId: projectBindingId,
|
|
309
|
+
dryRun: flags.dryRun,
|
|
310
|
+
prune: flags.prune,
|
|
311
|
+
added,
|
|
312
|
+
removed,
|
|
313
|
+
unchanged,
|
|
314
|
+
repositories: nextRepositories
|
|
315
|
+
},
|
|
316
|
+
options
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
export {
|
|
320
|
+
repo_default as default
|
|
321
|
+
};
|