Repository: frictionlessdata/frictionless-py

Issue:
Consider remove frontmatter from default CLI output
# Overview

It seems to be more confusing than helpful. On the other hand, it creates some visual focus and separation:

![2](https://user-images.githubusercontent.com/557395/107607799-4f142780-6c4b-11eb-9b1f-a005186f1313.png)

![1](https://user-images.githubusercontent.com/557395/107607801-520f1800-6c4b-11eb-8fe3-650ba9588e30.png)



Discussion:

Patch:
diff --git a/frictionless/errors/row.py b/frictionless/errors/row.py
index 6277124d..e1c4d564 100644
--- a/frictionless/errors/row.py
+++ b/frictionless/errors/row.py
@@ -60,7 +60,7 @@ class PrimaryKeyError(RowError):
     code = "primary-key-error"
     name = "PrimaryKey Error"
     tags = ["#table", "#row"]
-    template = 'The row at position "{rowPosition}" does not conform to the primary key constraint: {note}'
+    template = 'Row at position "{rowPosition}" violates the primary key: {note}'
     description = "Values in the primary key fields should be unique for every row"
 
 
@@ -68,7 +68,7 @@ class ForeignKeyError(RowError):
     code = "foreign-key-error"
     name = "ForeignKey Error"
     tags = ["#table", "#row"]
-    template = 'The row at position "{rowPosition}" does not conform to the foreign key constraint: {note}'
+    template = 'Row at position "{rowPosition}" violates the foreign key: {note}'
     description = "Values in the foreign key fields should exist in the reference table"
 
 
diff --git a/frictionless/helpers.py b/frictionless/helpers.py
index 0bc80695..fb22133a 100644
--- a/frictionless/helpers.py
+++ b/frictionless/helpers.py
@@ -9,6 +9,7 @@ import zipfile
 import tempfile
 import datetime
 import platform
+import textwrap
 import stringcase
 from inspect import signature
 from importlib import import_module
@@ -21,6 +22,10 @@ from . import config
 # General
 
 
+def compact(text):
+    return " ".join(filter(None, textwrap.dedent(text).splitlines()))
+
+
 def apply_function(function, descriptor):
     options = create_options(descriptor)
     return function(**options)
diff --git a/frictionless/program/common.py b/frictionless/program/common.py
new file mode 100644
index 00000000..eeb1cdb8
--- /dev/null
+++ b/frictionless/program/common.py
@@ -0,0 +1,234 @@
+from typer import Argument, Option
+from .. import config
+
+# Source
+
+source = Argument(
+    default=None,
+    help="Data source [default: stdin]",
+)
+
+type = Option(
+    default=None,
+    help='Specify type e.g. "package"',
+)
+
+# File
+
+scheme = Option(
+    default=None,
+    help="Specify scheme  [default: inferred]",
+)
+
+format = Option(
+    default=None,
+    help="Specify format  [default: inferred]",
+)
+
+hashing = Option(
+    default=None,
+    help="Specify hashing algorithm  [default: inferred]",
+)
+
+encoding = Option(
+    default=None,
+    help="Specify encoding  [default: inferred]",
+)
+
+innerpath = Option(
+    default=None,
+    help="Specify in-archive path  [default: first]",
+)
+
+compression = Option(
+    default=None,
+    help="Specify compression  [default: inferred]",
+)
+
+# Layout
+
+header_rows = Option(
+    default=None,
+    help="Comma-separated row numbers [default: inferred]",
+)
+
+header_join = Option(
+    default=None,
+    help="Multiline header joiner [default: inferred]",
+)
+
+pick_fields = Option(
+    default=None,
+    help='Comma-separated fields to pick e.g. "1,name1"',
+)
+
+skip_fields = Option(
+    default=None,
+    help='Comma-separated fields to skip e.g. "2,name2"',
+)
+
+limit_fields = Option(
+    default=None,
+    help='Limit fields by this integer e.g. "10"',
+)
+
+offset_fields = Option(
+    default=None,
+    help='Offset fields by this integer e.g "5"',
+)
+
+pick_rows = Option(
+    default=None,
+    help='Comma-separated rows to pick e.g. "1,<blank>"',
+)
+
+skip_rows = Option(
+    default=None,
+    help='Comma-separated rows to skip e.g. "2,3,4,5"',
+)
+
+limit_rows = Option(
+    default=None,
+    help='Limit rows by this integer e.g "100"',
+)
+
+offset_rows = Option(
+    default=None,
+    help='Offset rows by this integer e.g. "50"',
+)
+
+# Schema
+
+schema = Option(
+    default=None,
+    help="Specify a path to a schema",
+)
+
+# Stats
+
+stats = Option(
+    default=None,
+    help="Infer stats",
+)
+
+stats_hash = Option(
+    default=None,
+    help="Expected hash based on hashing option",
+)
+
+stats_bytes = Option(
+    default=None,
+    help="Expected size in bytes",
+)
+
+stats_fields = Option(
+    default=None,
+    help="Expected amount of fields",
+)
+
+stats_rows = Option(
+    default=None,
+    help="Expected amount of rows",
+)
+
+# Detector
+
+buffer_size = Option(
+    default=config.DEFAULT_BUFFER_SIZE,
+    help="Limit the amount of bytes to be extracted as a buffer",
+)
+
+sample_size = Option(
+    default=config.DEFAULT_SAMPLE_SIZE,
+    help="Limit the number of rows to be extracted as a sample",
+)
+
+field_type = Option(
+    default=None,
+    help="Force all the fields to have this type",
+)
+
+field_names = Option(
+    default=None,
+    help="Comma-separated list of field names",
+)
+
+field_confidence = Option(
+    default=config.DEFAULT_FIELD_CONFIDENCE,
+    help=(
+        "Infer confidence. A float from 0 to 1. "
+        "If 1, (sampled) data is guaranteed to be valid against the inferred schema"
+    ),
+)
+
+field_float_numbers = Option(
+    default=config.DEFAULT_FLOAT_NUMBERS,
+    help="Make number floats instead of decimals",
+)
+
+field_missing_values = Option(
+    default=f'"{",".join(config.DEFAULT_MISSING_VALUES)}"',
+    help="Comma-separated list of missing values",
+)
+
+schema_sync = Option(
+    default=None,
+    help="Sync the schema based on headers",
+)
+
+# Command
+
+basepath = Option(
+    default=None,
+    help="Basepath of the resource/package",
+)
+
+expand = Option(
+    default=None,
+    help="Expand default values",
+)
+
+original = Option(
+    default=None,
+    help="Don't call infer on resources",
+)
+
+parallel = Option(
+    default=None,
+    help="Enable multiprocessing",
+)
+
+pick_errors = Option(
+    default=None,
+    help='Comma-separated errors to pick e.g. "type-error"',
+)
+
+skip_errors = Option(
+    default=None,
+    help='Comma-separated errors to skip e.g. "blank-row"',
+)
+
+limit_errors = Option(
+    default=None,
+    help="Limit errors by this integer",
+)
+
+limit_memory = Option(
+    default=None,
+    help="Limit memory by this integer in MB",
+)
+
+yaml = Option(
+    default=False,
+    help="Return in pure YAML format",
+)
+
+json = Option(
+    default=False,
+    help="Return in JSON format",
+)
+
+csv = Option(
+    default=False,
+    help="Return in CSV format",
+)
diff --git a/frictionless/program/describe.py b/frictionless/program/describe.py
index 7e1c3fab..cd9fe162 100644
--- a/frictionless/program/describe.py
+++ b/frictionless/program/describe.py
@@ -1,61 +1,52 @@
 import sys
 import typer
 from typing import List
-from typer import Option as Opt
-from typer import Argument as Arg
 from ..describe import describe
 from ..detector import Detector
 from ..layout import Layout
 from .main import program
 from .. import helpers
+from . import common
 
 
 @program.command(name="describe")
 def program_describe(
-    source: List[str] = Arg(None, help="Data source to describe [default: stdin]"),
-    type: str = Opt(None, help='Specify source type e.g. "package"'),
+    # Source
+    source: List[str] = common.source,
+    type: str = common.type,
     # File
-    scheme: str = Opt(None, help="Specify schema  [default: inferred]"),
-    format: str = Opt(None, help="Specify format  [default: inferred]"),
-    hashing: str = Opt(None, help="Specify hashing algorithm  [default: inferred]"),
-    encoding: str = Opt(None, help="Specify encoding  [default: inferred]"),
-    innerpath: str = Opt(None, help="Specify in-archive path  [default: first]"),
-    compression: str = Opt(None, help="Specify compression  [default: inferred]"),
+    scheme: str = common.scheme,
+    format: str = common.format,
+    hashing: str = common.hashing,
+    encoding: str = common.encoding,
+    innerpath: str = common.innerpath,
+    compression: str = common.compression,
     # Layout
-    header_rows: str = Opt(None, help="Comma-separated row numbers  [default: 1]"),
-    header_join: str = Opt(None, help="A separator to join a multiline header"),
-    pick_fields: str = Opt(None, help='Comma-separated fields to pick e.g. "1,name1"'),
-    skip_fields: str = Opt(None, help='Comma-separated fields to skip e.g. "2,name2"'),
-    limit_fields: int = Opt(None, help="Limit fields by this integer"),
-    offset_fields: int = Opt(None, help="Offset fields by this integer"),
-    pick_rows: str = Opt(None, help='Comma-separated rows to pick e.g. "1,<blank>"'),
-    skip_rows: str = Opt(None, help='Comma-separated rows to skip e.g. "2,3,4,5"'),
-    limit_rows: int = Opt(None, help="Limit rows by this integer"),
-    offset_rows: int = Opt(None, help="Offset rows by this integer"),
+    header_rows: str = common.header_rows,
+    header_join: str = common.header_join,
+    pick_fields: str = common.pick_fields,
+    skip_fields: str = common.skip_fields,
+    limit_fields: int = common.limit_fields,
+    offset_fields: int = common.offset_fields,
+    pick_rows: str = common.pick_rows,
+    skip_rows: str = common.skip_rows,
+    limit_rows: int = common.limit_rows,
+    offset_rows: int = common.offset_rows,
+    # Stats
+    stats: bool = common.stats,
     # Detector
-    buffer_size: int = Opt(
-        10000, help="Limit the amount of bytes to be extracted as a buffer"
-    ),
-    sample_size: int = Opt(
-        100, help="Limit the number of rows to be extracted as a sample"
-    ),
-    field_type: str = Opt(None, help="Force all the fields to have this type"),
-    field_names: str = Opt(None, help="Comma-separated list of field names"),
-    field_confidence: float = Opt(
-        0.9,
-        help=(
-            "Infer confidence. A float from 0 to 1. If 1, (sampled) data ",
-            "is guaranteed to be valid against the inferred schema",
-        ),
-    ),
-    field_float_numbers: bool = Opt(False, help="Make number floats instead of decimals"),
-    field_missing_values: str = Opt('""', help="Comma-separated list of missing values"),
-    # Description
-    basepath: str = Opt(None, help="Basepath of the resource/package"),
-    expand: bool = Opt(None, help="Expand default values"),
-    stats: bool = Opt(None, help="Do not infer stats"),
-    yaml: bool = Opt(False, help="Return in pure YAML format"),
-    json: bool = Opt(False, help="Return in JSON format"),
+    buffer_size: int = common.buffer_size,
+    sample_size: int = common.sample_size,
+    field_type: str = common.field_type,
+    field_names: str = common.field_names,
+    field_confidence: float = common.field_confidence,
+    field_float_numbers: bool = common.field_float_numbers,
+    field_missing_values: str = common.field_missing_values,
+    # Command
+    basepath: str = common.basepath,
+    expand: bool = common.expand,
+    yaml: bool = common.yaml,
+    json: bool = common.json,
 ):
     """
     Describe a data source.
@@ -155,9 +146,10 @@ def program_describe(
         source = "stdin"
     elif isinstance(source, list):
         source = " ".join(source)
-    typer.secho("---")
-    typer.secho(f"metadata: {source}", bold=True)
-    typer.secho("---")
+    prefix = "metadata"
+    typer.secho(f"# {'-'*len(prefix)}", bold=True)
+    typer.secho(f"# {prefix}: {source}", bold=True)
+    typer.secho(f"# {'-'*len(prefix)}", bold=True)
     typer.secho("")
     typer.secho(metadata.to_yaml().strip())
     typer.secho("")
diff --git a/frictionless/program/extract.py b/frictionless/program/extract.py
index 3e230d59..4a7d6586 100644
--- a/frictionless/program/extract.py
+++ b/frictionless/program/extract.py
@@ -4,53 +4,53 @@ import typer
 import json as pyjson
 import yaml as pyyaml
 from typing import List
-from typer import Option as Opt
-from typer import Argument as Arg
 from ..detector import Detector
 from ..extract import extract
 from ..layout import Layout
 from .main import program
 from .. import helpers
+from . import common
 
 
 @program.command(name="extract")
 def program_extract(
-    source: List[str] = Arg(None, help="Data source to describe [default: stdin]"),
-    type: str = Opt(None, help='Specify source type e.g. "package"'),
+    # Source
+    source: List[str] = common.source,
+    type: str = common.type,
     # File
-    scheme: str = Opt(None, help="Specify schema  [default: inferred]"),
-    format: str = Opt(None, help="Specify format  [default: inferred]"),
-    hashing: str = Opt(None, help="Specify hashing algorithm  [default: inferred]"),
-    encoding: str = Opt(None, help="Specify encoding  [default: inferred]"),
-    innerpath: str = Opt(None, help="Specify in-archive path  [default: first]"),
-    compression: str = Opt(None, help="Specify compression  [default: inferred]"),
+    scheme: str = common.scheme,
+    format: str = common.format,
+    hashing: str = common.hashing,
+    encoding: str = common.encoding,
+    innerpath: str = common.innerpath,
+    compression: str = common.compression,
     # Layout
-    header_rows: str = Opt(None, help="Comma-separated row numbers  [default: 1]"),
-    header_join: str = Opt(None, help="A separator to join a multiline header"),
-    pick_fields: str = Opt(None, help='Comma-separated fields to pick e.g. "1,name1"'),
-    skip_fields: str = Opt(None, help='Comma-separated fields to skip e.g. "2,name2"'),
-    limit_fields: int = Opt(None, help="Limit fields by this integer"),
-    offset_fields: int = Opt(None, help="Offset fields by this integer"),
-    pick_rows: str = Opt(None, help='Comma-separated rows to pick e.g. "1,<blank>"'),
-    skip_rows: str = Opt(None, help='Comma-separated rows to skip e.g. "2,3,4,5"'),
-    limit_rows: int = Opt(None, help="Limit rows by this integer"),
-    offset_rows: int = Opt(None, help="Offset rows by this integer"),
+    header_rows: str = common.header_rows,
+    header_join: str = common.header_join,
+    pick_fields: str = common.pick_fields,
+    skip_fields: str = common.skip_fields,
+    limit_fields: int = common.limit_fields,
+    offset_fields: int = common.offset_fields,
+    pick_rows: str = common.pick_rows,
+    skip_rows: str = common.skip_rows,
+    limit_rows: int = common.limit_rows,
+    offset_rows: int = common.offset_rows,
     # Schema
-    schema: str = Opt(None, help="Specify a path to a schema"),
+    schema: str = common.schema,
     # Detector
-    buffer_size: int = Opt(None, help="Limit byte buffer size by this integer"),
-    sample_size: int = Opt(None, help="Limit data sample size by this integer"),
-    field_type: str = Opt(None, help="Force all the fields to have this type"),
-    field_names: str = Opt(None, help="Comma-separated list of field names"),
-    field_confidence: float = Opt(None, help="A float from 0 to 1"),
-    field_float_numbers: bool = Opt(None, help="Make number floats instead of decimals"),
-    field_missing_values: str = Opt(None, help="Comma-separated list of missing values"),
-    schema_sync: bool = Opt(None, help="Sync the schema based on headers"),
-    # Extraction
-    basepath: str = Opt(None, help="Basepath of the resource/package"),
-    yaml: bool = Opt(False, help="Return in pure YAML format"),
-    json: bool = Opt(False, help="Return in JSON format"),
-    csv: bool = Opt(False, help="Return in CSV format"),
+    buffer_size: int = common.buffer_size,
+    sample_size: int = common.sample_size,
+    field_type: str = common.field_type,
+    field_names: str = common.field_names,
+    field_confidence: float = common.field_confidence,
+    field_float_numbers: bool = common.field_float_numbers,
+    field_missing_values: str = common.field_missing_values,
+    schema_sync: bool = common.schema_sync,
+    # Command
+    basepath: str = common.basepath,
+    yaml: bool = common.yaml,
+    json: bool = common.json,
+    csv: bool = common.csv,
 ):
     """
     Extract a data source.
@@ -167,9 +167,10 @@ def program_extract(
     for number, (name, rows) in enumerate(normdata.items(), start=1):
         if is_stdin:
             name = "stdin"
-        typer.secho("---")
-        typer.secho(f"data: {name}", bold=True)
-        typer.secho("---")
+        prefix = "data"
+        typer.secho(f"# {'-'*len(prefix)}", bold=True)
+        typer.secho(f"# {prefix}: {name}", bold=True)
+        typer.secho(f"# {'-'*len(prefix)}", bold=True)
         typer.secho("")
         subdata = helpers.rows_to_data(rows)
         typer.secho(str(petl.util.vis.lookall(subdata, vrepr=str, style="simple")))
diff --git a/frictionless/program/transform.py b/frictionless/program/transform.py
index 83ecfbb2..a5af7d31 100644
--- a/frictionless/program/transform.py
+++ b/frictionless/program/transform.py
@@ -1,22 +1,27 @@
 import sys
 import typer
 from ..transform import transform
-from typer import Argument as Arg
 from .main import program
 from .. import helpers
+from . import common
 
 
 @program.command(name="transform")
 def program_transform(
-    source: str = Arg(None, help="Path to a transform pipeline [default: stdin]"),
+    # Source
+    source: str = common.source,
 ):
-    """Transform data source using a provided pipeline."""
+    """Transform data using a provided pipeline.
+
+    Please read more about Transform pipelines to write a pipeline
+    that can be accepted by this funtion.
+    """
 
     # Support stdin
     is_stdin = False
     if not source:
         is_stdin = True
-        source = [helpers.create_byte_stream(sys.stdin.buffer.read())]
+        source = helpers.create_byte_stream(sys.stdin.buffer.read())
 
     # Transform source
     try:
@@ -28,6 +33,7 @@ def program_transform(
     # Return default
     if is_stdin:
         source = "stdin"
-    typer.secho("---")
-    typer.secho(f'success: "{source}"', bold=True)
-    typer.secho("---")
+    prefix = "success"
+    typer.secho(f"# {'-'*len(prefix)}", bold=True)
+    typer.secho(f"# {prefix}: {source}", bold=True)
+    typer.secho(f"# {'-'*len(prefix)}", bold=True)
diff --git a/frictionless/program/validate.py b/frictionless/program/validate.py
index 05fbc178..776a6ae3 100644
--- a/frictionless/program/validate.py
+++ b/frictionless/program/validate.py
@@ -2,63 +2,63 @@ import sys
 import petl
 import typer
 from typing import List
-from typer import Option as Opt
-from typer import Argument as Arg
 from ..validate import validate
 from ..detector import Detector
 from ..layout import Layout
 from .main import program
 from .. import helpers
+from . import common
 
 
 @program.command(name="validate")
 def program_validate(
-    source: List[str] = Arg(None, help="Data source to describe [default: stdin]"),
-    type: str = Opt(None, help='Specify source type e.g. "package"'),
+    # Source
+    source: List[str] = common.source,
+    type: str = common.type,
     # File
-    scheme: str = Opt(None, help="Specify schema  [default: inferred]"),
-    format: str = Opt(None, help="Specify format  [default: inferred]"),
-    hashing: str = Opt(None, help="Specify hashing algorithm  [default: inferred]"),
-    encoding: str = Opt(None, help="Specify encoding  [default: inferred]"),
-    innerpath: str = Opt(None, help="Specify in-archive path  [default: first]"),
-    compression: str = Opt(None, help="Specify compression  [default: inferred]"),
+    scheme: str = common.scheme,
+    format: str = common.format,
+    hashing: str = common.hashing,
+    encoding: str = common.encoding,
+    innerpath: str = common.innerpath,
+    compression: str = common.compression,
     # Layout
-    header_rows: str = Opt(None, help="Comma-separated row numbers  [default: 1]"),
-    header_join: str = Opt(None, help="A separator to join a multiline header"),
-    pick_fields: str = Opt(None, help='Comma-separated fields to pick e.g. "1,name1"'),
-    skip_fields: str = Opt(None, help='Comma-separated fields to skip e.g. "2,name2"'),
-    limit_fields: int = Opt(None, help="Limit fields by this integer"),
-    offset_fields: int = Opt(None, help="Offset fields by this integer"),
-    pick_rows: str = Opt(None, help='Comma-separated rows to pick e.g. "1,<blank>"'),
-    skip_rows: str = Opt(None, help='Comma-separated rows to skip e.g. "2,3,4,5"'),
-    limit_rows: int = Opt(None, help="Limit rows by this integer"),
-    offset_rows: int = Opt(None, help="Offset rows by this integer"),
+    header_rows: str = common.header_rows,
+    header_join: str = common.header_join,
+    pick_fields: str = common.pick_fields,
+    skip_fields: str = common.skip_fields,
+    limit_fields: int = common.limit_fields,
+    offset_fields: int = common.offset_fields,
+    pick_rows: str = common.pick_rows,
+    skip_rows: str = common.skip_rows,
+    limit_rows: int = common.limit_rows,
+    offset_rows: int = common.offset_rows,
     # Schema
-    schema: str = Opt(None, help="Specify a path to a schema"),
+    schema: str = common.schema,
     # Stats
-    stats_hash: str = Opt(None, help="Expected hash based on hashing option"),
-    stats_bytes: int = Opt(None, help="Expected size in bytes"),
-    stats_fields: int = Opt(None, help="Expected amount of fields"),
-    stats_rows: int = Opt(None, help="Expected amount of rows"),
+    stats_hash: str = common.stats_hash,
+    stats_bytes: int = common.stats_bytes,
+    stats_fields: int = common.stats_fields,
+    stats_rows: int = common.stats_rows,
     # Detector
-    buffer_size: int = Opt(None, help="Limit byte buffer size by this integer"),
-    sample_size: int = Opt(None, help="Limit data sample size by this integer"),
-    field_type: str = Opt(None, help="Force all the fields to have this type"),
-    field_names: str = Opt(None, help="Comma-separated list of field names"),
-    field_confidence: float = Opt(None, help="A float from 0 to 1"),
-    field_float_numbers: bool = Opt(None, help="Make number floats instead of decimals"),
-    field_missing_values: str = Opt(None, help="Comma-separated list of missing values"),
-    schema_sync: bool = Opt(None, help="Sync the schema based on headers"),
-    # Validation
-    basepath: str = Opt(None, help="Basepath of the resource/package"),
-    pick_errors: str = Opt(None, help='Comma-separated errors to pick e.g. "type-error"'),
-    skip_errors: str = Opt(None, help='Comma-separated errors to skip e.g. "blank-row"'),
-    limit_errors: int = Opt(None, help="Limit errors by this integer"),
-    limit_memory: int = Opt(None, help="Limit memory by this integer in MB"),
-    original: bool = Opt(None, help="Don't call infer on resources"),
-    parallel: bool = Opt(None, help="Enable multiprocessing"),
-    yaml: bool = Opt(False, help="Return in pure YAML format"),
-    json: bool = Opt(False, help="Return in JSON format"),
+    buffer_size: int = common.buffer_size,
+    sample_size: int = common.sample_size,
+    field_type: str = common.field_type,
+    field_names: str = common.field_names,
+    field_confidence: float = common.field_confidence,
+    field_float_numbers: bool = common.field_float_numbers,
+    field_missing_values: str = common.field_missing_values,
+    schema_sync: bool = common.schema_sync,
+    # Command
+    basepath: str = common.basepath,
+    pick_errors: str = common.pick_errors,
+    skip_errors: str = common.skip_errors,
+    limit_errors: int = common.limit_errors,
+    limit_memory: int = common.limit_memory,
+    original: bool = common.original,
+    parallel: bool = common.parallel,
+    yaml: bool = common.yaml,
+    json: bool = common.json,
 ):
     """
     Validate a data source.
@@ -181,9 +181,10 @@ def program_validate(
         content = []
         if is_stdin:
             source = "stdin"
-        typer.secho("---")
-        typer.secho(f"invalid: {source}", bold=True)
-        typer.secho("---")
+        prefix = "invalid"
+        typer.secho(f"# {'-'*len(prefix)}", bold=True)
+        typer.secho(f"# {prefix}: {source}", bold=True)
+        typer.secho(f"# {'-'*len(prefix)}", bold=True)
         for error in report.errors:
             content.append([error.code, error.message])
         typer.secho(
@@ -203,9 +204,9 @@ def program_validate(
         source = task.resource.path
         if is_stdin:
             source = "stdin"
-        typer.secho("---")
-        typer.secho(f"{prefix}: {source}", bold=True)
-        typer.secho("---")
+        typer.secho(f"# {'-'*len(prefix)}", bold=True)
+        typer.secho(f"# {prefix}: {source}", bold=True)
+        typer.secho(f"# {'-'*len(prefix)}", bold=True)
         if task.errors:
             prev_invalid = True
             typer.secho("")
diff --git a/frictionless/validate/resource.py b/frictionless/validate/resource.py
index 062b6973..a1b0223d 100644
--- a/frictionless/validate/resource.py
+++ b/frictionless/validate/resource.py
@@ -15,11 +15,11 @@ def validate_resource(
     *,
     # Validation
     checks=None,
+    original=False,
     pick_errors=None,
     skip_errors=None,
     limit_errors=config.DEFAULT_LIMIT_ERRORS,
     limit_memory=config.DEFAULT_LIMIT_MEMORY,
-    original=False,
     **options,
 ):
     """Validate table


Test patch:
diff --git a/tests/program/test_transform.py b/tests/program/test_transform.py
index be7c0dd6..876b9ebd 100644
--- a/tests/program/test_transform.py
+++ b/tests/program/test_transform.py
@@ -12,7 +12,7 @@ runner = CliRunner()
 def test_transform():
     result = runner.invoke(program, "transform data/pipeline.yaml")
     assert result.exit_code == 0
-    assert result.stdout.count('success: "data/pipeline.yaml"')
+    assert result.stdout.count("success: data/pipeline.yaml")
 
 
 def test_transform_error_not_found():