argsbarg 5.0.3 → 5.1.1
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/CHANGELOG.md +19 -1
- package/docs/cli-program.md +4 -4
- package/docs/config-schema.md +9 -9
- package/docs/configure.md +5 -1
- package/docs/mcp.md +1 -1
- package/examples/full-example/README.md +2 -2
- package/examples/full-example/src/program.ts +0 -3
- package/index.d.ts +2 -2
- package/package.json +1 -1
- package/src/builtins/builtins.test.ts +9 -3
- package/src/builtins/completion-bash.ts +2 -100
- package/src/builtins/completion-simulate-shared.ts +120 -0
- package/src/builtins/completion-zsh.ts +4 -102
- package/src/builtins/config.test.ts +4 -4
- package/src/builtins/config.ts +19 -22
- package/src/builtins/configure.ts +35 -4
- package/src/builtins/dispatch.ts +10 -15
- package/src/builtins/registry.ts +0 -2
- package/src/capabilities.ts +6 -12
- package/src/config/entry.ts +2 -2
- package/src/config/resolve.ts +2 -2
- package/src/configure/prompt.ts +2 -4
- package/src/docs/docs.test.ts +1 -1
- package/src/install/install-validate.test.ts +11 -0
- package/src/install/target-effective.ts +14 -2
- package/src/install/target-registry.ts +17 -0
- package/src/install/targets.test.ts +2 -0
- package/src/parse.ts +51 -172
- package/src/schema.ts +1 -1
- package/src/types.ts +2 -2
- package/src/validate.ts +12 -1
package/src/parse.ts
CHANGED
|
@@ -249,21 +249,6 @@ function finishLeaf(
|
|
|
249
249
|
optionDefs: CliOption[],
|
|
250
250
|
forcePositionalsIn: boolean,
|
|
251
251
|
): ParseResult {
|
|
252
|
-
/** Builds a parse error for positional consumption failures. */
|
|
253
|
-
function errorResult(msg: string): ParseResult {
|
|
254
|
-
const pr: ParseResult = {
|
|
255
|
-
kind: ParseKind.Error,
|
|
256
|
-
path: [],
|
|
257
|
-
opts: {},
|
|
258
|
-
args: [],
|
|
259
|
-
helpExplicit: false,
|
|
260
|
-
helpPath: [],
|
|
261
|
-
errorMsg: msg,
|
|
262
|
-
errorHelpPath: path,
|
|
263
|
-
};
|
|
264
|
-
return pr;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
252
|
let idx = startIdx;
|
|
268
253
|
const args: string[] = [];
|
|
269
254
|
let forcePositionals = forcePositionalsIn;
|
|
@@ -273,7 +258,7 @@ function finishLeaf(
|
|
|
273
258
|
if (argMax === 1) {
|
|
274
259
|
if (argMin >= 1) {
|
|
275
260
|
if (idx >= argv.length) {
|
|
276
|
-
return errorResult(`Missing positional argument: ${p.name}
|
|
261
|
+
return errorResult(`Missing positional argument: ${p.name}`, path, []);
|
|
277
262
|
}
|
|
278
263
|
args.push(argv[idx]);
|
|
279
264
|
idx += 1;
|
|
@@ -308,7 +293,7 @@ function finishLeaf(
|
|
|
308
293
|
// MUST be false — lenient mode swallows unknown flags as positionals silently
|
|
309
294
|
const tailRep = consumeOptions(optionDefs, false, argv, idx, opts);
|
|
310
295
|
if (tailRep.report.err) {
|
|
311
|
-
return errorResult(tailRep.report.err);
|
|
296
|
+
return errorResult(tailRep.report.err, path, []);
|
|
312
297
|
}
|
|
313
298
|
if (tailRep.report.sawDoubleDash) {
|
|
314
299
|
forcePositionals = true;
|
|
@@ -317,7 +302,7 @@ function finishLeaf(
|
|
|
317
302
|
idx = tailRep.nextIndex;
|
|
318
303
|
continue;
|
|
319
304
|
}
|
|
320
|
-
return errorResult(`Unexpected option token: ${tok}
|
|
305
|
+
return errorResult(`Unexpected option token: ${tok}`, path, []);
|
|
321
306
|
}
|
|
322
307
|
|
|
323
308
|
args.push(tok);
|
|
@@ -332,13 +317,17 @@ function finishLeaf(
|
|
|
332
317
|
}
|
|
333
318
|
}
|
|
334
319
|
if (count < argMin) {
|
|
335
|
-
return errorResult(
|
|
320
|
+
return errorResult(
|
|
321
|
+
`Expected at least ${argMin} argument(s) for ${p.name}, got ${count}`,
|
|
322
|
+
path,
|
|
323
|
+
[],
|
|
324
|
+
);
|
|
336
325
|
}
|
|
337
326
|
}
|
|
338
327
|
|
|
339
328
|
if (idx < argv.length) {
|
|
340
329
|
if (forcePositionals) {
|
|
341
|
-
return errorResult("Unexpected extra arguments");
|
|
330
|
+
return errorResult("Unexpected extra arguments", path, []);
|
|
342
331
|
}
|
|
343
332
|
|
|
344
333
|
if (isHelpTok(argv[idx])) {
|
|
@@ -347,12 +336,12 @@ function finishLeaf(
|
|
|
347
336
|
|
|
348
337
|
const tailRep = consumeOptions(optionDefs, false, argv, idx, opts);
|
|
349
338
|
if (tailRep.report.err) {
|
|
350
|
-
return errorResult(tailRep.report.err);
|
|
339
|
+
return errorResult(tailRep.report.err, path, []);
|
|
351
340
|
}
|
|
352
341
|
idx = tailRep.nextIndex;
|
|
353
342
|
|
|
354
343
|
if (idx < argv.length) {
|
|
355
|
-
return errorResult("Unexpected extra arguments");
|
|
344
|
+
return errorResult("Unexpected extra arguments", path, []);
|
|
356
345
|
}
|
|
357
346
|
}
|
|
358
347
|
|
|
@@ -370,6 +359,24 @@ function finishLeaf(
|
|
|
370
359
|
|
|
371
360
|
// ── Main Parser ───────────────────────────────────────────────────────────────
|
|
372
361
|
|
|
362
|
+
/** Builds a user-error parse result; `path` defaults to `errorHelpPath`. */
|
|
363
|
+
function errorResult(
|
|
364
|
+
errorMsg: string,
|
|
365
|
+
errorHelpPath: string[] = [],
|
|
366
|
+
path: string[] = errorHelpPath,
|
|
367
|
+
): ParseResult {
|
|
368
|
+
return {
|
|
369
|
+
kind: ParseKind.Error,
|
|
370
|
+
path,
|
|
371
|
+
opts: {},
|
|
372
|
+
args: [],
|
|
373
|
+
helpExplicit: false,
|
|
374
|
+
helpPath: [],
|
|
375
|
+
errorMsg,
|
|
376
|
+
errorHelpPath,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
|
|
373
380
|
/** Builds a help-request result for the current routing path. */
|
|
374
381
|
function helpResult(p: string[], explicit: boolean): ParseResult {
|
|
375
382
|
return {
|
|
@@ -401,16 +408,7 @@ export function parse(root: CliNode, argv: string[]): ParseResult {
|
|
|
401
408
|
// Consume root-level options first
|
|
402
409
|
const rootRep = consumeOptions(root.options ?? [], rootLenient, argv, i, opts);
|
|
403
410
|
if (rootRep.report.err) {
|
|
404
|
-
return
|
|
405
|
-
kind: ParseKind.Error,
|
|
406
|
-
path: [],
|
|
407
|
-
opts: {},
|
|
408
|
-
args: [],
|
|
409
|
-
helpExplicit: false,
|
|
410
|
-
helpPath: [],
|
|
411
|
-
errorMsg: rootRep.report.err,
|
|
412
|
-
errorHelpPath: [],
|
|
413
|
-
};
|
|
411
|
+
return errorResult(rootRep.report.err);
|
|
414
412
|
}
|
|
415
413
|
i = rootRep.nextIndex;
|
|
416
414
|
let forcePositionals = rootRep.report.sawDoubleDash;
|
|
@@ -436,16 +434,7 @@ export function parse(root: CliNode, argv: string[]): ParseResult {
|
|
|
436
434
|
cmdName = root.fallbackCommand;
|
|
437
435
|
node = findChild(root.commands, cmdName);
|
|
438
436
|
if (!node) {
|
|
439
|
-
return {
|
|
440
|
-
kind: ParseKind.Error,
|
|
441
|
-
path: [],
|
|
442
|
-
opts: {},
|
|
443
|
-
args: [],
|
|
444
|
-
helpExplicit: false,
|
|
445
|
-
helpPath: [],
|
|
446
|
-
errorMsg: `Unknown command: ${cmdName}`,
|
|
447
|
-
errorHelpPath: path,
|
|
448
|
-
};
|
|
437
|
+
return errorResult(`Unknown command: ${cmdName}`, path, []);
|
|
449
438
|
}
|
|
450
439
|
} else {
|
|
451
440
|
return helpResult([], false);
|
|
@@ -469,34 +458,20 @@ export function parse(root: CliNode, argv: string[]): ParseResult {
|
|
|
469
458
|
cmdName = fallbackCommand;
|
|
470
459
|
node = findChild(root.commands, cmdName);
|
|
471
460
|
if (!node) {
|
|
472
|
-
return {
|
|
473
|
-
kind: ParseKind.Error,
|
|
474
|
-
path: [],
|
|
475
|
-
opts: {},
|
|
476
|
-
args: [],
|
|
477
|
-
helpExplicit: false,
|
|
478
|
-
helpPath: [],
|
|
479
|
-
errorMsg: `Unknown command: ${cmdName}`,
|
|
480
|
-
errorHelpPath: path,
|
|
481
|
-
};
|
|
461
|
+
return errorResult(`Unknown command: ${cmdName}`, path, []);
|
|
482
462
|
}
|
|
483
463
|
} else {
|
|
484
464
|
cmdName = peek;
|
|
485
465
|
if (!forcePositionals) i += 1;
|
|
486
466
|
node = findChild(root.commands, cmdName);
|
|
487
467
|
if (!node) {
|
|
488
|
-
return
|
|
489
|
-
|
|
490
|
-
path: [],
|
|
491
|
-
opts: {},
|
|
492
|
-
args: [],
|
|
493
|
-
helpExplicit: false,
|
|
494
|
-
helpPath: [],
|
|
495
|
-
errorMsg: forcePositionals
|
|
468
|
+
return errorResult(
|
|
469
|
+
forcePositionals
|
|
496
470
|
? `Expected subcommand but got positional: ${cmdName}`
|
|
497
471
|
: `Unknown command: ${cmdName}`,
|
|
498
|
-
|
|
499
|
-
|
|
472
|
+
path,
|
|
473
|
+
[],
|
|
474
|
+
);
|
|
500
475
|
}
|
|
501
476
|
}
|
|
502
477
|
}
|
|
@@ -504,16 +479,7 @@ export function parse(root: CliNode, argv: string[]): ParseResult {
|
|
|
504
479
|
|
|
505
480
|
path.push(cmdName);
|
|
506
481
|
if (!node) {
|
|
507
|
-
return {
|
|
508
|
-
kind: ParseKind.Error,
|
|
509
|
-
path,
|
|
510
|
-
opts: {},
|
|
511
|
-
args: [],
|
|
512
|
-
helpExplicit: false,
|
|
513
|
-
helpPath: [],
|
|
514
|
-
errorMsg: `Unknown command: ${cmdName}`,
|
|
515
|
-
errorHelpPath: path,
|
|
516
|
-
};
|
|
482
|
+
return errorResult(`Unknown command: ${cmdName}`, path);
|
|
517
483
|
}
|
|
518
484
|
let current = node;
|
|
519
485
|
|
|
@@ -522,16 +488,7 @@ export function parse(root: CliNode, argv: string[]): ParseResult {
|
|
|
522
488
|
if (!forcePositionals) {
|
|
523
489
|
const orep = consumeOptions(collectOptionDefs(root, path), false, argv, i, opts);
|
|
524
490
|
if (orep.report.err) {
|
|
525
|
-
return
|
|
526
|
-
kind: ParseKind.Error,
|
|
527
|
-
path,
|
|
528
|
-
opts: {},
|
|
529
|
-
args: [],
|
|
530
|
-
helpExplicit: false,
|
|
531
|
-
helpPath: [],
|
|
532
|
-
errorMsg: orep.report.err,
|
|
533
|
-
errorHelpPath: path,
|
|
534
|
-
};
|
|
491
|
+
return errorResult(orep.report.err, path);
|
|
535
492
|
}
|
|
536
493
|
i = orep.nextIndex;
|
|
537
494
|
if (orep.report.sawDoubleDash) {
|
|
@@ -576,16 +533,7 @@ export function parse(root: CliNode, argv: string[]): ParseResult {
|
|
|
576
533
|
|
|
577
534
|
const tok = argv[i];
|
|
578
535
|
if (!forcePositionals && tok.startsWith("-")) {
|
|
579
|
-
return {
|
|
580
|
-
kind: ParseKind.Error,
|
|
581
|
-
path,
|
|
582
|
-
opts: {},
|
|
583
|
-
args: [],
|
|
584
|
-
helpExplicit: false,
|
|
585
|
-
helpPath: [],
|
|
586
|
-
errorMsg: `Unexpected option token: ${tok}`,
|
|
587
|
-
errorHelpPath: path,
|
|
588
|
-
};
|
|
536
|
+
return errorResult(`Unexpected option token: ${tok}`, path);
|
|
589
537
|
}
|
|
590
538
|
|
|
591
539
|
if (!forcePositionals && isCliRouter(current)) {
|
|
@@ -614,18 +562,12 @@ export function parse(root: CliNode, argv: string[]): ParseResult {
|
|
|
614
562
|
}
|
|
615
563
|
}
|
|
616
564
|
|
|
617
|
-
return
|
|
618
|
-
|
|
619
|
-
path,
|
|
620
|
-
opts: {},
|
|
621
|
-
args: [],
|
|
622
|
-
helpExplicit: false,
|
|
623
|
-
helpPath: [],
|
|
624
|
-
errorMsg: forcePositionals
|
|
565
|
+
return errorResult(
|
|
566
|
+
forcePositionals
|
|
625
567
|
? `Expected subcommand but got positional: ${tok}`
|
|
626
568
|
: `Unknown subcommand: ${tok}`,
|
|
627
|
-
|
|
628
|
-
|
|
569
|
+
path,
|
|
570
|
+
);
|
|
629
571
|
}
|
|
630
572
|
|
|
631
573
|
if (!isCliLeaf(current)) {
|
|
@@ -656,29 +598,11 @@ export function postParseValidate(root: CliNode, pr: ParseResult): ParseResult {
|
|
|
656
598
|
|
|
657
599
|
for (const seg of pr.path) {
|
|
658
600
|
if (!isCliRouter(node)) {
|
|
659
|
-
return
|
|
660
|
-
kind: ParseKind.Error,
|
|
661
|
-
path: pr.path,
|
|
662
|
-
opts: {},
|
|
663
|
-
args: [],
|
|
664
|
-
helpExplicit: false,
|
|
665
|
-
helpPath: [],
|
|
666
|
-
errorMsg: "Internal path error",
|
|
667
|
-
errorHelpPath: pr.path,
|
|
668
|
-
};
|
|
601
|
+
return errorResult("Internal path error", pr.path);
|
|
669
602
|
}
|
|
670
603
|
const ch = findChild(node.commands, seg);
|
|
671
604
|
if (!ch) {
|
|
672
|
-
return
|
|
673
|
-
kind: ParseKind.Error,
|
|
674
|
-
path: pr.path,
|
|
675
|
-
opts: {},
|
|
676
|
-
args: [],
|
|
677
|
-
helpExplicit: false,
|
|
678
|
-
helpPath: [],
|
|
679
|
-
errorMsg: "Internal path error",
|
|
680
|
-
errorHelpPath: pr.path,
|
|
681
|
-
};
|
|
605
|
+
return errorResult("Internal path error", pr.path);
|
|
682
606
|
}
|
|
683
607
|
defs.push(...(ch.options ?? []));
|
|
684
608
|
node = ch;
|
|
@@ -693,60 +617,24 @@ export function postParseValidate(root: CliNode, pr: ParseResult): ParseResult {
|
|
|
693
617
|
|
|
694
618
|
for (const d of defs) {
|
|
695
619
|
if (d.required && !(d.name in opts)) {
|
|
696
|
-
return {
|
|
697
|
-
kind: ParseKind.Error,
|
|
698
|
-
path: pr.path,
|
|
699
|
-
opts: {},
|
|
700
|
-
args: [],
|
|
701
|
-
helpExplicit: false,
|
|
702
|
-
helpPath: [],
|
|
703
|
-
errorMsg: `Missing required option: --${d.name}`,
|
|
704
|
-
errorHelpPath: pr.path,
|
|
705
|
-
};
|
|
620
|
+
return errorResult(`Missing required option: --${d.name}`, pr.path);
|
|
706
621
|
}
|
|
707
622
|
}
|
|
708
623
|
|
|
709
624
|
for (const [k, v] of Object.entries(opts)) {
|
|
710
625
|
const d = findOptionByName(defs, k);
|
|
711
626
|
if (!d) {
|
|
712
|
-
return {
|
|
713
|
-
kind: ParseKind.Error,
|
|
714
|
-
path: pr.path,
|
|
715
|
-
opts: {},
|
|
716
|
-
args: [],
|
|
717
|
-
helpExplicit: false,
|
|
718
|
-
helpPath: [],
|
|
719
|
-
errorMsg: `Unknown option key: ${k}`,
|
|
720
|
-
errorHelpPath: pr.path,
|
|
721
|
-
};
|
|
627
|
+
return errorResult(`Unknown option key: ${k}`, pr.path);
|
|
722
628
|
}
|
|
723
629
|
if (d.kind === CliOptionKind.Number) {
|
|
724
630
|
if (!fullStringIsDouble(v)) {
|
|
725
|
-
return {
|
|
726
|
-
kind: ParseKind.Error,
|
|
727
|
-
path: pr.path,
|
|
728
|
-
opts: {},
|
|
729
|
-
args: [],
|
|
730
|
-
helpExplicit: false,
|
|
731
|
-
helpPath: [],
|
|
732
|
-
errorMsg: `Invalid number for option --${k}: ${v}`,
|
|
733
|
-
errorHelpPath: pr.path,
|
|
734
|
-
};
|
|
631
|
+
return errorResult(`Invalid number for option --${k}: ${v}`, pr.path);
|
|
735
632
|
}
|
|
736
633
|
}
|
|
737
634
|
if (d.kind === CliOptionKind.Enum) {
|
|
738
635
|
const choices = d.choices ?? [];
|
|
739
636
|
if (!choices.includes(v)) {
|
|
740
|
-
return {
|
|
741
|
-
kind: ParseKind.Error,
|
|
742
|
-
path: pr.path,
|
|
743
|
-
opts: {},
|
|
744
|
-
args: [],
|
|
745
|
-
helpExplicit: false,
|
|
746
|
-
helpPath: [],
|
|
747
|
-
errorMsg: `Option --${k}: '${v}' is not one of: ${choices.join(", ")}`,
|
|
748
|
-
errorHelpPath: pr.path,
|
|
749
|
-
};
|
|
637
|
+
return errorResult(`Option --${k}: '${v}' is not one of: ${choices.join(", ")}`, pr.path);
|
|
750
638
|
}
|
|
751
639
|
}
|
|
752
640
|
if (d.kind === CliOptionKind.String && (d.format !== undefined || d.pattern !== undefined)) {
|
|
@@ -759,16 +647,7 @@ export function postParseValidate(root: CliNode, pr: ParseResult): ParseResult {
|
|
|
759
647
|
: err instanceof Error
|
|
760
648
|
? err.message
|
|
761
649
|
: String(err);
|
|
762
|
-
return {
|
|
763
|
-
kind: ParseKind.Error,
|
|
764
|
-
path: pr.path,
|
|
765
|
-
opts: {},
|
|
766
|
-
args: [],
|
|
767
|
-
helpExplicit: false,
|
|
768
|
-
helpPath: [],
|
|
769
|
-
errorMsg: `Invalid value for option --${k}: ${msg}`,
|
|
770
|
-
errorHelpPath: pr.path,
|
|
771
|
-
};
|
|
650
|
+
return errorResult(`Invalid value for option --${k}: ${msg}`, pr.path);
|
|
772
651
|
}
|
|
773
652
|
}
|
|
774
653
|
}
|
package/src/schema.ts
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
leafOutputSchema,
|
|
14
14
|
} from "./types.ts";
|
|
15
15
|
|
|
16
|
-
const RESERVED = new Set(["completion", "configure", "docs", "mcp", "version"
|
|
16
|
+
const RESERVED = new Set(["completion", "configure", "docs", "mcp", "version"]);
|
|
17
17
|
|
|
18
18
|
function exportCommand(cmd: CliNode, root: CliProgram): CliSchemaExport | null {
|
|
19
19
|
if (cmd.hidden) {
|
package/src/types.ts
CHANGED
|
@@ -236,7 +236,7 @@ export interface CliAppConfigEntry {
|
|
|
236
236
|
/** When `false`, optional for bootstrap and MCP enforcement. Default: `true`. */
|
|
237
237
|
required?: boolean;
|
|
238
238
|
/**
|
|
239
|
-
* Mask stdin during prompts and redact on `
|
|
239
|
+
* Mask stdin during prompts and redact on `configure get`.
|
|
240
240
|
* Default: `/key|token|secret|password/i.test(name)`.
|
|
241
241
|
*/
|
|
242
242
|
sensitive?: boolean;
|
|
@@ -253,7 +253,7 @@ export interface CliAppConfigEntry {
|
|
|
253
253
|
* App configuration block on the program root ({@link CliProgram.appConfig}).
|
|
254
254
|
*/
|
|
255
255
|
export interface CliAppConfig {
|
|
256
|
-
/** Built-in `
|
|
256
|
+
/** Built-in `configure get` / `configure set`. Default: enabled when `appConfig` is set. */
|
|
257
257
|
commands?: boolean | { enabled?: boolean; mcpSet?: boolean };
|
|
258
258
|
/** Block JSON Schema (draft-07). When omitted, synthesize all-string schema from `entries`. */
|
|
259
259
|
jsonSchema?: Record<string, unknown>;
|
package/src/validate.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { reservedCommandNames, resolveCapabilities } from "./capabilities.ts";
|
|
|
6
6
|
import { reservedDocsTopicResourceUris } from "./docs/mcp-resources.ts";
|
|
7
7
|
import { DOCS_BUILTIN_TOPIC_KEYS } from "./docs/resolve.ts";
|
|
8
8
|
import { validateFormatValue } from "./formats.ts";
|
|
9
|
-
import { AGENT_PAIRS } from "./install/target-registry.ts";
|
|
9
|
+
import { AGENT_PAIRS, MCP_KEYS, mcpServerRequiredForArtifact } from "./install/target-registry.ts";
|
|
10
10
|
import { resolveMcpSchemaUri } from "./mcp/tools.ts";
|
|
11
11
|
import {
|
|
12
12
|
type CliLeaf,
|
|
@@ -150,6 +150,8 @@ function validateConfigureConfig(program: CliProgram): void {
|
|
|
150
150
|
const integration: InstallAgentIntegration =
|
|
151
151
|
configure.agentIntegration ?? (program.mcpServer?.enabled === true ? "mcp" : "skill");
|
|
152
152
|
|
|
153
|
+
const mcpEnabled = program.mcpServer?.enabled === true;
|
|
154
|
+
|
|
153
155
|
for (const [mcpKey, skillKey] of AGENT_PAIRS) {
|
|
154
156
|
const mcpSpec = targets[mcpKey];
|
|
155
157
|
const skillSpec = targets[skillKey];
|
|
@@ -174,6 +176,15 @@ function validateConfigureConfig(program: CliProgram): void {
|
|
|
174
176
|
);
|
|
175
177
|
}
|
|
176
178
|
}
|
|
179
|
+
|
|
180
|
+
for (const mcpKey of MCP_KEYS) {
|
|
181
|
+
if (
|
|
182
|
+
!mcpServerRequiredForArtifact(mcpKey, mcpEnabled) &&
|
|
183
|
+
installTargetExplicitTruthy(targets[mcpKey])
|
|
184
|
+
) {
|
|
185
|
+
throw new CliSchemaValidationError(`configure.targets.${mcpKey} requires mcpServer.enabled`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
177
188
|
}
|
|
178
189
|
|
|
179
190
|
/** Validates a program schema. */
|