@remogram/cli 0.1.0-beta.3 → 0.1.0-beta.5
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/index.js +206 -6
- package/package.json +7 -7
package/index.js
CHANGED
|
@@ -21,6 +21,8 @@ import {
|
|
|
21
21
|
throwIfStaleHeadByNumber,
|
|
22
22
|
FACT_INVENTORY_PACKET_TYPES,
|
|
23
23
|
forgeFactInventoryPacket,
|
|
24
|
+
assertWriteCommandConfigured,
|
|
25
|
+
parseSinceObservedAt,
|
|
24
26
|
} from '@remogram/core';
|
|
25
27
|
import { provider as giteaApi } from '@remogram/provider-gitea-api';
|
|
26
28
|
import { provider as githubApi } from '@remogram/provider-github-api';
|
|
@@ -36,6 +38,13 @@ const PROVIDERS = {
|
|
|
36
38
|
'github-gh': githubGh,
|
|
37
39
|
};
|
|
38
40
|
|
|
41
|
+
const REPEATABLE_FLAGS = new Set(['allowed_path']);
|
|
42
|
+
|
|
43
|
+
function parseAllowedPathFlags(flags) {
|
|
44
|
+
if (flags.allowed_path == null) return undefined;
|
|
45
|
+
return Array.isArray(flags.allowed_path) ? flags.allowed_path : [flags.allowed_path];
|
|
46
|
+
}
|
|
47
|
+
|
|
39
48
|
function parsePositiveInt(value, name) {
|
|
40
49
|
if (value == null) return undefined;
|
|
41
50
|
const n = Number(value);
|
|
@@ -212,6 +221,22 @@ async function buildDoctorPacket(cwd, providers) {
|
|
|
212
221
|
),
|
|
213
222
|
);
|
|
214
223
|
|
|
224
|
+
if (providerCapabilities.write_support) {
|
|
225
|
+
const providerWrites = (providerCapabilities.write_commands || []).filter(Boolean);
|
|
226
|
+
const configuredWrites = Array.isArray(config?.write_commands) ? config.write_commands : [];
|
|
227
|
+
const missing = providerWrites.filter((name) => !configuredWrites.includes(name));
|
|
228
|
+
checks.push(
|
|
229
|
+
doctorCheck(
|
|
230
|
+
'write_config',
|
|
231
|
+
missing.length ? 'warn' : 'pass',
|
|
232
|
+
missing.length
|
|
233
|
+
? `Provider supports write commands but .remogram.json write_commands omits: ${missing.join(', ')}. Add ids for Remogram CLI/MCP writes, or use forge/CI tooling for those actions outside Remogram.`
|
|
234
|
+
: 'Consumer write_commands matches provider write surface',
|
|
235
|
+
{ provider_write_commands: providerWrites, configured_write_commands: configuredWrites },
|
|
236
|
+
),
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
215
240
|
if (!providerCapabilities.check_sources?.length) {
|
|
216
241
|
checks.push(doctorCheck('checks', 'warn', 'Provider does not report forge check sources'));
|
|
217
242
|
} else {
|
|
@@ -270,7 +295,13 @@ export async function runCli(argv, options = {}) {
|
|
|
270
295
|
else if (arg.startsWith('--')) {
|
|
271
296
|
const key = arg.slice(2).replace(/-/g, '_');
|
|
272
297
|
const next = argv[i + 1];
|
|
273
|
-
if (
|
|
298
|
+
if (REPEATABLE_FLAGS.has(key)) {
|
|
299
|
+
if (!flags[key]) flags[key] = [];
|
|
300
|
+
if (next != null && !next.startsWith('--')) {
|
|
301
|
+
flags[key].push(next);
|
|
302
|
+
i += 1;
|
|
303
|
+
}
|
|
304
|
+
} else if (next != null && !next.startsWith('--')) {
|
|
274
305
|
flags[key] = next;
|
|
275
306
|
i += 1;
|
|
276
307
|
} else {
|
|
@@ -361,12 +392,139 @@ export async function runCli(argv, options = {}) {
|
|
|
361
392
|
),
|
|
362
393
|
});
|
|
363
394
|
}
|
|
395
|
+
const inventoryBody = await provider.crInventory(ctx, {
|
|
396
|
+
slice_ref: flags.slice_ref,
|
|
397
|
+
limit: parsePositiveInt(flags.limit, '--limit'),
|
|
398
|
+
sort: flags.sort,
|
|
399
|
+
});
|
|
400
|
+
if (inventoryBody.list_truncated === true) {
|
|
401
|
+
throw Object.assign(new Error('Open CR list incomplete'), {
|
|
402
|
+
forgeError: forgeError(
|
|
403
|
+
ERROR_CODES.INVENTORY_LIST_INCOMPLETE,
|
|
404
|
+
'Open change request list could not be proved complete within pagination bounds',
|
|
405
|
+
null,
|
|
406
|
+
{
|
|
407
|
+
inventory_list: {
|
|
408
|
+
entry_count: inventoryBody.entry_count,
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
),
|
|
412
|
+
});
|
|
413
|
+
}
|
|
364
414
|
packet = forgeFactInventoryPacket(
|
|
365
415
|
FACT_INVENTORY_PACKET_TYPES.CR_INVENTORY_SLICE,
|
|
366
416
|
ctx,
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
417
|
+
inventoryBody,
|
|
418
|
+
);
|
|
419
|
+
} else if (group === 'cr' && sub === 'files') {
|
|
420
|
+
const number = parsePositiveInt(flags.number, '--number');
|
|
421
|
+
if (number == null) {
|
|
422
|
+
throw Object.assign(new Error('--number required'), {
|
|
423
|
+
forgeError: forgeError(ERROR_CODES.INVALID_ARGS, '--number required for cr files'),
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
if (typeof provider.crFiles !== 'function') {
|
|
427
|
+
throw Object.assign(new Error('cr files not implemented for provider'), {
|
|
428
|
+
forgeError: forgeError(
|
|
429
|
+
ERROR_CODES.PROVIDER_UNSUPPORTED,
|
|
430
|
+
'cr files not implemented for provider',
|
|
431
|
+
),
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
packet = forgePacket(
|
|
435
|
+
PACKET_TYPES.CR_FILES,
|
|
436
|
+
ctx,
|
|
437
|
+
await provider.crFiles(ctx, { number }),
|
|
438
|
+
);
|
|
439
|
+
} else if (group === 'cr' && sub === 'comments') {
|
|
440
|
+
const number = parsePositiveInt(flags.number, '--number');
|
|
441
|
+
if (number == null) {
|
|
442
|
+
throw Object.assign(new Error('--number required'), {
|
|
443
|
+
forgeError: forgeError(ERROR_CODES.INVALID_ARGS, '--number required for cr comments'),
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
if (typeof provider.crComments !== 'function') {
|
|
447
|
+
throw Object.assign(new Error('cr comments not implemented for provider'), {
|
|
448
|
+
forgeError: forgeError(
|
|
449
|
+
ERROR_CODES.PROVIDER_UNSUPPORTED,
|
|
450
|
+
'cr comments not implemented for provider',
|
|
451
|
+
),
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
packet = forgePacket(
|
|
455
|
+
PACKET_TYPES.CR_COMMENTS,
|
|
456
|
+
ctx,
|
|
457
|
+
await provider.crComments(ctx, { number }),
|
|
458
|
+
);
|
|
459
|
+
} else if (group === 'forge' && sub === 'changes') {
|
|
460
|
+
let sinceIso;
|
|
461
|
+
try {
|
|
462
|
+
sinceIso = parseSinceObservedAt(flags.since);
|
|
463
|
+
} catch (err) {
|
|
464
|
+
throw err;
|
|
465
|
+
}
|
|
466
|
+
if (typeof provider.forgeChanges !== 'function') {
|
|
467
|
+
throw Object.assign(new Error('forge changes not implemented for provider'), {
|
|
468
|
+
forgeError: forgeError(
|
|
469
|
+
ERROR_CODES.PROVIDER_UNSUPPORTED,
|
|
470
|
+
'forge changes not implemented for provider',
|
|
471
|
+
),
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
packet = forgePacket(
|
|
475
|
+
PACKET_TYPES.FORGE_CHANGES,
|
|
476
|
+
ctx,
|
|
477
|
+
await provider.forgeChanges(ctx, { since: sinceIso }),
|
|
478
|
+
);
|
|
479
|
+
} else if (group === 'cr' && sub === 'open') {
|
|
480
|
+
if (typeof provider.crOpen !== 'function') {
|
|
481
|
+
throw Object.assign(new Error('cr open not implemented for provider'), {
|
|
482
|
+
forgeError: forgeError(
|
|
483
|
+
ERROR_CODES.PROVIDER_UNSUPPORTED,
|
|
484
|
+
'cr open not implemented for provider',
|
|
485
|
+
),
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
if (!flags.head || !flags.base || !flags.title) {
|
|
489
|
+
throw Object.assign(new Error('--head, --base, and --title required'), {
|
|
490
|
+
forgeError: forgeError(
|
|
491
|
+
ERROR_CODES.INVALID_ARGS,
|
|
492
|
+
'--head, --base, and --title required for cr open',
|
|
493
|
+
),
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
assertGitRef(flags.head, '--head');
|
|
497
|
+
assertGitRef(flags.base, '--base');
|
|
498
|
+
assertWriteCommandConfigured(ctx.config, 'cr_open');
|
|
499
|
+
packet = forgePacket(
|
|
500
|
+
PACKET_TYPES.CHANGE_REQUEST_OPENED,
|
|
501
|
+
ctx,
|
|
502
|
+
await provider.crOpen(ctx, {
|
|
503
|
+
head: flags.head,
|
|
504
|
+
base: flags.base,
|
|
505
|
+
title: flags.title,
|
|
506
|
+
body: flags.body,
|
|
507
|
+
}),
|
|
508
|
+
);
|
|
509
|
+
} else if (group === 'status' && sub === 'set') {
|
|
510
|
+
if (typeof provider.statusSet !== 'function') {
|
|
511
|
+
throw Object.assign(new Error('status set not implemented for provider'), {
|
|
512
|
+
forgeError: forgeError(
|
|
513
|
+
ERROR_CODES.PROVIDER_UNSUPPORTED,
|
|
514
|
+
'status set not implemented for provider',
|
|
515
|
+
),
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
assertWriteCommandConfigured(ctx.config, 'status_set');
|
|
519
|
+
packet = forgePacket(
|
|
520
|
+
PACKET_TYPES.COMMIT_STATUS_SET,
|
|
521
|
+
ctx,
|
|
522
|
+
await provider.statusSet(ctx, {
|
|
523
|
+
sha: flags.sha,
|
|
524
|
+
context: flags.context,
|
|
525
|
+
state: flags.state,
|
|
526
|
+
target_url: flags.target_url,
|
|
527
|
+
description: flags.description,
|
|
370
528
|
}),
|
|
371
529
|
);
|
|
372
530
|
} else if (group === 'pr' && sub === 'view') {
|
|
@@ -415,7 +573,49 @@ export async function runCli(argv, options = {}) {
|
|
|
415
573
|
forgeError: forgeError(ERROR_CODES.INVALID_ARGS, '--number required for merge plan'),
|
|
416
574
|
});
|
|
417
575
|
}
|
|
418
|
-
|
|
576
|
+
const allowedPaths = parseAllowedPathFlags(flags);
|
|
577
|
+
packet = forgePacket(
|
|
578
|
+
PACKET_TYPES.MERGE_PLAN,
|
|
579
|
+
ctx,
|
|
580
|
+
await provider.mergePlan(ctx, {
|
|
581
|
+
number,
|
|
582
|
+
...(allowedPaths ? { allowed_paths: allowedPaths } : {}),
|
|
583
|
+
}),
|
|
584
|
+
);
|
|
585
|
+
} else if (group === 'branch' && sub === 'protection') {
|
|
586
|
+
const branchRef = flags.branch_ref;
|
|
587
|
+
if (!branchRef) {
|
|
588
|
+
throw Object.assign(new Error('--branch-ref required'), {
|
|
589
|
+
forgeError: forgeError(
|
|
590
|
+
ERROR_CODES.INVALID_ARGS,
|
|
591
|
+
'--branch-ref required for branch protection',
|
|
592
|
+
),
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
assertGitRef(branchRef, '--branch-ref');
|
|
596
|
+
if (typeof provider.branchProtection !== 'function') {
|
|
597
|
+
throw Object.assign(new Error('branch protection not implemented for provider'), {
|
|
598
|
+
forgeError: forgeError(
|
|
599
|
+
ERROR_CODES.PROVIDER_UNSUPPORTED,
|
|
600
|
+
'branch protection not implemented for provider',
|
|
601
|
+
),
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
packet = forgePacket(
|
|
605
|
+
PACKET_TYPES.BRANCH_PROTECTION,
|
|
606
|
+
ctx,
|
|
607
|
+
await provider.branchProtection(ctx, { branchRef }),
|
|
608
|
+
);
|
|
609
|
+
} else if (group === 'whoami' && sub == null) {
|
|
610
|
+
if (typeof provider.whoami !== 'function') {
|
|
611
|
+
throw Object.assign(new Error('whoami not implemented for provider'), {
|
|
612
|
+
forgeError: forgeError(
|
|
613
|
+
ERROR_CODES.PROVIDER_UNSUPPORTED,
|
|
614
|
+
'whoami not implemented for provider',
|
|
615
|
+
),
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
packet = forgePacket(PACKET_TYPES.PROVIDER_IDENTITY, ctx, await provider.whoami(ctx));
|
|
419
619
|
} else if (group === 'sync' && sub === 'plan') {
|
|
420
620
|
const remote = flags.remote || ctx.config.remote;
|
|
421
621
|
assertGitRemote(remote, '--remote');
|
|
@@ -428,7 +628,7 @@ export async function runCli(argv, options = {}) {
|
|
|
428
628
|
throw Object.assign(new Error(`Unknown command: ${positional.join(' ')}`), {
|
|
429
629
|
forgeError: forgeError(
|
|
430
630
|
ERROR_CODES.INVALID_ARGS,
|
|
431
|
-
'Unknown command. Try: provider capabilities, repo status, refs compare, refs inventory, cr inventory, pr view, pr checks, merge plan, sync plan',
|
|
631
|
+
'Unknown command. Try: provider capabilities, repo status, refs compare, refs inventory, cr inventory, cr files, cr comments, cr open, status set, forge changes, pr view, pr checks, merge plan, sync plan, whoami, branch protection',
|
|
432
632
|
),
|
|
433
633
|
});
|
|
434
634
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remogram/cli",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.5",
|
|
4
4
|
"description": "Remogram forge boundary CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"node": ">=20"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@remogram/core": "0.1.0-beta.
|
|
31
|
-
"@remogram/provider-gitea-api": "0.1.0-beta.
|
|
32
|
-
"@remogram/provider-github-api": "0.1.0-beta.
|
|
33
|
-
"@remogram/provider-gitlab-api": "0.1.0-beta.
|
|
34
|
-
"@remogram/provider-gitea-tea": "0.1.0-beta.
|
|
35
|
-
"@remogram/provider-github-gh": "0.1.0-beta.
|
|
30
|
+
"@remogram/core": "0.1.0-beta.5",
|
|
31
|
+
"@remogram/provider-gitea-api": "0.1.0-beta.5",
|
|
32
|
+
"@remogram/provider-github-api": "0.1.0-beta.5",
|
|
33
|
+
"@remogram/provider-gitlab-api": "0.1.0-beta.5",
|
|
34
|
+
"@remogram/provider-gitea-tea": "0.1.0-beta.5",
|
|
35
|
+
"@remogram/provider-github-gh": "0.1.0-beta.5"
|
|
36
36
|
}
|
|
37
37
|
}
|