gyrus 0.1.0
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/LICENSE +21 -0
- package/README.md +273 -0
- package/package.json +78 -0
- package/src/commands/adr.ts +482 -0
- package/src/commands/doctor.ts +263 -0
- package/src/commands/init.ts +293 -0
- package/src/commands/knowledge.ts +446 -0
- package/src/commands/list.ts +51 -0
- package/src/commands/mcp.ts +94 -0
- package/src/commands/use.ts +65 -0
- package/src/config/index.ts +262 -0
- package/src/config/schema.ts +139 -0
- package/src/formatters/adr.ts +295 -0
- package/src/formatters/index.ts +44 -0
- package/src/formatters/knowledge.ts +282 -0
- package/src/formatters/workspace.ts +149 -0
- package/src/index.ts +153 -0
- package/src/operations/adr.ts +312 -0
- package/src/operations/index.ts +58 -0
- package/src/operations/knowledge.ts +324 -0
- package/src/operations/types.ts +199 -0
- package/src/operations/workspace.ts +108 -0
- package/src/server/mcp-stdio.ts +526 -0
- package/src/services/adr.ts +511 -0
- package/src/services/knowledge.ts +516 -0
- package/src/services/markdown.ts +155 -0
- package/src/services/workspace.ts +377 -0
- package/src/templates/knowledge/README.md +199 -0
- package/src/tools/adr-create.ts +99 -0
- package/src/tools/adr-list.ts +72 -0
- package/src/tools/adr-read.ts +54 -0
- package/src/tools/adr-search.ts +79 -0
- package/src/tools/adr-update.ts +95 -0
- package/src/tools/gyrus-list.ts +41 -0
- package/src/tools/gyrus-switch.ts +45 -0
- package/src/tools/index.ts +91 -0
- package/src/tools/knowledge-create.ts +75 -0
- package/src/tools/knowledge-list.ts +60 -0
- package/src/tools/knowledge-read.ts +62 -0
- package/src/tools/knowledge-search.ts +65 -0
- package/src/tools/knowledge-update.ts +76 -0
- package/src/types/index.ts +343 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADR CLI Command
|
|
3
|
+
* Handles all ADR-related CLI operations
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* gyrus adr list [--workspace <name>] [--status <status>] [--type <type>] [--tags <t1,t2>]
|
|
7
|
+
* gyrus adr read <name> [--workspace <name>]
|
|
8
|
+
* gyrus adr search <query> [--workspace <name>] [--status <status>] [--type <type>] [--tags <t1,t2>]
|
|
9
|
+
* gyrus adr create --title <title> --type <type> --working-folder <path> [options]
|
|
10
|
+
* gyrus adr update <name> [--title <title>] [--status <status>] [--content <content>]
|
|
11
|
+
*
|
|
12
|
+
* Aliases: gyrus a
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { readConfig } from '../config/index.ts';
|
|
16
|
+
import { WorkspaceManager } from '../services/workspace.ts';
|
|
17
|
+
import {
|
|
18
|
+
listAdrs,
|
|
19
|
+
readAdr,
|
|
20
|
+
searchAdrs,
|
|
21
|
+
createAdr,
|
|
22
|
+
updateAdr,
|
|
23
|
+
} from '../operations/adr.ts';
|
|
24
|
+
import {
|
|
25
|
+
formatAdrListCli,
|
|
26
|
+
formatAdrReadCli,
|
|
27
|
+
formatAdrSearchCli,
|
|
28
|
+
formatAdrCreateCli,
|
|
29
|
+
formatAdrUpdateCli,
|
|
30
|
+
} from '../formatters/adr.ts';
|
|
31
|
+
import type { AdrStatus, AdrType } from '../types/index.ts';
|
|
32
|
+
|
|
33
|
+
const HELP = `
|
|
34
|
+
Usage: gyrus adr <subcommand> [options]
|
|
35
|
+
|
|
36
|
+
Subcommands:
|
|
37
|
+
list List all ADRs
|
|
38
|
+
read <name> Read a specific ADR by name (partial match supported)
|
|
39
|
+
search <query> Search ADRs by text query
|
|
40
|
+
create Create a new ADR
|
|
41
|
+
update <name> Update an existing ADR
|
|
42
|
+
|
|
43
|
+
Global Options:
|
|
44
|
+
-w, --workspace <name> Target workspace (default: active workspace)
|
|
45
|
+
-h, --help Show this help message
|
|
46
|
+
|
|
47
|
+
List Options:
|
|
48
|
+
-s, --status <status> Filter by status (todo, in-progress, in-review, blocked, completed, deprecated)
|
|
49
|
+
--type <type> Filter by type (enhancement, debug, research)
|
|
50
|
+
-t, --tags <t1,t2,...> Filter by tags (comma-separated)
|
|
51
|
+
|
|
52
|
+
Search Options:
|
|
53
|
+
-s, --status <status> Filter by status
|
|
54
|
+
--type <type> Filter by type
|
|
55
|
+
-t, --tags <t1,t2,...> Filter by tags
|
|
56
|
+
|
|
57
|
+
Create Options:
|
|
58
|
+
--title <title> ADR title (required)
|
|
59
|
+
--type <type> ADR type: enhancement, debug, research (required)
|
|
60
|
+
--working-folder <path> Working folder path (required)
|
|
61
|
+
--description <desc> Brief description
|
|
62
|
+
-t, --tags <t1,t2,...> Tags (comma-separated)
|
|
63
|
+
-s, --status <status> Initial status (default: todo)
|
|
64
|
+
--content <content> Custom content (or use --file)
|
|
65
|
+
--file <path> Read content from file
|
|
66
|
+
|
|
67
|
+
Update Options:
|
|
68
|
+
--title <title> New title
|
|
69
|
+
--description <desc> New description
|
|
70
|
+
-s, --status <status> New status
|
|
71
|
+
--type <type> New type
|
|
72
|
+
-t, --tags <t1,t2,...> New tags (replaces existing)
|
|
73
|
+
--content <content> New content (or use --file)
|
|
74
|
+
--file <path> Read content from file
|
|
75
|
+
--working-folder <path> New working folder
|
|
76
|
+
|
|
77
|
+
Examples:
|
|
78
|
+
gyrus adr list
|
|
79
|
+
gyrus adr list --status in-progress
|
|
80
|
+
gyrus adr read brain-cli
|
|
81
|
+
gyrus adr search "authentication" --type enhancement
|
|
82
|
+
gyrus adr create --title "New Feature" --type enhancement --working-folder /path/to/project
|
|
83
|
+
gyrus adr update brain-cli --status completed
|
|
84
|
+
gyrus a list -w work
|
|
85
|
+
|
|
86
|
+
Aliases: a
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Parse command-line arguments into options object
|
|
91
|
+
*/
|
|
92
|
+
function parseArgs(args: string[]): {
|
|
93
|
+
subcommand: string;
|
|
94
|
+
positional: string[];
|
|
95
|
+
options: Record<string, string | boolean>;
|
|
96
|
+
} {
|
|
97
|
+
const options: Record<string, string | boolean> = {};
|
|
98
|
+
const positional: string[] = [];
|
|
99
|
+
let subcommand = '';
|
|
100
|
+
|
|
101
|
+
let i = 0;
|
|
102
|
+
|
|
103
|
+
// First non-flag argument is the subcommand
|
|
104
|
+
while (i < args.length && !subcommand) {
|
|
105
|
+
if (!args[i].startsWith('-')) {
|
|
106
|
+
subcommand = args[i];
|
|
107
|
+
i++;
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
i++;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Parse remaining arguments
|
|
114
|
+
while (i < args.length) {
|
|
115
|
+
const arg = args[i];
|
|
116
|
+
|
|
117
|
+
if (arg === '-h' || arg === '--help') {
|
|
118
|
+
options.help = true;
|
|
119
|
+
i++;
|
|
120
|
+
} else if (arg === '-w' || arg === '--workspace') {
|
|
121
|
+
options.workspace = args[++i] || '';
|
|
122
|
+
i++;
|
|
123
|
+
} else if (arg === '-s' || arg === '--status') {
|
|
124
|
+
options.status = args[++i] || '';
|
|
125
|
+
i++;
|
|
126
|
+
} else if (arg === '--type') {
|
|
127
|
+
options.type = args[++i] || '';
|
|
128
|
+
i++;
|
|
129
|
+
} else if (arg === '-t' || arg === '--tags') {
|
|
130
|
+
options.tags = args[++i] || '';
|
|
131
|
+
i++;
|
|
132
|
+
} else if (arg === '--title') {
|
|
133
|
+
options.title = args[++i] || '';
|
|
134
|
+
i++;
|
|
135
|
+
} else if (arg === '--description') {
|
|
136
|
+
options.description = args[++i] || '';
|
|
137
|
+
i++;
|
|
138
|
+
} else if (arg === '--content') {
|
|
139
|
+
options.content = args[++i] || '';
|
|
140
|
+
i++;
|
|
141
|
+
} else if (arg === '--file') {
|
|
142
|
+
options.file = args[++i] || '';
|
|
143
|
+
i++;
|
|
144
|
+
} else if (arg === '--working-folder') {
|
|
145
|
+
options.workingFolder = args[++i] || '';
|
|
146
|
+
i++;
|
|
147
|
+
} else if (!arg.startsWith('-')) {
|
|
148
|
+
positional.push(arg);
|
|
149
|
+
i++;
|
|
150
|
+
} else {
|
|
151
|
+
// Unknown flag, skip
|
|
152
|
+
i++;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return { subcommand, positional, options };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Parse comma-separated string into array
|
|
161
|
+
*/
|
|
162
|
+
function parseList(value: string | undefined): string[] | undefined {
|
|
163
|
+
if (!value || typeof value !== 'string') return undefined;
|
|
164
|
+
return value
|
|
165
|
+
.split(',')
|
|
166
|
+
.map((s) => s.trim())
|
|
167
|
+
.filter(Boolean);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Read content from file if --file option is provided
|
|
172
|
+
*/
|
|
173
|
+
async function getContent(
|
|
174
|
+
options: Record<string, string | boolean>
|
|
175
|
+
): Promise<string | undefined> {
|
|
176
|
+
if (options.file && typeof options.file === 'string') {
|
|
177
|
+
try {
|
|
178
|
+
const file = Bun.file(options.file);
|
|
179
|
+
return await file.text();
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.error(`Error reading file: ${options.file}`);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return typeof options.content === 'string' ? options.content : undefined;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Validate status value
|
|
190
|
+
*/
|
|
191
|
+
function validateStatus(status: string | undefined): AdrStatus | undefined {
|
|
192
|
+
if (!status) return undefined;
|
|
193
|
+
const valid: AdrStatus[] = [
|
|
194
|
+
'todo',
|
|
195
|
+
'in-progress',
|
|
196
|
+
'in-review',
|
|
197
|
+
'blocked',
|
|
198
|
+
'completed',
|
|
199
|
+
'deprecated',
|
|
200
|
+
];
|
|
201
|
+
if (!valid.includes(status as AdrStatus)) {
|
|
202
|
+
console.error(`Error: Invalid status "${status}"`);
|
|
203
|
+
console.error(`Valid statuses: ${valid.join(', ')}`);
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
return status as AdrStatus;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Validate type value
|
|
211
|
+
*/
|
|
212
|
+
function validateType(type: string | undefined): AdrType | undefined {
|
|
213
|
+
if (!type) return undefined;
|
|
214
|
+
const valid: AdrType[] = ['enhancement', 'debug', 'research'];
|
|
215
|
+
if (!valid.includes(type as AdrType)) {
|
|
216
|
+
console.error(`Error: Invalid type "${type}"`);
|
|
217
|
+
console.error(`Valid types: ${valid.join(', ')}`);
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
return type as AdrType;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Handle the list subcommand
|
|
225
|
+
*/
|
|
226
|
+
async function handleList(
|
|
227
|
+
manager: WorkspaceManager,
|
|
228
|
+
options: Record<string, string | boolean>
|
|
229
|
+
): Promise<void> {
|
|
230
|
+
const result = await listAdrs(manager, {
|
|
231
|
+
workspace:
|
|
232
|
+
typeof options.workspace === 'string' ? options.workspace : undefined,
|
|
233
|
+
status: validateStatus(options.status as string),
|
|
234
|
+
type: validateType(options.type as string),
|
|
235
|
+
tags: parseList(options.tags as string),
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
if (!result.success) {
|
|
239
|
+
console.error(`Error: ${result.error}`);
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
console.log(formatAdrListCli(result.data!));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Handle the read subcommand
|
|
248
|
+
*/
|
|
249
|
+
async function handleRead(
|
|
250
|
+
manager: WorkspaceManager,
|
|
251
|
+
positional: string[],
|
|
252
|
+
options: Record<string, string | boolean>
|
|
253
|
+
): Promise<void> {
|
|
254
|
+
const name = positional[0];
|
|
255
|
+
|
|
256
|
+
if (!name) {
|
|
257
|
+
console.error('Error: Please provide an ADR name');
|
|
258
|
+
console.error('Usage: gyrus adr read <name>');
|
|
259
|
+
console.error('\nTip: You can use a partial name to search.');
|
|
260
|
+
process.exit(1);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const result = await readAdr(manager, {
|
|
264
|
+
workspace:
|
|
265
|
+
typeof options.workspace === 'string' ? options.workspace : undefined,
|
|
266
|
+
name,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
if (!result.success) {
|
|
270
|
+
console.error(`Error: ${result.error}`);
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
console.log(formatAdrReadCli(result.data!));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Handle the search subcommand
|
|
279
|
+
*/
|
|
280
|
+
async function handleSearch(
|
|
281
|
+
manager: WorkspaceManager,
|
|
282
|
+
positional: string[],
|
|
283
|
+
options: Record<string, string | boolean>
|
|
284
|
+
): Promise<void> {
|
|
285
|
+
const query = positional[0];
|
|
286
|
+
const status = validateStatus(options.status as string);
|
|
287
|
+
const type = validateType(options.type as string);
|
|
288
|
+
const tags = parseList(options.tags as string);
|
|
289
|
+
|
|
290
|
+
if (!query && !status && !type && !tags?.length) {
|
|
291
|
+
console.error(
|
|
292
|
+
'Error: Please provide a search query, status, type, or tags'
|
|
293
|
+
);
|
|
294
|
+
console.error(
|
|
295
|
+
'Usage: gyrus adr search <query> [--status <status>] [--type <type>] [--tags <t1,t2>]'
|
|
296
|
+
);
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const result = await searchAdrs(manager, {
|
|
301
|
+
workspace:
|
|
302
|
+
typeof options.workspace === 'string' ? options.workspace : undefined,
|
|
303
|
+
query,
|
|
304
|
+
status,
|
|
305
|
+
type,
|
|
306
|
+
tags,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
if (!result.success) {
|
|
310
|
+
console.error(`Error: ${result.error}`);
|
|
311
|
+
process.exit(1);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
console.log(formatAdrSearchCli(result.data!));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Handle the create subcommand
|
|
319
|
+
*/
|
|
320
|
+
async function handleCreate(
|
|
321
|
+
manager: WorkspaceManager,
|
|
322
|
+
options: Record<string, string | boolean>
|
|
323
|
+
): Promise<void> {
|
|
324
|
+
const title = typeof options.title === 'string' ? options.title : undefined;
|
|
325
|
+
const type = typeof options.type === 'string' ? options.type : undefined;
|
|
326
|
+
const workingFolder =
|
|
327
|
+
typeof options.workingFolder === 'string'
|
|
328
|
+
? options.workingFolder
|
|
329
|
+
: undefined;
|
|
330
|
+
const content = await getContent(options);
|
|
331
|
+
|
|
332
|
+
if (!title || !type || !workingFolder) {
|
|
333
|
+
console.error('Error: Missing required options for create');
|
|
334
|
+
console.error('Required: --title, --type, --working-folder');
|
|
335
|
+
console.error('\nExample:');
|
|
336
|
+
console.error(
|
|
337
|
+
' gyrus adr create --title "New Feature" --type enhancement --working-folder /path/to/project'
|
|
338
|
+
);
|
|
339
|
+
process.exit(1);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const validatedType = validateType(type);
|
|
343
|
+
if (!validatedType) {
|
|
344
|
+
process.exit(1);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const result = await createAdr(manager, {
|
|
348
|
+
workspace:
|
|
349
|
+
typeof options.workspace === 'string' ? options.workspace : undefined,
|
|
350
|
+
title,
|
|
351
|
+
type: validatedType,
|
|
352
|
+
workingFolder,
|
|
353
|
+
description:
|
|
354
|
+
typeof options.description === 'string' ? options.description : undefined,
|
|
355
|
+
tags: parseList(options.tags as string),
|
|
356
|
+
status: validateStatus(options.status as string),
|
|
357
|
+
content,
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
if (!result.success) {
|
|
361
|
+
console.error(`Error: ${result.error}`);
|
|
362
|
+
process.exit(1);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
console.log(formatAdrCreateCli(result.data!));
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Handle the update subcommand
|
|
370
|
+
*/
|
|
371
|
+
async function handleUpdate(
|
|
372
|
+
manager: WorkspaceManager,
|
|
373
|
+
positional: string[],
|
|
374
|
+
options: Record<string, string | boolean>
|
|
375
|
+
): Promise<void> {
|
|
376
|
+
const name = positional[0];
|
|
377
|
+
|
|
378
|
+
if (!name) {
|
|
379
|
+
console.error('Error: Please provide an ADR name to update');
|
|
380
|
+
console.error('Usage: gyrus adr update <name> [options]');
|
|
381
|
+
process.exit(1);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const content = await getContent(options);
|
|
385
|
+
const hasUpdates =
|
|
386
|
+
options.title ||
|
|
387
|
+
options.description ||
|
|
388
|
+
options.status ||
|
|
389
|
+
options.type ||
|
|
390
|
+
options.tags ||
|
|
391
|
+
content ||
|
|
392
|
+
options.workingFolder;
|
|
393
|
+
|
|
394
|
+
if (!hasUpdates) {
|
|
395
|
+
console.error('Error: No update options provided');
|
|
396
|
+
console.error(
|
|
397
|
+
'Available options: --title, --description, --status, --type, --tags, --content, --file, --working-folder'
|
|
398
|
+
);
|
|
399
|
+
process.exit(1);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const result = await updateAdr(manager, {
|
|
403
|
+
workspace:
|
|
404
|
+
typeof options.workspace === 'string' ? options.workspace : undefined,
|
|
405
|
+
name,
|
|
406
|
+
title: typeof options.title === 'string' ? options.title : undefined,
|
|
407
|
+
description:
|
|
408
|
+
typeof options.description === 'string' ? options.description : undefined,
|
|
409
|
+
status: validateStatus(options.status as string),
|
|
410
|
+
type: validateType(options.type as string),
|
|
411
|
+
tags: parseList(options.tags as string),
|
|
412
|
+
content,
|
|
413
|
+
workingFolder:
|
|
414
|
+
typeof options.workingFolder === 'string'
|
|
415
|
+
? options.workingFolder
|
|
416
|
+
: undefined,
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
if (!result.success) {
|
|
420
|
+
console.error(`Error: ${result.error}`);
|
|
421
|
+
process.exit(1);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
console.log(formatAdrUpdateCli(result.data!));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Main ADR command handler
|
|
429
|
+
*/
|
|
430
|
+
export async function adrCommand(args: string[]): Promise<void> {
|
|
431
|
+
const { subcommand, positional, options } = parseArgs(args);
|
|
432
|
+
|
|
433
|
+
// Show help if requested or no subcommand
|
|
434
|
+
if (options.help || !subcommand) {
|
|
435
|
+
console.log(HELP);
|
|
436
|
+
process.exit(0);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Load config and create workspace manager
|
|
440
|
+
const config = readConfig();
|
|
441
|
+
if (!config) {
|
|
442
|
+
console.error('Error: No configuration found. Run `gyrus init` first.');
|
|
443
|
+
process.exit(1);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const manager = new WorkspaceManager(config);
|
|
447
|
+
|
|
448
|
+
// Route to subcommand handler
|
|
449
|
+
switch (subcommand) {
|
|
450
|
+
case 'list':
|
|
451
|
+
case 'ls':
|
|
452
|
+
await handleList(manager, options);
|
|
453
|
+
break;
|
|
454
|
+
|
|
455
|
+
case 'read':
|
|
456
|
+
case 'get':
|
|
457
|
+
case 'show':
|
|
458
|
+
await handleRead(manager, positional, options);
|
|
459
|
+
break;
|
|
460
|
+
|
|
461
|
+
case 'search':
|
|
462
|
+
case 'find':
|
|
463
|
+
await handleSearch(manager, positional, options);
|
|
464
|
+
break;
|
|
465
|
+
|
|
466
|
+
case 'create':
|
|
467
|
+
case 'new':
|
|
468
|
+
case 'add':
|
|
469
|
+
await handleCreate(manager, options);
|
|
470
|
+
break;
|
|
471
|
+
|
|
472
|
+
case 'update':
|
|
473
|
+
case 'edit':
|
|
474
|
+
await handleUpdate(manager, positional, options);
|
|
475
|
+
break;
|
|
476
|
+
|
|
477
|
+
default:
|
|
478
|
+
console.error(`Unknown subcommand: ${subcommand}`);
|
|
479
|
+
console.log('\nRun `gyrus adr --help` for usage information.');
|
|
480
|
+
process.exit(1);
|
|
481
|
+
}
|
|
482
|
+
}
|