@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/README.md
CHANGED
|
@@ -25,6 +25,20 @@ Verify the installation:
|
|
|
25
25
|
gh-symphony --version
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
Enable shell completion:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
gh-symphony completion bash >> ~/.bashrc
|
|
32
|
+
gh-symphony completion zsh >> ~/.zshrc
|
|
33
|
+
gh-symphony completion fish > ~/.config/fish/completions/gh-symphony.fish
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
If your `zsh` config does not already initialize completion, add this before the generated script line:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
autoload -Uz compinit && compinit
|
|
40
|
+
```
|
|
41
|
+
|
|
28
42
|
## 2. Set Repository
|
|
29
43
|
|
|
30
44
|
Navigate to the repository you want to orchestrate, then run:
|
|
@@ -48,28 +62,27 @@ You can further customize the agent's behavior by editing `WORKFLOW.md` — this
|
|
|
48
62
|
|
|
49
63
|
> Currently supported runtimes: **Codex**, **Claude Code**
|
|
50
64
|
|
|
51
|
-
## 3. Set Orchestrator Runner (
|
|
65
|
+
## 3. Set Orchestrator Runner (Project)
|
|
52
66
|
|
|
53
|
-
On the machine where you want the orchestrator to run, register a
|
|
67
|
+
On the machine where you want the orchestrator to run, register a project:
|
|
54
68
|
|
|
55
69
|
```bash
|
|
56
|
-
gh-symphony
|
|
70
|
+
gh-symphony project add
|
|
57
71
|
```
|
|
58
72
|
|
|
59
73
|
The interactive wizard will:
|
|
60
74
|
|
|
61
75
|
1. Authenticate via `gh` CLI
|
|
62
76
|
2. Let you select a **GitHub Project**
|
|
63
|
-
3.
|
|
64
|
-
4. Optionally
|
|
65
|
-
5.
|
|
66
|
-
6. Write tenant configuration to `~/.gh-symphony/`
|
|
77
|
+
3. Optionally limit processing to issues assigned to the authenticated user
|
|
78
|
+
4. Optionally customize advanced settings for repository filtering and workspace root directory
|
|
79
|
+
5. Write project configuration to `~/.gh-symphony/`
|
|
67
80
|
|
|
68
|
-
###
|
|
81
|
+
### Project Management
|
|
69
82
|
|
|
70
83
|
```bash
|
|
71
|
-
gh-symphony
|
|
72
|
-
gh-symphony
|
|
84
|
+
gh-symphony project list # List all configured projects
|
|
85
|
+
gh-symphony project remove <id> # Remove a project
|
|
73
86
|
```
|
|
74
87
|
|
|
75
88
|
## 4. Run the Orchestrator
|
|
@@ -126,11 +139,12 @@ Orchestration:
|
|
|
126
139
|
run <issue> Dispatch a single issue
|
|
127
140
|
recover Recover stalled runs
|
|
128
141
|
logs View orchestrator logs
|
|
142
|
+
completion <shell> Print shell completion for bash/zsh/fish
|
|
129
143
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
144
|
+
Project Management:
|
|
145
|
+
project add Add a new project (interactive wizard)
|
|
146
|
+
project list List all configured projects
|
|
147
|
+
project remove Remove a project
|
|
134
148
|
|
|
135
149
|
Global Options:
|
|
136
150
|
--config <dir> Config directory (default: ~/.gh-symphony)
|
|
@@ -31,12 +31,12 @@ async function configShow(options) {
|
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
33
|
process.stdout.write(`Config: ${configFilePath(options.configDir)}\n\n`);
|
|
34
|
-
process.stdout.write(`Active
|
|
35
|
-
process.stdout.write(`
|
|
34
|
+
process.stdout.write(`Active project: ${config.activeProject ?? "none"}\n`);
|
|
35
|
+
process.stdout.write(`Projects: ${config.projects.join(", ") || "none"}\n`);
|
|
36
36
|
}
|
|
37
37
|
// ── 7.2: config set ──────────────────────────────────────────────────────────
|
|
38
38
|
const VALID_KEYS = {
|
|
39
|
-
"active-
|
|
39
|
+
"active-project": { type: "string" },
|
|
40
40
|
};
|
|
41
41
|
async function configSet(args, options) {
|
|
42
42
|
const [key, value] = args;
|
|
@@ -54,17 +54,17 @@ async function configSet(args, options) {
|
|
|
54
54
|
}
|
|
55
55
|
const config = (await loadGlobalConfig(options.configDir)) ??
|
|
56
56
|
{
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
activeProject: null,
|
|
58
|
+
projects: [],
|
|
59
59
|
};
|
|
60
60
|
switch (key) {
|
|
61
|
-
case "active-
|
|
62
|
-
if (!config.
|
|
63
|
-
process.stderr.write(`
|
|
61
|
+
case "active-project":
|
|
62
|
+
if (!config.projects.includes(value)) {
|
|
63
|
+
process.stderr.write(`Project "${value}" not found. Available: ${config.projects.join(", ")}\n`);
|
|
64
64
|
process.exitCode = 1;
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
67
|
-
config.
|
|
67
|
+
config.activeProject = value;
|
|
68
68
|
break;
|
|
69
69
|
}
|
|
70
70
|
await saveGlobalConfig(options.configDir, config);
|
package/dist/commands/help.js
CHANGED
|
@@ -4,7 +4,7 @@ gh-symphony — AI Coding Agent Orchestrator
|
|
|
4
4
|
Usage: gh-symphony <command> [options]
|
|
5
5
|
|
|
6
6
|
Setup:
|
|
7
|
-
init Interactive
|
|
7
|
+
init Interactive project setup wizard
|
|
8
8
|
config show Show current configuration
|
|
9
9
|
config set Set a configuration value
|
|
10
10
|
config edit Open config in $EDITOR
|
|
@@ -18,15 +18,15 @@ Orchestration:
|
|
|
18
18
|
recover Recover stalled runs
|
|
19
19
|
logs View orchestrator logs
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
Project Management:
|
|
22
|
+
project add Add a new project (interactive wizard)
|
|
23
|
+
project list List all configured projects
|
|
24
|
+
project remove Remove a project
|
|
25
25
|
|
|
26
26
|
Project / Repo:
|
|
27
|
-
project list List
|
|
28
|
-
project switch Switch active
|
|
29
|
-
project status Show
|
|
27
|
+
project list List projects
|
|
28
|
+
project switch Switch active project
|
|
29
|
+
project status Show orchestrator status for a project
|
|
30
30
|
repo list List configured repositories
|
|
31
31
|
repo add Add a repository
|
|
32
32
|
repo remove Remove a repository
|
|
@@ -40,10 +40,10 @@ Global Options:
|
|
|
40
40
|
--version, -V Show version
|
|
41
41
|
|
|
42
42
|
Examples:
|
|
43
|
-
gh-symphony
|
|
44
|
-
gh-symphony
|
|
45
|
-
gh-symphony
|
|
46
|
-
gh-symphony
|
|
43
|
+
gh-symphony project add # Add a project (interactive)
|
|
44
|
+
gh-symphony project add --non-interactive --project <id> --workspace-dir <path>
|
|
45
|
+
gh-symphony project list # List all projects
|
|
46
|
+
gh-symphony project remove <id> # Remove a project
|
|
47
47
|
gh-symphony start # Start orchestrator
|
|
48
48
|
gh-symphony start --daemon # Start in background
|
|
49
49
|
gh-symphony run org/repo#123 # Dispatch a specific issue
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ type EcosystemOptions = {
|
|
|
13
13
|
};
|
|
14
14
|
export type EcosystemResult = {
|
|
15
15
|
projectId: string;
|
|
16
|
-
|
|
16
|
+
githubProjectTitle: string;
|
|
17
17
|
runtime: string;
|
|
18
18
|
skillsDir: string | null;
|
|
19
19
|
contextYamlWritten: boolean;
|
|
@@ -23,7 +23,7 @@ export type EcosystemResult = {
|
|
|
23
23
|
};
|
|
24
24
|
export declare function writeEcosystem(opts: EcosystemOptions): Promise<EcosystemResult>;
|
|
25
25
|
type WriteConfigInput = {
|
|
26
|
-
|
|
26
|
+
projectId: string;
|
|
27
27
|
project: ProjectDetail;
|
|
28
28
|
repos: LinkedRepository[];
|
|
29
29
|
workspaceDir: string;
|
|
@@ -31,4 +31,4 @@ type WriteConfigInput = {
|
|
|
31
31
|
assignedOnly?: boolean;
|
|
32
32
|
};
|
|
33
33
|
export declare function writeConfig(configDir: string, input: WriteConfigInput): Promise<void>;
|
|
34
|
-
export declare function
|
|
34
|
+
export declare function generateProjectId(githubProjectTitle: string, uniqueKey: string): string;
|
package/dist/commands/init.js
CHANGED
|
@@ -5,7 +5,7 @@ import { basename, dirname, join, relative, resolve } from "node:path";
|
|
|
5
5
|
import { createClient, validateToken, checkRequiredScopes, listUserProjects, getProjectDetail, GitHubScopeError, } from "../github/client.js";
|
|
6
6
|
import { inferAllStateRoles, toWorkflowLifecycleConfig, validateStateMapping, } from "../mapping/smart-defaults.js";
|
|
7
7
|
import { generateWorkflowMarkdown } from "../workflow/generate-workflow-md.js";
|
|
8
|
-
import { loadGlobalConfig, saveGlobalConfig,
|
|
8
|
+
import { loadGlobalConfig, saveGlobalConfig, saveProjectConfig, } from "../config.js";
|
|
9
9
|
import { getGhToken, ensureGhAuth, GhAuthError } from "../github/gh-auth.js";
|
|
10
10
|
import { detectEnvironment } from "../detection/environment-detector.js";
|
|
11
11
|
import { buildContextYaml, writeContextYaml, } from "../context/generate-context-yaml.js";
|
|
@@ -120,7 +120,7 @@ export async function writeEcosystem(opts) {
|
|
|
120
120
|
const result = await writeAllSkills(cwd, runtime, ALL_SKILL_TEMPLATES, {
|
|
121
121
|
runtime,
|
|
122
122
|
projectId: projectDetail.id,
|
|
123
|
-
|
|
123
|
+
githubProjectTitle: projectDetail.title,
|
|
124
124
|
repositories: projectDetail.linkedRepositories.map((r) => ({
|
|
125
125
|
owner: r.owner,
|
|
126
126
|
name: r.name,
|
|
@@ -139,7 +139,7 @@ export async function writeEcosystem(opts) {
|
|
|
139
139
|
}
|
|
140
140
|
return {
|
|
141
141
|
projectId: projectDetail.id,
|
|
142
|
-
|
|
142
|
+
githubProjectTitle: projectDetail.title,
|
|
143
143
|
runtime,
|
|
144
144
|
skillsDir,
|
|
145
145
|
contextYamlWritten,
|
|
@@ -153,7 +153,7 @@ function printEcosystemSummary(result, workflowPath, opts) {
|
|
|
153
153
|
const cwd = process.cwd();
|
|
154
154
|
const relWorkflow = relative(cwd, workflowPath) || "WORKFLOW.md";
|
|
155
155
|
const lines = [];
|
|
156
|
-
lines.push(`Project ${result.
|
|
156
|
+
lines.push(`GitHub Project ${result.githubProjectTitle} (${result.projectId})`);
|
|
157
157
|
lines.push(`Runtime ${result.runtime}`);
|
|
158
158
|
lines.push("");
|
|
159
159
|
lines.push("Generated files");
|
|
@@ -217,7 +217,7 @@ async function runNonInteractive(flags, options) {
|
|
|
217
217
|
}
|
|
218
218
|
// Find project
|
|
219
219
|
const projects = await listUserProjects(client);
|
|
220
|
-
let
|
|
220
|
+
let githubProject;
|
|
221
221
|
if (flags.project) {
|
|
222
222
|
const match = projects.find((proj) => proj.id === flags.project || proj.url === flags.project);
|
|
223
223
|
if (!match) {
|
|
@@ -225,10 +225,10 @@ async function runNonInteractive(flags, options) {
|
|
|
225
225
|
process.exitCode = 1;
|
|
226
226
|
return;
|
|
227
227
|
}
|
|
228
|
-
|
|
228
|
+
githubProject = await getProjectDetail(client, match.id);
|
|
229
229
|
}
|
|
230
230
|
else if (projects.length === 1) {
|
|
231
|
-
|
|
231
|
+
githubProject = await getProjectDetail(client, projects[0].id);
|
|
232
232
|
}
|
|
233
233
|
else {
|
|
234
234
|
process.stderr.write("Error: --project is required when multiple projects exist.\n");
|
|
@@ -236,8 +236,8 @@ async function runNonInteractive(flags, options) {
|
|
|
236
236
|
return;
|
|
237
237
|
}
|
|
238
238
|
// Auto-map with smart defaults
|
|
239
|
-
const statusField =
|
|
240
|
-
|
|
239
|
+
const statusField = githubProject.statusFields.find((f) => f.name.toLowerCase() === "status") ??
|
|
240
|
+
githubProject.statusFields[0];
|
|
241
241
|
if (!statusField) {
|
|
242
242
|
process.stderr.write("Error: No status field found on the project.\n");
|
|
243
243
|
process.exitCode = 1;
|
|
@@ -260,7 +260,7 @@ async function runNonInteractive(flags, options) {
|
|
|
260
260
|
const lifecycleConfig = toWorkflowLifecycleConfig(statusField.name, mappings);
|
|
261
261
|
const outputPath = resolve(flags.output ?? "WORKFLOW.md");
|
|
262
262
|
const workflowMd = generateWorkflowMarkdown({
|
|
263
|
-
projectId:
|
|
263
|
+
projectId: githubProject.id,
|
|
264
264
|
stateFieldName: statusField.name,
|
|
265
265
|
mappings,
|
|
266
266
|
lifecycle: lifecycleConfig,
|
|
@@ -269,7 +269,7 @@ async function runNonInteractive(flags, options) {
|
|
|
269
269
|
await writeFile(outputPath, workflowMd, "utf8");
|
|
270
270
|
const ecosystemResult = await writeEcosystem({
|
|
271
271
|
cwd: process.cwd(),
|
|
272
|
-
projectDetail:
|
|
272
|
+
projectDetail: githubProject,
|
|
273
273
|
statusField,
|
|
274
274
|
runtime: "codex",
|
|
275
275
|
skipSkills: flags.skipSkills,
|
|
@@ -281,7 +281,7 @@ async function runNonInteractive(flags, options) {
|
|
|
281
281
|
else {
|
|
282
282
|
printEcosystemSummary(ecosystemResult, outputPath, {
|
|
283
283
|
interactive: false,
|
|
284
|
-
nextSteps: "Run 'gh-symphony
|
|
284
|
+
nextSteps: "Run 'gh-symphony project add' to register a project.",
|
|
285
285
|
});
|
|
286
286
|
}
|
|
287
287
|
}
|
|
@@ -346,8 +346,8 @@ async function runInteractiveStandalone(_options) {
|
|
|
346
346
|
process.exitCode = 1;
|
|
347
347
|
return;
|
|
348
348
|
}
|
|
349
|
-
const
|
|
350
|
-
message: "Step 1/2 — Select a GitHub Project:",
|
|
349
|
+
const selectedGithubProjectId = await abortIfCancelled(p.select({
|
|
350
|
+
message: "Step 1/2 — Select a GitHub Project board:",
|
|
351
351
|
options: projects.map((proj) => ({
|
|
352
352
|
value: proj.id,
|
|
353
353
|
label: `${proj.owner.login}/${proj.title}`,
|
|
@@ -359,7 +359,7 @@ async function runInteractiveStandalone(_options) {
|
|
|
359
359
|
s2d.start("Loading project details...");
|
|
360
360
|
let projectDetail;
|
|
361
361
|
try {
|
|
362
|
-
projectDetail = await getProjectDetail(client,
|
|
362
|
+
projectDetail = await getProjectDetail(client, selectedGithubProjectId);
|
|
363
363
|
s2d.stop(`Loaded: ${projectDetail.title}`);
|
|
364
364
|
}
|
|
365
365
|
catch (error) {
|
|
@@ -432,13 +432,14 @@ async function runInteractiveStandalone(_options) {
|
|
|
432
432
|
});
|
|
433
433
|
printEcosystemSummary(ecosystemResult, outputPath, {
|
|
434
434
|
interactive: true,
|
|
435
|
-
nextSteps: "Run 'gh-symphony
|
|
435
|
+
nextSteps: "Run 'gh-symphony project add' to register a project.",
|
|
436
436
|
});
|
|
437
437
|
}
|
|
438
438
|
export async function writeConfig(configDir, input) {
|
|
439
|
-
await
|
|
440
|
-
|
|
441
|
-
slug: input.
|
|
439
|
+
await saveProjectConfig(configDir, input.projectId, {
|
|
440
|
+
projectId: input.projectId,
|
|
441
|
+
slug: input.projectId,
|
|
442
|
+
displayName: input.project.title,
|
|
442
443
|
workspaceDir: input.workspaceDir,
|
|
443
444
|
repositories: input.repos.map((r) => ({
|
|
444
445
|
owner: r.owner,
|
|
@@ -457,20 +458,20 @@ export async function writeConfig(configDir, input) {
|
|
|
457
458
|
// Save/update global config
|
|
458
459
|
const existing = await loadGlobalConfig(configDir);
|
|
459
460
|
const globalConfig = {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
...(existing?.
|
|
463
|
-
input.
|
|
461
|
+
activeProject: input.projectId,
|
|
462
|
+
projects: [
|
|
463
|
+
...(existing?.projects ?? []).filter((t) => t !== input.projectId),
|
|
464
|
+
input.projectId,
|
|
464
465
|
],
|
|
465
466
|
};
|
|
466
467
|
await saveGlobalConfig(configDir, globalConfig);
|
|
467
468
|
}
|
|
468
|
-
export function
|
|
469
|
-
const slug =
|
|
469
|
+
export function generateProjectId(githubProjectTitle, uniqueKey) {
|
|
470
|
+
const slug = githubProjectTitle
|
|
470
471
|
.toLowerCase()
|
|
471
472
|
.replace(/[^a-z0-9]+/g, "-")
|
|
472
473
|
.replace(/^-|-$/g, "")
|
|
473
474
|
.slice(0, 32);
|
|
474
475
|
const suffix = createHash("sha1").update(uniqueKey).digest("hex").slice(0, 8);
|
|
475
|
-
return [slug || "
|
|
476
|
+
return [slug || "project", suffix].join("-");
|
|
476
477
|
}
|
package/dist/commands/logs.js
CHANGED
|
@@ -2,7 +2,8 @@ import { readFile, readdir } from "node:fs/promises";
|
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import { createReadStream } from "node:fs";
|
|
4
4
|
import { createInterface } from "node:readline";
|
|
5
|
-
import {
|
|
5
|
+
import { orchestratorLogPath } from "../config.js";
|
|
6
|
+
import { handleMissingManagedProjectConfig, resolveManagedProjectConfig, } from "../project-selection.js";
|
|
6
7
|
function parseLogsArgs(args) {
|
|
7
8
|
const parsed = { follow: false };
|
|
8
9
|
for (let i = 0; i < args.length; i += 1) {
|
|
@@ -22,17 +23,15 @@ function parseLogsArgs(args) {
|
|
|
22
23
|
parsed.level = args[i + 1];
|
|
23
24
|
i += 1;
|
|
24
25
|
}
|
|
26
|
+
if (arg === "--project" || arg === "--project-id") {
|
|
27
|
+
parsed.projectId = args[i + 1];
|
|
28
|
+
i += 1;
|
|
29
|
+
}
|
|
25
30
|
}
|
|
26
31
|
return parsed;
|
|
27
32
|
}
|
|
28
33
|
const handler = async (args, options) => {
|
|
29
34
|
const parsed = parseLogsArgs(args);
|
|
30
|
-
const tenantConfig = await loadActiveTenantConfig(options.configDir);
|
|
31
|
-
if (!tenantConfig) {
|
|
32
|
-
process.stderr.write("No tenant configured. Run 'gh-symphony init' first.\n");
|
|
33
|
-
process.exitCode = 1;
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
35
|
// If --run is specified, read that run's events
|
|
37
36
|
if (parsed.run) {
|
|
38
37
|
const eventsPath = join(resolve(options.configDir), "orchestrator", "runs", parsed.run, "events.ndjson");
|
|
@@ -41,6 +40,8 @@ const handler = async (args, options) => {
|
|
|
41
40
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
42
41
|
for (const line of lines) {
|
|
43
42
|
const event = JSON.parse(line);
|
|
43
|
+
if (parsed.projectId && event.projectId !== parsed.projectId)
|
|
44
|
+
continue;
|
|
44
45
|
if (parsed.level && event.level !== parsed.level)
|
|
45
46
|
continue;
|
|
46
47
|
if (parsed.issue && event.issueIdentifier !== parsed.issue)
|
|
@@ -56,7 +57,15 @@ const handler = async (args, options) => {
|
|
|
56
57
|
}
|
|
57
58
|
// Default: read orchestrator log or scan all events
|
|
58
59
|
if (parsed.follow) {
|
|
59
|
-
const
|
|
60
|
+
const projectConfig = await resolveManagedProjectConfig({
|
|
61
|
+
configDir: options.configDir,
|
|
62
|
+
requestedProjectId: parsed.projectId,
|
|
63
|
+
});
|
|
64
|
+
if (!projectConfig) {
|
|
65
|
+
handleMissingManagedProjectConfig();
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const logPath = orchestratorLogPath(options.configDir, projectConfig.projectId);
|
|
60
69
|
try {
|
|
61
70
|
const stream = createReadStream(logPath, { encoding: "utf8" });
|
|
62
71
|
const rl = createInterface({ input: stream });
|
|
@@ -97,6 +106,8 @@ const handler = async (args, options) => {
|
|
|
97
106
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
98
107
|
for (const line of lines) {
|
|
99
108
|
const event = JSON.parse(line);
|
|
109
|
+
if (parsed.projectId && event.projectId !== parsed.projectId)
|
|
110
|
+
continue;
|
|
100
111
|
if (parsed.level && event.level !== parsed.level)
|
|
101
112
|
continue;
|
|
102
113
|
if (parsed.issue && event.issueIdentifier !== parsed.issue)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { parseArgs, type ParseArgsOptionsConfig } from "node:util";
|
|
2
|
+
type ParseCliArgsResult = ReturnType<typeof parseArgs> | {
|
|
3
|
+
error: string;
|
|
4
|
+
};
|
|
5
|
+
export declare function parseCliArgs(args: string[], options: ParseArgsOptionsConfig): ParseCliArgsResult;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
export function parseCliArgs(args, options) {
|
|
3
|
+
try {
|
|
4
|
+
return parseArgs({
|
|
5
|
+
args,
|
|
6
|
+
options,
|
|
7
|
+
allowPositionals: false,
|
|
8
|
+
strict: true,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
return { error: formatParseArgsError(error) };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function formatParseArgsError(error) {
|
|
16
|
+
if (error instanceof Error) {
|
|
17
|
+
return error.message;
|
|
18
|
+
}
|
|
19
|
+
return "Invalid arguments";
|
|
20
|
+
}
|