@gh-symphony/cli 0.0.6 → 0.0.8
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 +28 -14
- package/dist/commands/config-cmd.js +9 -9
- package/dist/commands/help.js +12 -12
- package/dist/commands/init.d.ts +3 -3
- package/dist/commands/init.js +27 -26
- package/dist/commands/logs.js +19 -8
- package/dist/commands/parse-cli-args.d.ts +6 -0
- package/dist/commands/parse-cli-args.js +20 -0
- package/dist/commands/project.js +592 -62
- package/dist/commands/recover.js +17 -14
- package/dist/commands/repo.js +13 -13
- package/dist/commands/run.js +19 -16
- package/dist/commands/start.d.ts +11 -0
- package/dist/commands/start.js +165 -122
- package/dist/commands/status-refresh.d.ts +1 -0
- package/dist/commands/status-refresh.js +7 -1
- package/dist/commands/status.js +48 -40
- package/dist/commands/stop.js +53 -7
- package/dist/completion.d.ts +1 -0
- package/dist/completion.js +204 -0
- package/dist/config.d.ts +18 -16
- package/dist/config.js +29 -19
- package/dist/dashboard/renderer.d.ts +2 -2
- package/dist/dashboard/renderer.js +5 -5
- package/dist/index.d.ts +0 -5
- package/dist/index.js +340 -53
- package/dist/orchestrator-runtime.d.ts +4 -4
- package/dist/orchestrator-runtime.js +11 -11
- package/dist/orchestrator-status-endpoint.d.ts +5 -0
- package/dist/orchestrator-status-endpoint.js +27 -0
- package/dist/project-selection.d.ts +8 -0
- package/dist/project-selection.js +56 -0
- package/dist/skills/types.d.ts +1 -1
- package/package.json +6 -5
package/dist/index.js
CHANGED
|
@@ -1,44 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { realpathSync } from "node:fs";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { Command, CommanderError, InvalidArgumentError, Option, } from "commander";
|
|
4
5
|
import { resolveConfigDir } from "./config.js";
|
|
5
|
-
|
|
6
|
-
const globalFlags = {
|
|
7
|
-
configDir: resolveConfigDir(),
|
|
8
|
-
verbose: false,
|
|
9
|
-
json: false,
|
|
10
|
-
noColor: false,
|
|
11
|
-
};
|
|
12
|
-
const remaining = [];
|
|
13
|
-
let i = 0;
|
|
14
|
-
while (i < argv.length) {
|
|
15
|
-
const arg = argv[i];
|
|
16
|
-
if (arg === "--config" || arg === "--config-dir") {
|
|
17
|
-
globalFlags.configDir = resolveConfigDir(argv[i + 1]);
|
|
18
|
-
i += 2;
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
if (arg === "--verbose" || arg === "-v") {
|
|
22
|
-
globalFlags.verbose = true;
|
|
23
|
-
i += 1;
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
if (arg === "--json") {
|
|
27
|
-
globalFlags.json = true;
|
|
28
|
-
i += 1;
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
if (arg === "--no-color") {
|
|
32
|
-
globalFlags.noColor = true;
|
|
33
|
-
i += 1;
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
remaining.push(arg);
|
|
37
|
-
i += 1;
|
|
38
|
-
}
|
|
39
|
-
const [command = "help", ...args] = remaining;
|
|
40
|
-
return { options: globalFlags, command, args };
|
|
41
|
-
}
|
|
6
|
+
import { renderCompletionScript } from "./completion.js";
|
|
42
7
|
const COMMANDS = {
|
|
43
8
|
init: () => import("./commands/init.js"),
|
|
44
9
|
start: () => import("./commands/start.js"),
|
|
@@ -49,34 +14,356 @@ const COMMANDS = {
|
|
|
49
14
|
logs: () => import("./commands/logs.js"),
|
|
50
15
|
project: () => import("./commands/project.js"),
|
|
51
16
|
repo: () => import("./commands/repo.js"),
|
|
52
|
-
tenant: () => import("./commands/tenant.js"),
|
|
53
17
|
config: () => import("./commands/config-cmd.js"),
|
|
54
|
-
help: () => import("./commands/help.js"),
|
|
55
18
|
version: () => import("./commands/version.js"),
|
|
56
19
|
};
|
|
57
|
-
|
|
58
|
-
|
|
20
|
+
function addGlobalOptions(command) {
|
|
21
|
+
return command
|
|
22
|
+
.option("--config <dir>", "Config directory")
|
|
23
|
+
.addOption(new Option("--config-dir <dir>").hideHelp())
|
|
24
|
+
.option("-v, --verbose", "Enable verbose output")
|
|
25
|
+
.option("--json", "Output in JSON format")
|
|
26
|
+
.option("--no-color", "Disable color output");
|
|
27
|
+
}
|
|
28
|
+
function resolveGlobalOptions(values) {
|
|
29
|
+
const configInput = typeof values.config === "string"
|
|
30
|
+
? values.config
|
|
31
|
+
: typeof values.configDir === "string"
|
|
32
|
+
? values.configDir
|
|
33
|
+
: undefined;
|
|
34
|
+
const options = {
|
|
35
|
+
configDir: resolveConfigDir(configInput),
|
|
36
|
+
verbose: Boolean(values.verbose),
|
|
37
|
+
json: Boolean(values.json),
|
|
38
|
+
noColor: Boolean(values.noColor),
|
|
39
|
+
};
|
|
59
40
|
if (options.noColor) {
|
|
60
41
|
process.env.NO_COLOR = "1";
|
|
61
42
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
43
|
+
return options;
|
|
44
|
+
}
|
|
45
|
+
function resolveProjectId(values) {
|
|
46
|
+
return values.projectId ?? values.project;
|
|
47
|
+
}
|
|
48
|
+
function pushOption(args, flag, value) {
|
|
49
|
+
if (typeof value === "string" && value.length > 0) {
|
|
50
|
+
args.push(flag, value);
|
|
65
51
|
return;
|
|
66
52
|
}
|
|
67
|
-
if (
|
|
53
|
+
if (value === true) {
|
|
54
|
+
args.push(flag);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function invokeHandler(key, args, values) {
|
|
58
|
+
const module = await COMMANDS[key]();
|
|
59
|
+
await module.default(args, resolveGlobalOptions(values));
|
|
60
|
+
}
|
|
61
|
+
function shellArgument(value) {
|
|
62
|
+
if (value === "bash" || value === "zsh" || value === "fish") {
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
throw new InvalidArgumentError("Shell must be one of: bash, zsh, fish");
|
|
66
|
+
}
|
|
67
|
+
function hasVersionFlag(argv) {
|
|
68
|
+
return argv.some((arg) => arg === "--version" || arg === "-V");
|
|
69
|
+
}
|
|
70
|
+
function resolveVersionOptions(argv) {
|
|
71
|
+
const options = {
|
|
72
|
+
configDir: resolveConfigDir(),
|
|
73
|
+
verbose: argv.some((arg) => arg === "--verbose" || arg === "-v"),
|
|
74
|
+
json: argv.includes("--json"),
|
|
75
|
+
noColor: argv.includes("--no-color"),
|
|
76
|
+
};
|
|
77
|
+
if (options.noColor) {
|
|
78
|
+
process.env.NO_COLOR = "1";
|
|
79
|
+
}
|
|
80
|
+
return options;
|
|
81
|
+
}
|
|
82
|
+
function createProgram() {
|
|
83
|
+
let actionInvoked = false;
|
|
84
|
+
const markInvoked = () => {
|
|
85
|
+
actionInvoked = true;
|
|
86
|
+
};
|
|
87
|
+
const program = addGlobalOptions(new Command()
|
|
88
|
+
.name("gh-symphony")
|
|
89
|
+
.description("AI Coding Agent Orchestrator")
|
|
90
|
+
.exitOverride()
|
|
91
|
+
.helpOption("-h, --help", "Show help")
|
|
92
|
+
.addHelpCommand("help [command]", "Show help for command")
|
|
93
|
+
.showHelpAfterError("(run with --help for usage)")
|
|
94
|
+
.option("-V, --version", "Show version"));
|
|
95
|
+
addGlobalOptions(program
|
|
96
|
+
.command("init")
|
|
97
|
+
.description("Interactive project setup wizard")
|
|
98
|
+
.allowExcessArguments(false)).action(async function () {
|
|
99
|
+
markInvoked();
|
|
100
|
+
await invokeHandler("init", [], this.optsWithGlobals());
|
|
101
|
+
});
|
|
102
|
+
addGlobalOptions(program
|
|
103
|
+
.command("start")
|
|
104
|
+
.description("Start the orchestrator")
|
|
105
|
+
.option("-d, --daemon", "Start in daemon mode")
|
|
106
|
+
.option("--project-id <projectId>", "Project identifier")
|
|
107
|
+
.addOption(new Option("--project <projectId>").hideHelp())
|
|
108
|
+
.allowExcessArguments(false)).action(async function () {
|
|
109
|
+
markInvoked();
|
|
110
|
+
const values = this.optsWithGlobals();
|
|
111
|
+
const args = [];
|
|
112
|
+
pushOption(args, "--project-id", resolveProjectId(values));
|
|
113
|
+
pushOption(args, "--daemon", values.daemon);
|
|
114
|
+
await invokeHandler("start", args, values);
|
|
115
|
+
});
|
|
116
|
+
addGlobalOptions(program
|
|
117
|
+
.command("stop")
|
|
118
|
+
.description("Stop the background orchestrator")
|
|
119
|
+
.option("--force", "Force stop with SIGKILL")
|
|
120
|
+
.option("--project-id <projectId>", "Project identifier")
|
|
121
|
+
.addOption(new Option("--project <projectId>").hideHelp())
|
|
122
|
+
.allowExcessArguments(false)).action(async function () {
|
|
123
|
+
markInvoked();
|
|
124
|
+
const values = this.optsWithGlobals();
|
|
125
|
+
const args = [];
|
|
126
|
+
pushOption(args, "--project-id", resolveProjectId(values));
|
|
127
|
+
pushOption(args, "--force", values.force);
|
|
128
|
+
await invokeHandler("stop", args, values);
|
|
129
|
+
});
|
|
130
|
+
addGlobalOptions(program
|
|
131
|
+
.command("status")
|
|
132
|
+
.description("Show orchestrator status")
|
|
133
|
+
.option("-w, --watch", "Watch status continuously")
|
|
134
|
+
.option("--project-id <projectId>", "Project identifier")
|
|
135
|
+
.addOption(new Option("--project <projectId>").hideHelp())
|
|
136
|
+
.allowExcessArguments(false)).action(async function () {
|
|
137
|
+
markInvoked();
|
|
138
|
+
const values = this.optsWithGlobals();
|
|
139
|
+
const args = [];
|
|
140
|
+
pushOption(args, "--project-id", resolveProjectId(values));
|
|
141
|
+
pushOption(args, "--watch", values.watch);
|
|
142
|
+
await invokeHandler("status", args, values);
|
|
143
|
+
});
|
|
144
|
+
addGlobalOptions(program
|
|
145
|
+
.command("run")
|
|
146
|
+
.description("Dispatch a single issue")
|
|
147
|
+
.argument("<issue>", "Issue identifier")
|
|
148
|
+
.option("-w, --watch", "Watch status after dispatch")
|
|
149
|
+
.option("--project-id <projectId>", "Project identifier")
|
|
150
|
+
.addOption(new Option("--project <projectId>").hideHelp())
|
|
151
|
+
.allowExcessArguments(false)).action(async function (issue) {
|
|
152
|
+
markInvoked();
|
|
153
|
+
const values = this.optsWithGlobals();
|
|
154
|
+
const args = [issue];
|
|
155
|
+
pushOption(args, "--project-id", resolveProjectId(values));
|
|
156
|
+
pushOption(args, "--watch", values.watch);
|
|
157
|
+
await invokeHandler("run", args, values);
|
|
158
|
+
});
|
|
159
|
+
addGlobalOptions(program
|
|
160
|
+
.command("recover")
|
|
161
|
+
.description("Recover stalled runs")
|
|
162
|
+
.option("--dry-run", "Show recoverable runs without recovering")
|
|
163
|
+
.option("--project-id <projectId>", "Project identifier")
|
|
164
|
+
.addOption(new Option("--project <projectId>").hideHelp())
|
|
165
|
+
.allowExcessArguments(false)).action(async function () {
|
|
166
|
+
markInvoked();
|
|
167
|
+
const values = this.optsWithGlobals();
|
|
168
|
+
const args = [];
|
|
169
|
+
pushOption(args, "--project-id", resolveProjectId(values));
|
|
170
|
+
pushOption(args, "--dry-run", values.dryRun);
|
|
171
|
+
await invokeHandler("recover", args, values);
|
|
172
|
+
});
|
|
173
|
+
addGlobalOptions(program
|
|
174
|
+
.command("logs")
|
|
175
|
+
.description("View orchestrator logs")
|
|
176
|
+
.option("-f, --follow", "Follow new log lines")
|
|
177
|
+
.option("--issue <issue>", "Filter by issue identifier")
|
|
178
|
+
.option("--run <runId>", "Read events for a specific run")
|
|
179
|
+
.option("--level <level>", "Filter by log level")
|
|
180
|
+
.option("--project-id <projectId>", "Project identifier")
|
|
181
|
+
.addOption(new Option("--project <projectId>").hideHelp())
|
|
182
|
+
.allowExcessArguments(false)).action(async function () {
|
|
183
|
+
markInvoked();
|
|
184
|
+
const values = this.optsWithGlobals();
|
|
185
|
+
const args = [];
|
|
186
|
+
pushOption(args, "--project-id", resolveProjectId(values));
|
|
187
|
+
pushOption(args, "--follow", values.follow);
|
|
188
|
+
pushOption(args, "--issue", values.issue);
|
|
189
|
+
pushOption(args, "--run", values.run);
|
|
190
|
+
pushOption(args, "--level", values.level);
|
|
191
|
+
await invokeHandler("logs", args, values);
|
|
192
|
+
});
|
|
193
|
+
const project = addGlobalOptions(program.command("project").description("Manage configured projects"));
|
|
194
|
+
project.action(async function () {
|
|
195
|
+
markInvoked();
|
|
196
|
+
await invokeHandler("project", [], this.optsWithGlobals());
|
|
197
|
+
});
|
|
198
|
+
addGlobalOptions(project
|
|
199
|
+
.command("add")
|
|
200
|
+
.description("Add a new project")
|
|
201
|
+
.option("--non-interactive", "Run without prompts")
|
|
202
|
+
.option("--project <id>", "GitHub Project ID")
|
|
203
|
+
.option("--workspace-dir <path>", "Workspace directory")
|
|
204
|
+
.option("--assigned-only", "Limit processing to assigned issues")
|
|
205
|
+
.allowExcessArguments(false)).action(async function () {
|
|
206
|
+
markInvoked();
|
|
207
|
+
const values = this.optsWithGlobals();
|
|
208
|
+
const args = [];
|
|
209
|
+
pushOption(args, "--non-interactive", values.nonInteractive);
|
|
210
|
+
pushOption(args, "--project", values.project);
|
|
211
|
+
pushOption(args, "--workspace-dir", values.workspaceDir);
|
|
212
|
+
pushOption(args, "--assigned-only", values.assignedOnly);
|
|
213
|
+
await invokeHandler("project", ["add", ...args], values);
|
|
214
|
+
});
|
|
215
|
+
addGlobalOptions(project.command("list").description("List configured projects")).action(async function () {
|
|
216
|
+
markInvoked();
|
|
217
|
+
const values = this.optsWithGlobals();
|
|
218
|
+
await invokeHandler("project", ["list"], values);
|
|
219
|
+
});
|
|
220
|
+
addGlobalOptions(project
|
|
221
|
+
.command("remove")
|
|
222
|
+
.description("Remove a project")
|
|
223
|
+
.argument("<projectId>", "Project identifier")
|
|
224
|
+
.allowExcessArguments(false)).action(async function (projectId) {
|
|
225
|
+
markInvoked();
|
|
226
|
+
await invokeHandler("project", ["remove", projectId], this.optsWithGlobals());
|
|
227
|
+
});
|
|
228
|
+
addGlobalOptions(project
|
|
229
|
+
.command("start")
|
|
230
|
+
.description("Start a specific project")
|
|
231
|
+
.option("-d, --daemon", "Start in daemon mode")
|
|
232
|
+
.option("--project-id <projectId>", "Project identifier")
|
|
233
|
+
.addOption(new Option("--project <projectId>").hideHelp())
|
|
234
|
+
.allowExcessArguments(false)).action(async function () {
|
|
235
|
+
markInvoked();
|
|
236
|
+
const values = this.optsWithGlobals();
|
|
237
|
+
const args = ["start"];
|
|
238
|
+
pushOption(args, "--project-id", resolveProjectId(values));
|
|
239
|
+
pushOption(args, "--daemon", values.daemon);
|
|
240
|
+
await invokeHandler("project", args, values);
|
|
241
|
+
});
|
|
242
|
+
addGlobalOptions(project
|
|
243
|
+
.command("stop")
|
|
244
|
+
.description("Stop a specific project")
|
|
245
|
+
.option("--force", "Force stop with SIGKILL")
|
|
246
|
+
.option("--project-id <projectId>", "Project identifier")
|
|
247
|
+
.addOption(new Option("--project <projectId>").hideHelp())
|
|
248
|
+
.allowExcessArguments(false)).action(async function () {
|
|
249
|
+
markInvoked();
|
|
250
|
+
const values = this.optsWithGlobals();
|
|
251
|
+
const args = ["stop"];
|
|
252
|
+
pushOption(args, "--project-id", resolveProjectId(values));
|
|
253
|
+
pushOption(args, "--force", values.force);
|
|
254
|
+
await invokeHandler("project", args, values);
|
|
255
|
+
});
|
|
256
|
+
addGlobalOptions(project.command("switch").description("Switch the active project")).action(async function () {
|
|
257
|
+
markInvoked();
|
|
258
|
+
await invokeHandler("project", ["switch"], this.optsWithGlobals());
|
|
259
|
+
});
|
|
260
|
+
addGlobalOptions(project
|
|
261
|
+
.command("status")
|
|
262
|
+
.description("Show status for a specific project")
|
|
263
|
+
.option("-w, --watch", "Watch status continuously")
|
|
264
|
+
.option("--project-id <projectId>", "Project identifier")
|
|
265
|
+
.addOption(new Option("--project <projectId>").hideHelp())
|
|
266
|
+
.allowExcessArguments(false)).action(async function () {
|
|
267
|
+
markInvoked();
|
|
268
|
+
const values = this.optsWithGlobals();
|
|
269
|
+
const args = ["status"];
|
|
270
|
+
pushOption(args, "--project-id", resolveProjectId(values));
|
|
271
|
+
pushOption(args, "--watch", values.watch);
|
|
272
|
+
await invokeHandler("project", args, values);
|
|
273
|
+
});
|
|
274
|
+
const repo = addGlobalOptions(program
|
|
275
|
+
.command("repo")
|
|
276
|
+
.description("Manage repositories in the active project"));
|
|
277
|
+
repo.action(async function () {
|
|
278
|
+
markInvoked();
|
|
279
|
+
await invokeHandler("repo", [], this.optsWithGlobals());
|
|
280
|
+
});
|
|
281
|
+
addGlobalOptions(repo.command("list").description("List repositories")).action(async function () {
|
|
282
|
+
markInvoked();
|
|
283
|
+
await invokeHandler("repo", ["list"], this.optsWithGlobals());
|
|
284
|
+
});
|
|
285
|
+
addGlobalOptions(repo
|
|
286
|
+
.command("add")
|
|
287
|
+
.description("Add a repository")
|
|
288
|
+
.argument("<owner/name>", "Repository spec")
|
|
289
|
+
.allowExcessArguments(false)).action(async function (repoSpec) {
|
|
290
|
+
markInvoked();
|
|
291
|
+
await invokeHandler("repo", ["add", repoSpec], this.optsWithGlobals());
|
|
292
|
+
});
|
|
293
|
+
addGlobalOptions(repo
|
|
294
|
+
.command("remove")
|
|
295
|
+
.description("Remove a repository")
|
|
296
|
+
.argument("<owner/name>", "Repository spec")
|
|
297
|
+
.allowExcessArguments(false)).action(async function (repoSpec) {
|
|
298
|
+
markInvoked();
|
|
299
|
+
await invokeHandler("repo", ["remove", repoSpec], this.optsWithGlobals());
|
|
300
|
+
});
|
|
301
|
+
const config = addGlobalOptions(program.command("config").description("Manage CLI configuration"));
|
|
302
|
+
config.action(async function () {
|
|
303
|
+
markInvoked();
|
|
304
|
+
await invokeHandler("config", [], this.optsWithGlobals());
|
|
305
|
+
});
|
|
306
|
+
addGlobalOptions(config.command("show").description("Show configuration")).action(async function () {
|
|
307
|
+
markInvoked();
|
|
308
|
+
await invokeHandler("config", ["show"], this.optsWithGlobals());
|
|
309
|
+
});
|
|
310
|
+
addGlobalOptions(config
|
|
311
|
+
.command("set")
|
|
312
|
+
.description("Set a configuration value")
|
|
313
|
+
.argument("<key>", "Configuration key")
|
|
314
|
+
.argument("<value>", "Configuration value")
|
|
315
|
+
.allowExcessArguments(false)).action(async function (key, value) {
|
|
316
|
+
markInvoked();
|
|
317
|
+
await invokeHandler("config", ["set", key, value], this.optsWithGlobals());
|
|
318
|
+
});
|
|
319
|
+
addGlobalOptions(config.command("edit").description("Open config in $EDITOR")).action(async function () {
|
|
320
|
+
markInvoked();
|
|
321
|
+
await invokeHandler("config", ["edit"], this.optsWithGlobals());
|
|
322
|
+
});
|
|
323
|
+
addGlobalOptions(program
|
|
324
|
+
.command("completion")
|
|
325
|
+
.description("Print shell completion script")
|
|
326
|
+
.argument("<shell>", "Shell name", shellArgument)
|
|
327
|
+
.allowExcessArguments(false)).action(async function (shell) {
|
|
328
|
+
markInvoked();
|
|
329
|
+
process.stdout.write(renderCompletionScript(shell));
|
|
330
|
+
});
|
|
331
|
+
program
|
|
332
|
+
.command("version")
|
|
333
|
+
.description("Show version")
|
|
334
|
+
.allowExcessArguments(false)
|
|
335
|
+
.action(async function () {
|
|
336
|
+
markInvoked();
|
|
337
|
+
await invokeHandler("version", [], this.optsWithGlobals());
|
|
338
|
+
});
|
|
339
|
+
return { program, wasInvoked: () => actionInvoked };
|
|
340
|
+
}
|
|
341
|
+
export async function runCli(argv) {
|
|
342
|
+
const { program, wasInvoked } = createProgram();
|
|
343
|
+
if (hasVersionFlag(argv)) {
|
|
68
344
|
const versionModule = await COMMANDS.version();
|
|
69
|
-
await versionModule.default(
|
|
345
|
+
await versionModule.default([], resolveVersionOptions(argv));
|
|
70
346
|
return;
|
|
71
347
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
348
|
+
try {
|
|
349
|
+
await program.parseAsync(["node", "gh-symphony", ...argv], {
|
|
350
|
+
from: "node",
|
|
351
|
+
});
|
|
352
|
+
if (!wasInvoked()) {
|
|
353
|
+
program.outputHelp();
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
if (error instanceof CommanderError &&
|
|
358
|
+
error.code === "commander.helpDisplayed") {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
if (error instanceof CommanderError) {
|
|
362
|
+
process.exitCode = error.exitCode;
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
throw error;
|
|
77
366
|
}
|
|
78
|
-
const module = await loader();
|
|
79
|
-
await module.default(args, options);
|
|
80
367
|
}
|
|
81
368
|
async function main() {
|
|
82
369
|
await runCli(process.argv.slice(2));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type CliProjectConfig } from "./config.js";
|
|
2
2
|
export declare function resolveRuntimeRoot(configDir: string): string;
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function
|
|
5
|
-
export declare function
|
|
3
|
+
export declare function resolveProjectConfig(configDir: string, requestedProjectId?: string): Promise<CliProjectConfig | null>;
|
|
4
|
+
export declare function orchestratorProjectConfigPath(runtimeRoot: string, projectId: string): string;
|
|
5
|
+
export declare function syncProjectToRuntime(configDir: string, projectConfig: CliProjectConfig): Promise<string>;
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
|
-
import { loadGlobalConfig,
|
|
3
|
+
import { loadGlobalConfig, loadProjectConfig, } from "./config.js";
|
|
4
4
|
export function resolveRuntimeRoot(configDir) {
|
|
5
5
|
return resolve(configDir);
|
|
6
6
|
}
|
|
7
|
-
export async function
|
|
8
|
-
if (
|
|
9
|
-
return
|
|
7
|
+
export async function resolveProjectConfig(configDir, requestedProjectId) {
|
|
8
|
+
if (requestedProjectId) {
|
|
9
|
+
return loadProjectConfig(configDir, requestedProjectId);
|
|
10
10
|
}
|
|
11
11
|
const global = await loadGlobalConfig(configDir);
|
|
12
|
-
if (!global?.
|
|
12
|
+
if (!global?.activeProject) {
|
|
13
13
|
return null;
|
|
14
14
|
}
|
|
15
|
-
return
|
|
15
|
+
return loadProjectConfig(configDir, global.activeProject);
|
|
16
16
|
}
|
|
17
|
-
export function
|
|
18
|
-
return join(runtimeRoot, "orchestrator", "
|
|
17
|
+
export function orchestratorProjectConfigPath(runtimeRoot, projectId) {
|
|
18
|
+
return join(runtimeRoot, "orchestrator", "projects", projectId, "config.json");
|
|
19
19
|
}
|
|
20
|
-
export async function
|
|
20
|
+
export async function syncProjectToRuntime(configDir, projectConfig) {
|
|
21
21
|
const runtimeRoot = resolveRuntimeRoot(configDir);
|
|
22
|
-
const configPath =
|
|
22
|
+
const configPath = orchestratorProjectConfigPath(runtimeRoot, projectConfig.projectId);
|
|
23
23
|
await mkdir(dirname(configPath), { recursive: true });
|
|
24
|
-
await writeFile(configPath, JSON.stringify(
|
|
24
|
+
await writeFile(configPath, JSON.stringify(projectConfig, null, 2) + "\n");
|
|
25
25
|
return runtimeRoot;
|
|
26
26
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { orchestratorPortPath } from "./config.js";
|
|
3
|
+
export async function resolveProjectOrchestratorStatusBaseUrl(input) {
|
|
4
|
+
const env = input.env ?? process.env;
|
|
5
|
+
const explicitBaseUrl = env.ORCHESTRATOR_STATUS_BASE_URL;
|
|
6
|
+
if (explicitBaseUrl) {
|
|
7
|
+
return explicitBaseUrl;
|
|
8
|
+
}
|
|
9
|
+
const host = env.ORCHESTRATOR_STATUS_HOST ?? "127.0.0.1";
|
|
10
|
+
const port = env.ORCHESTRATOR_STATUS_PORT ??
|
|
11
|
+
(await readProjectStatusPort(input.configDir, input.projectId));
|
|
12
|
+
if (!port) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const urlHost = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
|
|
16
|
+
return `http://${urlHost}:${port}`;
|
|
17
|
+
}
|
|
18
|
+
async function readProjectStatusPort(configDir, projectId) {
|
|
19
|
+
try {
|
|
20
|
+
const raw = await readFile(orchestratorPortPath(configDir, projectId), "utf8");
|
|
21
|
+
const port = raw.trim();
|
|
22
|
+
return port.length > 0 ? port : null;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type CliProjectConfig } from "./config.js";
|
|
2
|
+
type ResolveProjectSelectionInput = {
|
|
3
|
+
configDir: string;
|
|
4
|
+
requestedProjectId?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function resolveManagedProjectConfig(input: ResolveProjectSelectionInput): Promise<CliProjectConfig | null>;
|
|
7
|
+
export declare function handleMissingManagedProjectConfig(): void;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { loadGlobalConfig, loadProjectConfig, } from "./config.js";
|
|
3
|
+
function isInteractiveTerminal() {
|
|
4
|
+
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
5
|
+
}
|
|
6
|
+
function explicitProjectRequiredMessage() {
|
|
7
|
+
return "Multiple projects are configured. Re-run with --project-id in non-interactive environments.\n";
|
|
8
|
+
}
|
|
9
|
+
export async function resolveManagedProjectConfig(input) {
|
|
10
|
+
if (input.requestedProjectId) {
|
|
11
|
+
return loadProjectConfig(input.configDir, input.requestedProjectId);
|
|
12
|
+
}
|
|
13
|
+
const global = await loadGlobalConfig(input.configDir);
|
|
14
|
+
const projectIds = global?.projects ?? [];
|
|
15
|
+
if (projectIds.length === 0) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
if (projectIds.length === 1) {
|
|
19
|
+
return loadProjectConfig(input.configDir, projectIds[0]);
|
|
20
|
+
}
|
|
21
|
+
if (!isInteractiveTerminal()) {
|
|
22
|
+
process.stderr.write(explicitProjectRequiredMessage());
|
|
23
|
+
process.exitCode = 1;
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const projects = await Promise.all(projectIds.map(async (projectId) => ({
|
|
27
|
+
projectId,
|
|
28
|
+
config: await loadProjectConfig(input.configDir, projectId),
|
|
29
|
+
})));
|
|
30
|
+
const selected = await p.select({
|
|
31
|
+
message: "Select a project:",
|
|
32
|
+
options: projects.map(({ projectId, config }) => ({
|
|
33
|
+
value: projectId,
|
|
34
|
+
label: config?.displayName ?? config?.slug ?? projectId,
|
|
35
|
+
hint: projectId === global?.activeProject
|
|
36
|
+
? "current"
|
|
37
|
+
: config && config.displayName && config.displayName !== projectId
|
|
38
|
+
? projectId
|
|
39
|
+
: undefined,
|
|
40
|
+
})),
|
|
41
|
+
maxItems: 10,
|
|
42
|
+
});
|
|
43
|
+
if (p.isCancel(selected)) {
|
|
44
|
+
p.cancel("Cancelled.");
|
|
45
|
+
process.exitCode = 130;
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return loadProjectConfig(input.configDir, selected);
|
|
49
|
+
}
|
|
50
|
+
export function handleMissingManagedProjectConfig() {
|
|
51
|
+
if (process.exitCode) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
process.stderr.write("No project configured. Run 'gh-symphony project add' first.\n");
|
|
55
|
+
process.exitCode = 1;
|
|
56
|
+
}
|
package/dist/skills/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gh-symphony/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "hojinzs",
|
|
6
6
|
"description": "Interactive CLI for GitHub Symphony orchestration",
|
|
@@ -36,10 +36,11 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@clack/prompts": "^0.9.1",
|
|
39
|
-
"
|
|
40
|
-
"@gh-symphony/
|
|
41
|
-
"@gh-symphony/
|
|
42
|
-
"@gh-symphony/
|
|
39
|
+
"commander": "^14.0.1",
|
|
40
|
+
"@gh-symphony/core": "0.0.8",
|
|
41
|
+
"@gh-symphony/orchestrator": "0.0.8",
|
|
42
|
+
"@gh-symphony/tracker-github": "0.0.8",
|
|
43
|
+
"@gh-symphony/worker": "0.0.8"
|
|
43
44
|
},
|
|
44
45
|
"scripts": {
|
|
45
46
|
"build": "tsc -p tsconfig.json",
|