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