1use sva_core::{DEFAULT_LEDGER_DEPTH, DEFAULT_MAX_PEAKS, DEFAULT_OVERSAMPLE};
4use sva_engine::{DEFAULT_FRAME_SECS, DEFAULT_SAMPLE_RATE, PSYCHOACOUSTIC_V1};
5
6pub fn help_text() -> String {
9 let budget = PSYCHOACOUSTIC_V1.flop_budget;
10 format!(
11 r#"USAGE:
12 sva-cli (render | analyze | lint | trace | builtins | new) [arguments]
13
14DESCRIPTION:
15 A composition is a directory of node files, each one closed-form expression in
16 `t` or `f`. sva-cli reads that composition and prints what it is and what it
17 sounds like, as JSON on stdout.
18
19 `--in <dir>` picks the composition for render, lint and trace, wherever in the
20 arguments it is written; without it they read the current directory. `new`
21 writes beside the current directory and `analyze` reads a file, so neither
22 takes `--in`.
23
24RENDER:
25 sva-cli render [<node|expression>] [query options]
26
27 Renders a node, `master` by default, and prints one reading per `--as`. The
28 argument may be an expression instead, in the grammar a node file's body uses.
29 Every reading states its `source` (exact or measured), the conformance profile
30 it ran under, and its rate.
31
32 `--as lines` and `--as atoms` read the closed form and allocate no buffer.
33 `--as samples=<path>.wav` writes 32-bit float audio, or 16-bit PCM under
34 `--pcm16`. Any other destination path takes the same JSON, uncapped. A path
35 that already holds a file refuses unless `--confirm` is written.
36
37 `--from`/`--to` bound the window a collapse runs over. `--sample-rate <hz>` is
38 the observation rate and is legal with every `--as`: no expression can read it.
39 `--no-cache` skips the disk store.
40
41 `--as ledger` prints one row per node under the target. A row's `share` is the
42 part of its reader's own energy that row accounts for, so one reader's refs sum
43 to 1; a ref no addend isolates, such as one factor of a product, prints `null`.
44 Each ref carrying a share is collapsed once on its own, so a ledger costs one
45 collapse per attributed ref beyond the render, and `--depth` bounds how many.
46 `--brief` keeps only the rows that clipped, `--skim` drops the wider fields.
47
48ANALYZE:
49 sva-cli analyze <file.wav> [--as <representation>[=<destination>]]...
50
51 Runs the same readings over an external `.wav` at its own rate, never
52 resampled. Only the readings a buffer answers alone apply; the rest need the
53 graph behind it.
54
55LINT:
56 sva-cli lint [<node|expression>] [--in <dir>] [--format <json|text>]
57
58 Checks binding, ref and tempo resolution without rendering a sample. With no
59 target it checks the whole directory against `master`. With a target it checks
60 only the nodes that target reaches, and `entry-point` does not run, since the
61 target's own reach references every node in it.
62
63 Every check prints one `data.diagnostics` item. `advice` and `warning` exit 0,
64 `error` exits non-zero, so branch on the verdict and never on whether the array
65 is empty. No flag downgrades an error.
66
67 error missing-comment no `;` comment line
68 multiline-comment more than one
69 malformed-comment not four ` | ` fields, `Models:` `Neglects:`
70 `IO: <in> -> <out>` `Tags: <tag>[, <tag>...]`
71 long-comment-block a `;` block over 1000 characters, the line-1
72 doc comment's own run exempted
73 long-expression-body a body over 10000 characters, a backstop rather
74 than a complexity budget
75 warning grid-rows-per-bar a TSV grid's row count does not divide evenly
76 into its filename's bar span
77 key-is-not-a-pitch `variables/key` holds neither a note name nor
78 a number of hertz
79 entry-point-refused a node a whole-directory render reaches does
80 not type
81 advice entry-point nothing references this node
82 no-default-root the directory has no `master`
83 tag-shape a tag over 3 lowercase words or 24 characters
84 window-inside-ramp a window sits wholly inside a crop's shoulder
85 literal-sample-rate a written rate where `sp` belongs
86 not-a-file a socket, FIFO or device in the directory
87 not-a-node a filename no `@ref` can spell
88
89TRACE:
90 sva-cli trace <node|expression> [--in <dir>]
91
92 Prints one node's position without rendering audio: what it reads (`down`, one
93 hop), everything that reads it (`up`, transitively to an entry point), each
94 beside the expression doing the reading, the node that made it discrete, and
95 the feedback loop it sits in, if any.
96
97BUILTINS:
98 sva-cli builtins
99
100 Prints the whole callable and syntactic vocabulary: every builtin with its
101 arity and named arguments, unit suffixes, the note-name grammar, reserved
102 identifiers, special call shapes, and what has no operator at all.
103
104NEW:
105 sva-cli new <name> [--idempotency-key <key>]
106
107 Writes a starter composition at ./<name>, and refuses if that directory
108 exists. `--idempotency-key <key>` records the key beside the composition, so a
109 retry under the same key succeeds identically while the tree still holds what
110 was written. Any other key, or an edited tree, refuses.
111
112EXAMPLES:
113 sva-cli new song1 && cd song1
114 cd ./song1 && sva-cli render --as samples=/tmp/song1.wav
115 sva-cli render master --in ./song1 --as ledger --as loudness
116 cd ./song1 && sva-cli render chord/home --as lines
117 sva-cli lint --in ./song1
118 cd ./song1 && sva-cli lint voice/note
119 cd ./song1 && sva-cli trace grid/phrase-2b
120 sva-cli builtins
121
122OUTPUT:
123 {{"status": "success", "data": {{"node": "master", "down": {{"items": [...]}},
124 "diagnostics": {{"items": []}}}},
125 "meta": {{"request_id": "req_...", "timestamp": 1700000000}}}}
126
127 An error adds "error": {{"code", "message", "details": {{"count", "codes"}}}}.
128 Success or error, every response carries every finding in full at
129 "data": {{"diagnostics": {{"items": [{{"code", "severity", "message",
130 "location", "help"}}], "pagination": {{"count", "has_more", "next_cursor"}}}}}},
131 empty where it found none.
132
133 Every collection carries that same {{items, pagination}} pair. `count` is the
134 whole reading's, `has_more` says `items` holds less than that, and
135 `next_cursor` is a `--from` value to pass back verbatim for the rest. A framed
136 measurement restarts its state at that instant, so a second page is a second
137 reading rather than a continuation. `--as <name>=<path>` writes the whole
138 reading to a file instead, uncapped. This page is an envelope of its own, at
139 "data": {{"help"}}.
140
141DEFAULTS:
142 --depth <n> how deep below its target a `ledger` walks.
143 Default {DEFAULT_LEDGER_DEPTH}.
144 --peaks <n> peaks a `spectrum` keeps, notes a `pitch`, formants a
145 `formants`. Default {DEFAULT_MAX_PEAKS}.
146 --oversample <n> the multiple `alias` re-renders at to hear what folded.
147 Default {DEFAULT_OVERSAMPLE}.
148 --frame <secs> the step a framed reading advances by, in seconds.
149 Default {DEFAULT_FRAME_SECS}, except `spectrum`, which
150 sizes its own transform to the window unless this flag is
151 given.
152 --sample-rate <hz> the observation rate a render lays its seconds on.
153 Default {DEFAULT_SAMPLE_RATE}.
154 --flop-budget <n> the operation count paid before a render refuses.
155 Default {budget}, the `psychoacoustic-v1` profile's own.
156 --format <json|text> how `lint` prints its findings: the envelope, or one
157 terminal line each, colored where stdout is a terminal.
158 The same objects either way. Default json.
159 --in <dir> the composition `render`, `lint` and `trace` read.
160 Default: the directory the process runs in.
161 --node <path> the instance a reading is taken of. Defaults to the
162 target itself; `--as bindings` requires it.
163 --against <file.wav> the second signal `--as masking` reads against. No
164 default: that one analysis requires it.
165 --from <time> default 0s; `--to <time>` defaults to the node's extent.
166 --confirm replaces a destination that already holds a file. Without
167 it a path already taken refuses as `conflict` and nothing
168 is written.
169 --no-cache, --brief, --skim and --pcm16 are all off unless written.
170
171EXIT CODES:
172 0 success (error.code absent)
173 1 internal_error (a destination could not be written)
174 3 validation_error (bad arguments, or a composition that failed to parse)
175 4 conflict (a name `new` would overwrite, or a destination already holding a
176 file, without `--confirm`)
177 24 not_found (a `.wav` file, or a node this composition does not define)
178
179VERB ALIASES:
180 validate = lint, list = builtins, create = new, show = trace, from the `cli`
181 standard's own verb list. `render` and `analyze` take a reading, which that
182 list has no word for, so they keep their own names.
183
184SEE ALSO:
185 sva-cli --version Show version information"#
186 )
187}