mustflow 2.23.0 → 2.24.2
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 +12 -2
- package/dist/cli/commands/adapters.js +11 -9
- package/dist/cli/commands/api.js +263 -113
- package/dist/cli/commands/check.js +11 -7
- package/dist/cli/commands/classify.js +16 -42
- package/dist/cli/commands/context.js +18 -31
- package/dist/cli/commands/contract-lint.js +12 -7
- package/dist/cli/commands/dashboard.js +65 -114
- package/dist/cli/commands/docs.js +43 -26
- package/dist/cli/commands/doctor.js +11 -7
- package/dist/cli/commands/evidence.js +642 -0
- package/dist/cli/commands/explain-verify.js +1 -59
- package/dist/cli/commands/explain.js +84 -36
- package/dist/cli/commands/handoff.js +13 -17
- package/dist/cli/commands/impact.js +14 -20
- package/dist/cli/commands/index.js +15 -9
- package/dist/cli/commands/init.js +56 -70
- package/dist/cli/commands/line-endings.js +15 -9
- package/dist/cli/commands/map.js +30 -42
- package/dist/cli/commands/next.js +300 -0
- package/dist/cli/commands/onboard.js +136 -0
- package/dist/cli/commands/run.js +47 -42
- package/dist/cli/commands/search.js +43 -69
- package/dist/cli/commands/status.js +9 -6
- package/dist/cli/commands/update.js +16 -10
- package/dist/cli/commands/upgrade.js +9 -6
- package/dist/cli/commands/verify/args.js +55 -249
- package/dist/cli/commands/verify.js +2 -1
- package/dist/cli/commands/version-sources.js +9 -6
- package/dist/cli/commands/version.js +9 -6
- package/dist/cli/commands/workspace.js +564 -0
- package/dist/cli/i18n/en.js +60 -1
- package/dist/cli/i18n/es.js +60 -1
- package/dist/cli/i18n/fr.js +60 -1
- package/dist/cli/i18n/hi.js +60 -1
- package/dist/cli/i18n/ko.js +60 -1
- package/dist/cli/i18n/zh.js +60 -1
- package/dist/cli/index.js +28 -25
- package/dist/cli/lib/agent-context.js +8 -9
- package/dist/cli/lib/command-registry.js +24 -0
- package/dist/cli/lib/dashboard-html/client-script.js +1 -1
- package/dist/cli/lib/local-index/database-path.js +5 -0
- package/dist/cli/lib/local-index/database-read.js +88 -0
- package/dist/cli/lib/local-index/effect-graph-read-model.js +112 -0
- package/dist/cli/lib/local-index/freshness.js +60 -0
- package/dist/cli/lib/local-index/index.js +12 -1866
- package/dist/cli/lib/local-index/path-surface-read-model.js +134 -0
- package/dist/cli/lib/local-index/populate.js +474 -0
- package/dist/cli/lib/local-index/schema.js +413 -0
- package/dist/cli/lib/local-index/search-read-model.js +533 -0
- package/dist/cli/lib/local-index/search-text.js +79 -0
- package/dist/cli/lib/option-parser.js +93 -0
- package/dist/cli/lib/repo-map.js +2 -2
- package/dist/cli/lib/run-plan.js +5 -22
- package/dist/core/change-verification.js +11 -5
- package/dist/core/command-effects.js +1 -3
- package/dist/core/command-intent-eligibility.js +14 -0
- package/dist/core/command-preconditions.js +8 -4
- package/dist/core/command-run-constraints.js +43 -0
- package/dist/core/public-json-contracts.js +57 -0
- package/dist/core/test-selection.js +8 -2
- package/dist/core/verification-plan.js +32 -4
- package/package.json +1 -1
- package/schemas/README.md +16 -0
- package/schemas/api-serve-response.schema.json +89 -0
- package/schemas/change-verification-report.schema.json +4 -1
- package/schemas/contract-lint-report.schema.json +1 -0
- package/schemas/evidence-report.schema.json +287 -0
- package/schemas/explain-report.schema.json +4 -0
- package/schemas/next-report.schema.json +121 -0
- package/schemas/onboard-commands-report.schema.json +100 -0
- package/schemas/workspace-command-catalog.schema.json +172 -0
- package/schemas/workspace-status.schema.json +141 -0
- package/schemas/workspace-verification-plan.schema.json +195 -0
- package/templates/default/manifest.toml +1 -1
|
@@ -6,64 +6,6 @@ import { createVerificationSchedule } from '../../core/verification-scheduler.js
|
|
|
6
6
|
import { t } from '../lib/i18n.js';
|
|
7
7
|
import { readLatestLocalVerificationReadModelQueries, readLocalCommandEffectGraphs, } from '../lib/local-index.js';
|
|
8
8
|
import { planErrorMessageKey, readInputFromClassificationReport } from './verify.js';
|
|
9
|
-
export function parseExplainVerifyArgs(args) {
|
|
10
|
-
let reason;
|
|
11
|
-
let fromPlan;
|
|
12
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
13
|
-
const arg = args[index];
|
|
14
|
-
if (arg === '--reason') {
|
|
15
|
-
const value = args[index + 1];
|
|
16
|
-
if (!value || value.startsWith('-')) {
|
|
17
|
-
return { reason, fromPlan, error: 'missing_reason_value' };
|
|
18
|
-
}
|
|
19
|
-
reason = value;
|
|
20
|
-
index += 1;
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
if (arg === '--from-plan') {
|
|
24
|
-
const value = args[index + 1];
|
|
25
|
-
if (!value || value.startsWith('-')) {
|
|
26
|
-
return { reason, fromPlan, error: 'missing_from_plan_value' };
|
|
27
|
-
}
|
|
28
|
-
fromPlan = value;
|
|
29
|
-
index += 1;
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
if (arg.startsWith('--reason=')) {
|
|
33
|
-
const value = arg.slice('--reason='.length);
|
|
34
|
-
if (value.length === 0) {
|
|
35
|
-
return { reason, fromPlan, error: 'missing_reason_value' };
|
|
36
|
-
}
|
|
37
|
-
reason = value;
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
if (arg.startsWith('--from-plan=')) {
|
|
41
|
-
const value = arg.slice('--from-plan='.length);
|
|
42
|
-
if (value.length === 0) {
|
|
43
|
-
return { reason, fromPlan, error: 'missing_from_plan_value' };
|
|
44
|
-
}
|
|
45
|
-
fromPlan = value;
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
if (arg.startsWith('-')) {
|
|
49
|
-
return { reason, fromPlan, error: arg };
|
|
50
|
-
}
|
|
51
|
-
return { reason, fromPlan, error: `unexpected:${arg}` };
|
|
52
|
-
}
|
|
53
|
-
return { reason, fromPlan };
|
|
54
|
-
}
|
|
55
|
-
export function explainVerifyArgErrorMessage(error, lang) {
|
|
56
|
-
if (error === 'missing_reason_value') {
|
|
57
|
-
return t(lang, 'cli.error.missingValue', { option: '--reason' });
|
|
58
|
-
}
|
|
59
|
-
if (error === 'missing_from_plan_value') {
|
|
60
|
-
return t(lang, 'cli.error.missingValue', { option: '--from-plan' });
|
|
61
|
-
}
|
|
62
|
-
if (error.startsWith('unexpected:')) {
|
|
63
|
-
return t(lang, 'cli.error.unexpectedArgument', { argument: error.slice('unexpected:'.length) });
|
|
64
|
-
}
|
|
65
|
-
return t(lang, 'cli.error.unknownOption', { option: error });
|
|
66
|
-
}
|
|
67
9
|
export function explainVerifyPlanErrorMessage(error, lang) {
|
|
68
10
|
const code = error instanceof Error ? error.message : 'invalid_plan_file';
|
|
69
11
|
return t(lang, planErrorMessageKey(code));
|
|
@@ -73,7 +15,7 @@ export function readExplainVerifyPlanReasons(projectRoot, planPath) {
|
|
|
73
15
|
}
|
|
74
16
|
export async function getVerifyExplainOutput(schemaVersion, projectRoot, reasons, inputReason, planSource) {
|
|
75
17
|
const contract = readCommandContract(projectRoot);
|
|
76
|
-
const plans = reasons.map((reason) => createVerificationPlan(contract, reason));
|
|
18
|
+
const plans = reasons.map((reason) => createVerificationPlan(contract, reason, projectRoot));
|
|
77
19
|
const graphRequirements = reasons.map((reason) => ({
|
|
78
20
|
reason,
|
|
79
21
|
files: [],
|
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { printUsageError, renderHelp } from '../lib/cli-output.js';
|
|
4
4
|
import { t } from '../lib/i18n.js';
|
|
5
5
|
import { MUSTFLOW_JSON_MAX_BYTES, readMustflowTextFile } from '../lib/mustflow-read.js';
|
|
6
|
+
import { formatCliOptionParseError, getParsedCliStringOption, hasCliOptionToken, hasParsedCliOption, parseCliOptions, } from '../lib/option-parser.js';
|
|
6
7
|
import { resolveMustflowRoot } from '../lib/project-root.js';
|
|
7
8
|
import { explainAssetOptimization, explainCommandIntent, } from '../../core/command-explanation.js';
|
|
8
9
|
import { readCommandContract, readMustflowConfigIfExists } from '../../core/config-loading.js';
|
|
@@ -14,10 +15,16 @@ import { explainPublicSurface } from '../../core/public-surface-explanation.js';
|
|
|
14
15
|
import { explainSourceAnchor } from '../../core/source-anchor-explanation.js';
|
|
15
16
|
import { checkMustflowProject } from '../lib/validation.js';
|
|
16
17
|
import { readLocalCommandEffectGraph, readLocalPathSurface, } from '../lib/local-index.js';
|
|
17
|
-
import {
|
|
18
|
+
import { explainVerifyPlanErrorMessage, getVerifyExplainOutput, readExplainVerifyPlanReasons, renderVerifyExplainDecision, } from './explain-verify.js';
|
|
18
19
|
import { createRunPlan, } from '../lib/run-plan.js';
|
|
19
20
|
const EXPLAIN_SCHEMA_VERSION = '1';
|
|
20
21
|
const LATEST_RUN_RECEIPT_RELATIVE_PATH = '.mustflow/state/runs/latest.json';
|
|
22
|
+
const EXPLAIN_OPTIONS = [
|
|
23
|
+
{ name: '--json', kind: 'boolean' },
|
|
24
|
+
{ name: '--why-blocked', kind: 'boolean' },
|
|
25
|
+
{ name: '--reason', kind: 'string' },
|
|
26
|
+
{ name: '--from-plan', kind: 'string' },
|
|
27
|
+
];
|
|
21
28
|
export function getExplainHelp(lang = 'en') {
|
|
22
29
|
return renderHelp({
|
|
23
30
|
usage: 'mf explain <topic> [target] [options] | mf explain verify --reason <event> [options] | mf explain why <target> [options] | mf explain --why-blocked <intent> [options]',
|
|
@@ -322,10 +329,30 @@ function getLatestFailureExplainOutput(projectRoot) {
|
|
|
322
329
|
function withWhyTopic(output) {
|
|
323
330
|
return { ...output, topic: 'why' };
|
|
324
331
|
}
|
|
325
|
-
|
|
332
|
+
function firstVerifyOnlyOption(options) {
|
|
333
|
+
if (options.reason !== null) {
|
|
334
|
+
return '--reason';
|
|
335
|
+
}
|
|
336
|
+
if (options.fromPlan !== null) {
|
|
337
|
+
return '--from-plan';
|
|
338
|
+
}
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
function rejectVerifyOnlyOption(options, reporter, lang) {
|
|
342
|
+
const option = firstVerifyOnlyOption(options);
|
|
343
|
+
if (!option) {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
printUsageError(reporter, t(lang, 'cli.error.unknownOption', { option }), 'mf explain --help', getExplainHelp(lang), lang);
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
async function getWhyExplainOutput(projectRoot, targetArg, rest, options, lang, reporter) {
|
|
326
350
|
switch (targetArg) {
|
|
327
351
|
case 'command':
|
|
328
352
|
case 'intent': {
|
|
353
|
+
if (rejectVerifyOnlyOption(options, reporter, lang)) {
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
329
356
|
const [commandName, ...extra] = rest;
|
|
330
357
|
if (!commandName) {
|
|
331
358
|
printUsageError(reporter, t(lang, 'explain.error.missingCommand'), 'mf explain --help', getExplainHelp(lang), lang);
|
|
@@ -338,12 +365,11 @@ async function getWhyExplainOutput(projectRoot, targetArg, rest, lang, reporter)
|
|
|
338
365
|
return withWhyTopic(await getCommandExplainOutput(projectRoot, commandName));
|
|
339
366
|
}
|
|
340
367
|
case 'verify': {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
printUsageError(reporter, explainVerifyArgErrorMessage(parsed.error, lang), 'mf explain --help', getExplainHelp(lang), lang);
|
|
368
|
+
if (rest.length > 0) {
|
|
369
|
+
printUsageError(reporter, t(lang, 'cli.error.unexpectedArgument', { argument: rest[0] }), 'mf explain --help', getExplainHelp(lang), lang);
|
|
344
370
|
return null;
|
|
345
371
|
}
|
|
346
|
-
const selectedInputCount = [
|
|
372
|
+
const selectedInputCount = [options.reason, options.fromPlan].filter(Boolean).length;
|
|
347
373
|
if (selectedInputCount > 1) {
|
|
348
374
|
printUsageError(reporter, t(lang, 'verify.error.conflictingInputs'), 'mf explain --help', getExplainHelp(lang), lang);
|
|
349
375
|
return null;
|
|
@@ -353,11 +379,11 @@ async function getWhyExplainOutput(projectRoot, targetArg, rest, lang, reporter)
|
|
|
353
379
|
return null;
|
|
354
380
|
}
|
|
355
381
|
try {
|
|
356
|
-
if (
|
|
357
|
-
const reasons = readExplainVerifyPlanReasons(projectRoot,
|
|
358
|
-
return withWhyTopic(await getVerifyExplainOutput(EXPLAIN_SCHEMA_VERSION, projectRoot, reasons, null,
|
|
382
|
+
if (options.fromPlan) {
|
|
383
|
+
const reasons = readExplainVerifyPlanReasons(projectRoot, options.fromPlan);
|
|
384
|
+
return withWhyTopic(await getVerifyExplainOutput(EXPLAIN_SCHEMA_VERSION, projectRoot, reasons, null, options.fromPlan));
|
|
359
385
|
}
|
|
360
|
-
return withWhyTopic(await getVerifyExplainOutput(EXPLAIN_SCHEMA_VERSION, projectRoot, [
|
|
386
|
+
return withWhyTopic(await getVerifyExplainOutput(EXPLAIN_SCHEMA_VERSION, projectRoot, [options.reason], options.reason, null));
|
|
361
387
|
}
|
|
362
388
|
catch (error) {
|
|
363
389
|
printUsageError(reporter, explainVerifyPlanErrorMessage(error, lang), 'mf explain --help', getExplainHelp(lang), lang);
|
|
@@ -365,6 +391,9 @@ async function getWhyExplainOutput(projectRoot, targetArg, rest, lang, reporter)
|
|
|
365
391
|
}
|
|
366
392
|
}
|
|
367
393
|
case 'skill': {
|
|
394
|
+
if (rejectVerifyOnlyOption(options, reporter, lang)) {
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
368
397
|
const [skillName, ...extra] = rest;
|
|
369
398
|
if (!skillName) {
|
|
370
399
|
printUsageError(reporter, t(lang, 'explain.error.missingSkill'), 'mf explain --help', getExplainHelp(lang), lang);
|
|
@@ -377,12 +406,18 @@ async function getWhyExplainOutput(projectRoot, targetArg, rest, lang, reporter)
|
|
|
377
406
|
return withWhyTopic(getSkillExplainOutput(projectRoot, skillName));
|
|
378
407
|
}
|
|
379
408
|
case 'skills':
|
|
409
|
+
if (rejectVerifyOnlyOption(options, reporter, lang)) {
|
|
410
|
+
return null;
|
|
411
|
+
}
|
|
380
412
|
if (rest.length > 0) {
|
|
381
413
|
printUsageError(reporter, t(lang, 'cli.error.unexpectedArgument', { argument: rest[0] }), 'mf explain --help', getExplainHelp(lang), lang);
|
|
382
414
|
return null;
|
|
383
415
|
}
|
|
384
416
|
return withWhyTopic(getSkillsExplainOutput(projectRoot));
|
|
385
417
|
case 'surface': {
|
|
418
|
+
if (rejectVerifyOnlyOption(options, reporter, lang)) {
|
|
419
|
+
return null;
|
|
420
|
+
}
|
|
386
421
|
const [pathArg, ...extra] = rest;
|
|
387
422
|
if (extra.length > 0) {
|
|
388
423
|
printUsageError(reporter, t(lang, 'cli.error.unexpectedArgument', { argument: extra[0] }), 'mf explain --help', getExplainHelp(lang), lang);
|
|
@@ -392,6 +427,9 @@ async function getWhyExplainOutput(projectRoot, targetArg, rest, lang, reporter)
|
|
|
392
427
|
}
|
|
393
428
|
case 'latest-failure':
|
|
394
429
|
case 'latest-run':
|
|
430
|
+
if (rejectVerifyOnlyOption(options, reporter, lang)) {
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
395
433
|
if (rest.length > 0) {
|
|
396
434
|
printUsageError(reporter, t(lang, 'cli.error.unexpectedArgument', { argument: rest[0] }), 'mf explain --help', getExplainHelp(lang), lang);
|
|
397
435
|
return null;
|
|
@@ -549,24 +587,38 @@ function renderExplainDecision(output, lang) {
|
|
|
549
587
|
return lines.join('\n');
|
|
550
588
|
}
|
|
551
589
|
export async function runExplain(args, reporter, lang = 'en') {
|
|
552
|
-
if (args
|
|
590
|
+
if (hasCliOptionToken(args, '--help', ['-h'])) {
|
|
553
591
|
reporter.stdout(getExplainHelp(lang));
|
|
554
592
|
return 0;
|
|
555
593
|
}
|
|
556
|
-
const
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
594
|
+
const parsed = parseCliOptions(args, EXPLAIN_OPTIONS, { allowPositionals: true });
|
|
595
|
+
if (parsed.error) {
|
|
596
|
+
printUsageError(reporter, formatCliOptionParseError(parsed.error, lang), 'mf explain --help', getExplainHelp(lang), lang);
|
|
597
|
+
return 1;
|
|
598
|
+
}
|
|
599
|
+
const options = {
|
|
600
|
+
json: hasParsedCliOption(parsed, '--json'),
|
|
601
|
+
whyBlocked: hasParsedCliOption(parsed, '--why-blocked'),
|
|
602
|
+
reason: getParsedCliStringOption(parsed, '--reason'),
|
|
603
|
+
fromPlan: getParsedCliStringOption(parsed, '--from-plan'),
|
|
604
|
+
positionals: parsed.positionals,
|
|
605
|
+
};
|
|
606
|
+
const [topic, targetArg, ...rest] = options.positionals;
|
|
607
|
+
if (options.whyBlocked) {
|
|
608
|
+
const [commandName, ...extra] = options.positionals;
|
|
609
|
+
if (rejectVerifyOnlyOption(options, reporter, lang)) {
|
|
610
|
+
return 1;
|
|
611
|
+
}
|
|
612
|
+
if (!commandName) {
|
|
561
613
|
printUsageError(reporter, t(lang, 'explain.error.missingCommand'), 'mf explain --help', getExplainHelp(lang), lang);
|
|
562
614
|
return 1;
|
|
563
615
|
}
|
|
564
|
-
if (
|
|
565
|
-
printUsageError(reporter, t(lang, 'cli.error.unexpectedArgument', { argument:
|
|
616
|
+
if (extra.length > 0) {
|
|
617
|
+
printUsageError(reporter, t(lang, 'cli.error.unexpectedArgument', { argument: extra[0] }), 'mf explain --help', getExplainHelp(lang), lang);
|
|
566
618
|
return 1;
|
|
567
619
|
}
|
|
568
|
-
const output = getWhyBlockedExplainOutput(resolveMustflowRoot(),
|
|
569
|
-
if (json) {
|
|
620
|
+
const output = getWhyBlockedExplainOutput(resolveMustflowRoot(), commandName);
|
|
621
|
+
if (options.json) {
|
|
570
622
|
reporter.stdout(JSON.stringify(output, null, 2));
|
|
571
623
|
return 0;
|
|
572
624
|
}
|
|
@@ -575,11 +627,11 @@ export async function runExplain(args, reporter, lang = 'en') {
|
|
|
575
627
|
}
|
|
576
628
|
if (topic === 'why') {
|
|
577
629
|
const projectRoot = resolveMustflowRoot();
|
|
578
|
-
const output = await getWhyExplainOutput(projectRoot, targetArg, rest, lang, reporter);
|
|
630
|
+
const output = await getWhyExplainOutput(projectRoot, targetArg, rest, options, lang, reporter);
|
|
579
631
|
if (!output) {
|
|
580
632
|
return 1;
|
|
581
633
|
}
|
|
582
|
-
if (json) {
|
|
634
|
+
if (options.json) {
|
|
583
635
|
reporter.stdout(JSON.stringify(output, null, 2));
|
|
584
636
|
return 0;
|
|
585
637
|
}
|
|
@@ -587,13 +639,11 @@ export async function runExplain(args, reporter, lang = 'en') {
|
|
|
587
639
|
return 0;
|
|
588
640
|
}
|
|
589
641
|
if (topic === 'verify') {
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
if (parsed.error) {
|
|
593
|
-
printUsageError(reporter, explainVerifyArgErrorMessage(parsed.error, lang), 'mf explain --help', getExplainHelp(lang), lang);
|
|
642
|
+
if (targetArg) {
|
|
643
|
+
printUsageError(reporter, t(lang, 'cli.error.unexpectedArgument', { argument: targetArg }), 'mf explain --help', getExplainHelp(lang), lang);
|
|
594
644
|
return 1;
|
|
595
645
|
}
|
|
596
|
-
const selectedInputCount = [
|
|
646
|
+
const selectedInputCount = [options.reason, options.fromPlan].filter(Boolean).length;
|
|
597
647
|
if (selectedInputCount > 1) {
|
|
598
648
|
printUsageError(reporter, t(lang, 'verify.error.conflictingInputs'), 'mf explain --help', getExplainHelp(lang), lang);
|
|
599
649
|
return 1;
|
|
@@ -605,28 +655,26 @@ export async function runExplain(args, reporter, lang = 'en') {
|
|
|
605
655
|
const projectRoot = resolveMustflowRoot();
|
|
606
656
|
let output;
|
|
607
657
|
try {
|
|
608
|
-
if (
|
|
609
|
-
const reasons = readExplainVerifyPlanReasons(projectRoot,
|
|
610
|
-
output = await getVerifyExplainOutput(EXPLAIN_SCHEMA_VERSION, projectRoot, reasons, null,
|
|
658
|
+
if (options.fromPlan) {
|
|
659
|
+
const reasons = readExplainVerifyPlanReasons(projectRoot, options.fromPlan);
|
|
660
|
+
output = await getVerifyExplainOutput(EXPLAIN_SCHEMA_VERSION, projectRoot, reasons, null, options.fromPlan);
|
|
611
661
|
}
|
|
612
662
|
else {
|
|
613
|
-
output = await getVerifyExplainOutput(EXPLAIN_SCHEMA_VERSION, projectRoot, [
|
|
663
|
+
output = await getVerifyExplainOutput(EXPLAIN_SCHEMA_VERSION, projectRoot, [options.reason], options.reason, null);
|
|
614
664
|
}
|
|
615
665
|
}
|
|
616
666
|
catch (error) {
|
|
617
667
|
printUsageError(reporter, explainVerifyPlanErrorMessage(error, lang), 'mf explain --help', getExplainHelp(lang), lang);
|
|
618
668
|
return 1;
|
|
619
669
|
}
|
|
620
|
-
if (json) {
|
|
670
|
+
if (options.json) {
|
|
621
671
|
reporter.stdout(JSON.stringify(output, null, 2));
|
|
622
672
|
return 0;
|
|
623
673
|
}
|
|
624
674
|
reporter.stdout(renderExplainDecision(output, lang));
|
|
625
675
|
return 0;
|
|
626
676
|
}
|
|
627
|
-
|
|
628
|
-
if (unsupported.length > 0) {
|
|
629
|
-
printUsageError(reporter, t(lang, 'cli.error.unknownOption', { option: unsupported[0] }), 'mf explain --help', getExplainHelp(lang), lang);
|
|
677
|
+
if (rejectVerifyOnlyOption(options, reporter, lang)) {
|
|
630
678
|
return 1;
|
|
631
679
|
}
|
|
632
680
|
if (topic !== 'asset-optimization' &&
|
|
@@ -700,7 +748,7 @@ export async function runExplain(args, reporter, lang = 'en') {
|
|
|
700
748
|
output = getSkillsExplainOutput(projectRoot);
|
|
701
749
|
break;
|
|
702
750
|
}
|
|
703
|
-
if (json) {
|
|
751
|
+
if (options.json) {
|
|
704
752
|
reporter.stdout(JSON.stringify(output, null, 2));
|
|
705
753
|
return 0;
|
|
706
754
|
}
|
|
@@ -4,7 +4,9 @@ import { MAX_HANDOFF_RECORD_BYTES, validateHandoffRecordJson, } from '../../core
|
|
|
4
4
|
import { printUsageError, renderHelp } from '../lib/cli-output.js';
|
|
5
5
|
import { ensureInsideWithoutSymlinks, readUtf8FileInsideWithoutSymlinks } from '../lib/filesystem.js';
|
|
6
6
|
import { t } from '../lib/i18n.js';
|
|
7
|
+
import { formatCliOptionParseError, hasCliOptionToken, hasParsedCliOption, parseCliOptions, } from '../lib/option-parser.js';
|
|
7
8
|
import { resolveMustflowRoot } from '../lib/project-root.js';
|
|
9
|
+
const HANDOFF_OPTIONS = [{ name: '--json', kind: 'boolean' }];
|
|
8
10
|
export function getHandoffHelp(lang = 'en') {
|
|
9
11
|
return renderHelp({
|
|
10
12
|
usage: 'mf handoff validate <path> [options]',
|
|
@@ -25,26 +27,20 @@ export function getHandoffHelp(lang = 'en') {
|
|
|
25
27
|
}
|
|
26
28
|
function parseHandoffArgs(args, lang) {
|
|
27
29
|
const [action, ...rest] = args;
|
|
28
|
-
let json = false;
|
|
29
|
-
let recordPath;
|
|
30
30
|
if (!action) {
|
|
31
|
-
return { action: 'validate', json, error: t(lang, 'handoff.error.missingAction') };
|
|
31
|
+
return { action: 'validate', json: false, error: t(lang, 'handoff.error.missingAction') };
|
|
32
32
|
}
|
|
33
33
|
if (action !== 'validate') {
|
|
34
|
-
return { action: 'validate', json, error: t(lang, 'handoff.error.unknownAction', { action }) };
|
|
34
|
+
return { action: 'validate', json: false, error: t(lang, 'handoff.error.unknownAction', { action }) };
|
|
35
35
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
if (recordPath) {
|
|
45
|
-
return { action, path: recordPath, json, error: t(lang, 'cli.error.unexpectedArgument', { argument: arg }) };
|
|
46
|
-
}
|
|
47
|
-
recordPath = arg;
|
|
36
|
+
const parsed = parseCliOptions(rest, HANDOFF_OPTIONS, { allowPositionals: true });
|
|
37
|
+
const json = hasParsedCliOption(parsed, '--json');
|
|
38
|
+
const [recordPath, unexpectedArgument] = parsed.positionals;
|
|
39
|
+
if (unexpectedArgument) {
|
|
40
|
+
return { action, path: recordPath, json, error: t(lang, 'cli.error.unexpectedArgument', { argument: unexpectedArgument }) };
|
|
41
|
+
}
|
|
42
|
+
if (parsed.error) {
|
|
43
|
+
return { action, path: recordPath, json, error: formatCliOptionParseError(parsed.error, lang) };
|
|
48
44
|
}
|
|
49
45
|
if (!recordPath) {
|
|
50
46
|
return { action, json, error: t(lang, 'handoff.error.missingPath') };
|
|
@@ -94,7 +90,7 @@ function renderHandoffReport(report, lang) {
|
|
|
94
90
|
}
|
|
95
91
|
export function runHandoff(args, reporter, lang = 'en') {
|
|
96
92
|
const helpText = getHandoffHelp(lang);
|
|
97
|
-
if (args
|
|
93
|
+
if (hasCliOptionToken(args, '--help', ['-h'])) {
|
|
98
94
|
reporter.stdout(helpText);
|
|
99
95
|
return 0;
|
|
100
96
|
}
|
|
@@ -4,10 +4,15 @@ import { printUsageError, renderHelp } from '../lib/cli-output.js';
|
|
|
4
4
|
import { isRecord } from '../lib/command-contract.js';
|
|
5
5
|
import { requireGitChangedFiles } from '../lib/git-changes.js';
|
|
6
6
|
import { t } from '../lib/i18n.js';
|
|
7
|
+
import { formatCliOptionParseError, hasCliOptionToken, hasParsedCliOption, parseCliOptions, } from '../lib/option-parser.js';
|
|
7
8
|
import { resolveMustflowRoot } from '../lib/project-root.js';
|
|
8
9
|
import { readMustflowTomlFile } from '../lib/toml.js';
|
|
9
10
|
import { detectVersionSources, releaseVersioningIsEnabled, } from '../../core/version-sources.js';
|
|
10
11
|
const IMPACT_SCHEMA_VERSION = '1';
|
|
12
|
+
const IMPACT_OPTIONS = [
|
|
13
|
+
{ name: '--json', kind: 'boolean' },
|
|
14
|
+
{ name: '--changed', kind: 'boolean' },
|
|
15
|
+
];
|
|
11
16
|
export function getImpactHelp(lang = 'en') {
|
|
12
17
|
return renderHelp({
|
|
13
18
|
usage: 'mf impact --changed [options] | mf impact <path...> [options]',
|
|
@@ -25,24 +30,13 @@ export function getImpactHelp(lang = 'en') {
|
|
|
25
30
|
}, lang);
|
|
26
31
|
}
|
|
27
32
|
function parseImpactArgs(args) {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
if (arg === '--changed') {
|
|
37
|
-
changed = true;
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
if (arg.startsWith('-')) {
|
|
41
|
-
return { json, changed, paths, error: arg };
|
|
42
|
-
}
|
|
43
|
-
paths.push(arg);
|
|
44
|
-
}
|
|
45
|
-
return { json, changed, paths };
|
|
33
|
+
const parsed = parseCliOptions(args, IMPACT_OPTIONS, { allowPositionals: true });
|
|
34
|
+
return {
|
|
35
|
+
json: hasParsedCliOption(parsed, '--json'),
|
|
36
|
+
changed: hasParsedCliOption(parsed, '--changed'),
|
|
37
|
+
paths: parsed.positionals,
|
|
38
|
+
error: parsed.error,
|
|
39
|
+
};
|
|
46
40
|
}
|
|
47
41
|
function readPreferences(projectRoot) {
|
|
48
42
|
try {
|
|
@@ -92,13 +86,13 @@ function renderImpactOutput(output, lang) {
|
|
|
92
86
|
return lines.join('\n');
|
|
93
87
|
}
|
|
94
88
|
export function runImpact(args, reporter, lang = 'en') {
|
|
95
|
-
if (args
|
|
89
|
+
if (hasCliOptionToken(args, '--help', ['-h'])) {
|
|
96
90
|
reporter.stdout(getImpactHelp(lang));
|
|
97
91
|
return 0;
|
|
98
92
|
}
|
|
99
93
|
const parsed = parseImpactArgs(args);
|
|
100
94
|
if (parsed.error) {
|
|
101
|
-
printUsageError(reporter,
|
|
95
|
+
printUsageError(reporter, formatCliOptionParseError(parsed.error, lang), 'mf impact --help', getImpactHelp(lang), lang);
|
|
102
96
|
return 1;
|
|
103
97
|
}
|
|
104
98
|
if (parsed.changed && parsed.paths.length > 0) {
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { printUsageError, renderHelp } from '../lib/cli-output.js';
|
|
2
2
|
import { t } from '../lib/i18n.js';
|
|
3
3
|
import { createLocalIndex } from '../lib/local-index.js';
|
|
4
|
+
import { formatCliOptionParseError, hasCliOptionToken, hasParsedCliOption, parseCliOptions, } from '../lib/option-parser.js';
|
|
4
5
|
import { resolveMustflowRoot } from '../lib/project-root.js';
|
|
6
|
+
const INDEX_OPTIONS = [
|
|
7
|
+
{ name: '--dry-run', kind: 'boolean' },
|
|
8
|
+
{ name: '--json', kind: 'boolean' },
|
|
9
|
+
{ name: '--source', kind: 'boolean' },
|
|
10
|
+
{ name: '--incremental', kind: 'boolean' },
|
|
11
|
+
];
|
|
5
12
|
export function getIndexHelp(lang = 'en') {
|
|
6
13
|
return renderHelp({
|
|
7
14
|
usage: 'mf index [options]',
|
|
@@ -67,22 +74,21 @@ function renderIndexSummary(result, lang) {
|
|
|
67
74
|
return lines.join('\n');
|
|
68
75
|
}
|
|
69
76
|
export async function runIndex(args, reporter, lang = 'en') {
|
|
70
|
-
if (args
|
|
77
|
+
if (hasCliOptionToken(args, '--help', ['-h'])) {
|
|
71
78
|
reporter.stdout(getIndexHelp(lang));
|
|
72
79
|
return 0;
|
|
73
80
|
}
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
printUsageError(reporter, t(lang, 'cli.error.unknownOption', { option: unsupported[0] }), 'mf index --help', getIndexHelp(lang), lang);
|
|
81
|
+
const parsed = parseCliOptions(args, INDEX_OPTIONS);
|
|
82
|
+
if (parsed.error) {
|
|
83
|
+
printUsageError(reporter, formatCliOptionParseError(parsed.error, lang), 'mf index --help', getIndexHelp(lang), lang);
|
|
78
84
|
return 1;
|
|
79
85
|
}
|
|
80
86
|
const result = await createLocalIndex(resolveMustflowRoot(), {
|
|
81
|
-
dryRun:
|
|
82
|
-
includeSource:
|
|
83
|
-
incremental:
|
|
87
|
+
dryRun: hasParsedCliOption(parsed, '--dry-run'),
|
|
88
|
+
includeSource: hasParsedCliOption(parsed, '--source'),
|
|
89
|
+
incremental: hasParsedCliOption(parsed, '--incremental'),
|
|
84
90
|
});
|
|
85
|
-
if (
|
|
91
|
+
if (hasParsedCliOption(parsed, '--json')) {
|
|
86
92
|
reporter.stdout(JSON.stringify(result, null, 2));
|
|
87
93
|
return 0;
|
|
88
94
|
}
|