@sous-io/sous 0.2.0 → 0.2.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.
@@ -1,580 +1,331 @@
1
1
  # Consuming Recipes
2
2
 
3
- This is the task-oriented guide to using someone else's recipes in your project. It assumes you
4
- have read [Repositories](repositories.md) for the model, and it points at
5
- [Repository file formats](repositories-file-formats.md) for every schema rather than repeating
6
- them.
3
+ The task guide to using someone else's recipes. [Repositories](repositories.md) explains the model
4
+ and [Repository file formats](repositories-file-formats.md) holds every schema.
7
5
 
8
6
  ## Add a repository
9
7
 
10
- Adding a repository is how you trust it, so this is the one step that asks you a question:
8
+ Adding a repository is how you trust it, so it is the one step that asks a question, and what that
9
+ decision covers is [Trust](repositories.md#trust). Exactly one file is fetched, its
10
+ `sous.index.json`, which is all sous needs to resolve refs and list versions:
11
11
 
12
12
  ```term
13
13
  $ sous repo add https://github.com/sous-io/sous-recipes
14
14
  // the trust question, then one small download
15
+ ▶ Adding a repository:
15
16
  Repository: sous-recipes
16
17
  Location : https://github.com/sous-io/sous-recipes
17
18
  Provider : github
18
19
  Namespaces: communication, core, tool-usage, workflow
19
20
  Recipes : 6
20
-
21
21
  This project now trusts 'sous-recipes'. Nothing from it has been installed.
22
22
  ```
23
23
 
24
- Exactly one file is fetched: the repository's `sous.index.json`. That is everything sous needs
25
- in order to resolve a ref, list versions and decide what to download later, so adding a
26
- repository costs one small request and installs nothing.
24
+ `--name` sets the short name refs will use (it defaults to the last segment of the URL; `sous repo
25
+ link` on a path instead takes the name that checkout's own manifest suggests, so see
26
+ [Edit a repository in place](repositories-authoring.md#edit-a-repository-in-place) before you name
27
+ the same repository twice), and
28
+ `--provider github|gitlab|local` names the [provider](repositories-providers.md#how-a-url-is-matched)
29
+ for a host the URL does not give away. `--dry-run` prints what would change without trusting or
30
+ fetching anything, and `-y` accepts trust without being asked; its other spellings are under
31
+ [Flags that answer questions](commands.md#flags-that-answer-questions). The entry lands in the
32
+ committed `.sous/conf.d/500-repos.jsonc`, so colleagues inherit the repository and the trust
33
+ decision with it; sous edits that file by key, so your comments and key order survive.
27
34
 
28
- | Flag | What it does |
29
- |------|--------------|
30
- | `--name <name>` | The short name refs will use. Defaults to the last segment of the URL |
31
- | `--provider github\|gitlab\|local` | The provider that handles it, for a host the URL does not give away |
32
- | `-y, --yes` | Accept trust without being asked, for a run with no terminal. `--trust`, `--force` and `-f` are the same flag |
33
- | `--dry-run` | Print what would change without trusting or fetching anything |
35
+ A repository need not be hosted: `sous repo add ../my-recipes --name my-recipes` reads a path
36
+ through the built-in `local` provider, which resolves it against your working directory and stores
37
+ the absolute form; [The local provider](repositories-providers.md#the-local-provider) covers how it
38
+ reads the working tree and honors a version's tag.
34
39
 
35
- The entry lands in `.sous/conf.d/500-repos.jsonc`, which is committed, so your colleagues inherit
36
- both the repository and the trust decision. Sous edits that file by key, so anything you write in
37
- it yourself, comments included, stays where you put it.
40
+ !> A local path goes through the same trust question as a hosted one; see
41
+ [Trust](repositories.md#trust). To edit a repository you already subscribe to, use
42
+ [`sous repo link`](repositories-authoring.md#edit-a-repository-in-place).
38
43
 
39
- ## Browse what you trust
44
+ ## Find a recipe
40
45
 
41
- Four commands read the cached indexes and the lockfile, so all four work offline and none of them
42
- downloads anything:
46
+ These commands read the cached indexes and the lockfile; all work offline and download nothing:
43
47
 
44
48
  ```bash
45
- sous namespace list
46
- sous namespace show workflow
47
- sous recipe list
48
- sous recipe show workflow/task-files
49
+ sous search qa # names and descriptions everywhere; --limit defaults to 25
50
+ sous namespace list # every namespace, its recipe count, whether you subscribe
51
+ sous namespace show workflow # one namespace and the recipes in it
52
+ sous recipe list # every recipe, latest version, pinned version, subscribed
49
53
  ```
50
54
 
51
- The listings answer "what is there, and what do I already have of it": every namespace with how
52
- many recipes it holds and how much of it you subscribe to, and every recipe with its latest
53
- version, the version your lockfile pins and whether you are subscribed.
54
-
55
- `sous recipe show` is the one to read before subscribing to something. It describes one recipe
56
- completely from what sous already has: every published version, what the version depends on (as the
57
- recipe's manifest declares it, beside the exact version its repository's index resolved that to),
58
- the questions it will ask you and where each answer is stored, and the directories its files would
59
- be written into in this project.
55
+ Read `sous recipe show` before subscribing: every published version, what it depends on (as
56
+ declared, beside the version the index resolved it to), and its questions and files once it has
57
+ them here.
60
58
 
61
59
  ```term
62
- $ sous recipe show workflow/task-files
63
- Repository sous-recipes
64
- Location https://github.com/sous-io/sous-recipes
65
- Folder recipes/workflow/task-files
66
- Latest version 1.0.1
67
- Pinned version 1.0.1
68
- Subscribed yes
60
+ $ sous recipe show workflow/qa-variables
61
+ Latest version : 0.1.0
62
+ Pinned version : this project pins none
63
+ Subscribed : no
64
+ Dependency Declared as Resolved to Repository Kind
65
+ ------------------ ------------------------------ ----------- --------------- ----------------
66
+ workflow/qa-helper not declared in the manifest 0.1.0 this repository unknown
67
+ The recipe's own files are not on this machine, so the questions it asks and the files it
68
+ publishes are not known here. Subscribing to it fetches them.
69
69
  ```
70
70
 
71
- The questions and the file list live inside the recipe's own files, so a recipe you have not
72
- installed is described from its index alone and says as much; subscribing to it fetches the rest.
73
-
74
71
  ## Subscribe to a recipe
75
72
 
76
73
  ```bash
77
- sous subscription add workflow/task-files
74
+ sous subscription add workflow/qa-variables
75
+ sous subscription add workflow/qa-variables@^1.2.0
76
+ sous subscription add workflow # a whole namespace
78
77
  ```
79
78
 
80
- ?> `sous subscribe` is the original spelling of this command and still works, as does
81
- `sous unsubscribe` for `subscription remove`. Every topic also answers to both spellings of its
82
- name, so `sous subscriptions add`, `sous repos list` and `sous var show` all work too.
83
-
84
- The ref names a namespace, one recipe, or either with a version range. The whole dependency
85
- closure is resolved before anything is downloaded, and only then is anything written; installs
86
- are whole or not at all.
79
+ The whole dependency closure resolves before anything is downloaded, and only then is anything
80
+ written; an install is whole or not at all.
87
81
 
88
82
  | Flag | What it does |
89
83
  |------|--------------|
90
- | `-y, --yes` | Answer yes to both questions this command can ask: the subscribe confirmation, and the trust question for any repository it has to add. `--trust`, `--force` and `-f` are the same flag |
84
+ | `-y, --yes` | Answer yes to both questions this command can ask: the subscribe confirmation, and the trust question for a repository it has to add |
91
85
  | `--accept-first` | When a one-word ref matches several things, take the first one listed |
92
- | `--prerelease` | Let prerelease versions take part in version range matching |
86
+ | `--answer <name>=<value>` | Answer one question ahead of time. Repeat it per answer, or read a whole file of them with `--answers-file <path>` |
93
87
  | `--always-pull` | Install a newer in-range version whenever one exists, rather than holding the locked one |
94
- | `--dry-run` | Print what would be installed without writing or downloading anything |
95
- | `--no-build` | Change the subscription without rebuilding the project |
88
+ | `--dry-run` | Print what would be installed, writing and downloading nothing |
89
+ | `--no-build` | Record the subscription without rebuilding the project |
90
+ | `--prerelease` | Let prerelease versions take part in version range matching |
91
+ | `--non-interactive` | Never ask; fail instead, naming the flag that would have answered |
96
92
 
97
- Subscribing changes what this project compiles, so the command finishes by building it: the same
98
- compile and prune `sous build` runs, which means the new recipe's skills, memories and prompts are
99
- already on disk when the command returns. `--no-build` records the subscription and leaves the
100
- outputs alone, for when you would rather build later. If the build itself fails, the subscription
101
- stays; it is already written and locked, and the message says so and names the command to run once
102
- you have fixed the cause.
93
+ ?> `sous subscribe` and `sous unsubscribe` are accepted spellings of these two commands, and every
94
+ topic answers to both spellings of its name (`sous subscriptions add`, `sous repos list`).
103
95
 
104
96
  ### One-word refs
105
97
 
106
- You do not have to remember which namespace a recipe lives in. A ref of one word is looked for
107
- as a namespace first, and as a recipe name second, across the cached index of every repository
108
- the project trusts:
98
+ A bare word is looked for as a repository, a namespace and a recipe name across every cached index,
99
+ so you need not remember which namespace a recipe lives in. A single match is announced
100
+ (`Resolved to: qa-recipes:quality/qa-pattern`); a word matching nothing is an error naming every
101
+ repository searched. When it means more than one thing, sous lists every candidate as a full ref
102
+ and asks which you meant. That order is the contract, because `--accept-first` takes the first one:
103
+ candidates sort by how qualified the matching spelling was, then by kind (repository, namespace,
104
+ recipe, variable, environment variable), then by repository in search order, then alphabetically.
109
105
 
110
- ```term
111
- $ sous subscription add task-files
112
- Resolved to: sous-recipes:workflow/task-files
113
- Recipe : task-files
114
- Namespace : workflow
115
- Repository : sous-recipes
116
- Description: keeps one task file per branch
117
-
118
- 'task-files' named one recipe, and nothing else, so that is what is being used.
119
- ```
106
+ ### The plan, and the confirmation
120
107
 
121
- When the word means more than one thing, including the case where it is a namespace in one
122
- repository and a recipe name in another, sous lists every candidate as a full ref and asks which
123
- one you meant:
108
+ Sous says what it will do and asks first; nothing is downloaded or written until you answer.
124
109
 
125
110
  ```term
126
- $ sous subscription add formatter
127
- ? Which 'formatter' did you mean?
128
- > my-recipes:formatter (the whole namespace 'formatter' in the repository 'my-recipes')
129
- sous-recipes:tooling/formatter (the recipe 'formatter' in the namespace 'tooling' of the
130
- repository 'sous-recipes': formats what a recipe writes)
131
- ```
132
-
133
- The order is stable and worth knowing, because `--accept-first` takes the first candidate without
134
- asking: repositories come first in the order your config names them, with the built-in
135
- `sous-recipes` ahead of them; then namespaces alphabetically; then, inside a namespace, the whole
136
- namespace ahead of the recipes in it, which are alphabetical. A word that matches nothing is an
137
- error naming every repository that was searched.
138
-
139
- ### The confirmation
140
-
141
- Subscribing changes your project, so sous says what it is about to do and asks before doing any
142
- of it. Nothing is downloaded and nothing is written until the question is answered:
143
-
144
- ```term
145
- $ sous subscription add workflow/task-files
146
-
147
- Subscribing to 'sous-recipes:workflow/task-files' installs the recipe 'task-files' from
148
- the namespace 'workflow'.
149
-
111
+ $ sous subscription add workflow/qa-variables
112
+ Subscribing to 'workflow/qa-variables' installs the recipe 'qa-variables' from the namespace
113
+ 'workflow'.
150
114
  Here is what that does:
151
115
 
152
116
  • The files it ships are compiled into this project on the next build, which writes them
153
117
  into this project's agent directories.
154
118
  • Any scripts it ships can be run on this machine when an agent uses them. Sous does not
155
119
  run them itself, and it cannot vouch for what they do.
156
- • The variables it publishes are asked about at the end of this command, and the answers
157
- are written into this project's env files.
158
- • Its dependencies are fetched and pinned in this project's lockfile, at the exact
159
- versions resolved now.
160
- • If a dependency turns out to live in a repository this project does not trust, sous
161
- stops and asks about that repository by name before fetching anything from it.
162
-
163
- ? Proceed? (y/N)
120
+ // two more bullets: the variables it publishes are asked about at the end of the command and
121
+ // written into this project's env files, and its dependencies are pinned in the lockfile
122
+ • If a dependency turns out to live in a repository this project does not trust, sous stops
123
+ and asks about that repository by name before fetching anything from it.
124
+ ? Proceed? (y/N) y
125
+ Recipe Version Repository Why
126
+ --------------------- ------- ---------- ------------------------------------------------------
127
+ workflow/qa-helper 0.1.0 qa-recipes needed by workflow/qa-variables
128
+ workflow/qa-variables 0.1.0 qa-recipes you subscribed to it
164
129
  ```
165
130
 
166
- Answering no ends the command with nothing downloaded, no lockfile entry and no change to your
167
- config. `--yes` accepts the plan without being asked, which is what a script or a Makefile wants;
168
- `-y`, `--force`, `-f` and `--trust` are spellings of that same flag.
169
- `--dry-run` states the plan and then reports what would be installed, asking nothing, because
170
- there is nothing to decline.
171
-
172
- Three files change: `.sous/conf.d/510-subscriptions.jsonc` records the subscription,
131
+ Answering no ends the command with nothing downloaded and no change to your config. Three things
132
+ change when you say yes: `.sous/conf.d/510-subscriptions.jsonc` records the subscription,
173
133
  `.sous/sous.lock.json` records the exact versions and hashes, and the machine-wide store under
174
- `~/.sous/cache` gains the recipe's files. All three, apart from the store, are committed.
175
-
176
- If the closure reaches a repository you have not added, sous stops and asks about it by name,
177
- showing which recipe requires it. Declining aborts the whole install:
178
-
179
- ```term
180
- $ sous subscription add workflow/needs-extras
181
- // resolution reaches a repository this project has not added
182
- One repository has to be trusted before this can continue.
183
-
184
- extras
185
- Location: https://github.com/some-team/extras
186
- Required: tooling/formatter required by 'workflow/needs-extras'
187
- ```
188
-
189
- ?> A subscription entry holds the range; the ref you type may carry one (`@^1.2.0`), and the
190
- range is what gets recorded. The subscription key itself is never qualified and never carries a
191
- range. See [Project configuration](repositories-file-formats.md#project-configuration).
192
-
193
- ## Build, and see what lands where
194
-
195
- ```bash
196
- sous build
197
- ```
198
-
199
- Nothing about a recipe's files is special once they are on disk: they compile exactly the way one
200
- of your own `entryGlob` targets does, and the [`.tpl.` convention](configuration.md) applies
201
- unchanged, so a `.tpl.md` file is rendered and loses `.tpl.` from its name while everything else
202
- is copied verbatim.
203
-
204
- Where each kind of content lands is your project's decision, under the `recipeOutputs` config
205
- key:
206
-
207
- ```js
208
- recipeOutputs: {
209
- skills: ["${projectRoot}/.claude/skills", "${projectRoot}/.codex/skills"],
210
- memories: ["${projectRoot}/.claude/memories"],
211
- prompts: ["${projectRoot}/prompts/recipes"],
212
- },
213
- ```
214
-
134
+ `~/.sous/cache` gains the files (the first two are committed; the store is not). It then builds, so
135
+ the new skills are on disk when it returns; `--no-build` defers that, and a failed build keeps it.
136
+
137
+ ?> Only a recipe a manifest lists under `subscribes` contributes files; one listed under `depends`,
138
+ like `workflow/qa-helper` above, is fetched and pinned but stays out of your output. See
139
+ [Dependencies](repositories.md#dependencies).
140
+
141
+ ## Choose where the files land
142
+
143
+ Recipe files compile the way one of your own `entryGlob` targets does, under the
144
+ [`.tpl.` convention](configuration.md#templates-and-the-tpl-convention): a file with `.tpl.` in its
145
+ name is rendered through LiquidJS and loses `.tpl.` on the way out, and anything else is copied
146
+ verbatim. Where each content kind lands is your project's decision, under the
147
+ [`recipeOutputs`](repositories-file-formats.md#recipeoutputs-where-the-files-land) config key, which takes a list of
148
+ directories for each of `skills`, `memories` and `prompts`; for example
149
+ `recipeOutputs: { skills: ["${projectRoot}/.claude/skills", "${projectRoot}/.codex/skills"] }`.
215
150
  Only `skills` has a default, `<project root>/.claude/skills`, because that is where every agent
216
- looks. Nothing else does. A content kind with no destination is skipped and the build says so
217
- once, naming the key:
218
-
219
- ```text
220
- Some subscribed recipes contribute memories and prompts files, and this project has
221
- nowhere to put them, so they were skipped.
222
- Name a destination directory for each kind under the 'recipeOutputs' key of your
223
- sous config, for example:
224
- recipeOutputs: { memories: ["${projectRoot}/memories"], prompts: ["${projectRoot}/prompts"] }
225
- ```
226
-
227
- Recipe outputs are tracked like every other file sous writes, so `sous prune` removes what an
228
- unsubscribed recipe used to write and `sous clear` removes all of it. Neither ever reaches into
229
- a linked checkout or the machine-wide store.
230
-
231
- ?> Only recipes held through `subscribes` contribute files. A recipe pulled in through `depends`
232
- is fetched, pinned, and addressable from the recipe that declared it, and its files never enter
233
- your output.
234
-
235
- ## Recipes that configure your project
236
-
237
- A recipe's `config` contents are not written anywhere. They are config layers, and they load
238
- **after your primary config and before your own `conf.d/` drop-ins**:
239
-
240
- ```text
241
- primary config -> recipe config layers -> your conf.d/ layers -> managed 5xx layers
242
- ```
243
-
244
- So a recipe can supply defaults and your project always wins over them. Recipe layers are JSON
245
- or YAML only; sous must be able to read everything a repository publishes without running any of
246
- it, so an executable layer from a recipe is refused with a warning rather than loaded. Ordering
247
- among recipe layers is by recipe key and then by path, which makes it the same on every machine.
248
-
249
- A recipe layer may set only the keys that configure the recipe itself: `_vars`, `_aliases`,
250
- `compilation`, `runtimeContext`, `recipeOutputs`, `store` and `varMappings`. Sous removes
251
- anything else before merging and prints a warning naming the recipe and the key it removed.
252
-
253
- !> Subscribing to a recipe is not a decision to let it decide what else you trust. A recipe
254
- cannot add a repository to `repos:`, subscribe you to anything, point a `tools:` entry at a
255
- program `sous launch` would run, map new environment variables in through `_env`, or rename your
256
- project. Those decisions stay yours, and stay in your own config.
257
-
258
- ## Answer the variables a recipe needs
259
-
260
- A recipe publishes variable **definitions**; you supply **answers**. Subscribing asks whatever
261
- is unanswered, reports whatever it inherited from an answer already in scope, and never re-asks
262
- something that already fits:
263
-
264
- ```text
265
- Variables
266
-
267
- Answers already in scope:
268
- apiUrl : https://api.example.com from the shared scope name SOUS_VAR_API_URL, from
269
- the .env file
151
+ looks; a kind with no destination is skipped and the build says so once, naming the key. Recipe
152
+ outputs are tracked like every other file sous writes, so `sous prune` removes what an unsubscribed
153
+ recipe used to write and `sous clear` removes all of it; neither reaches into a linked checkout.
270
154
 
271
- Answers stored:
272
- taskFileRoot: .sous/tasks SOUS_VAR_TASK_FILE_ROOT in .env
273
- ```
155
+ ## Answer the questions
274
156
 
275
- In continuous integration there is no terminal, so an unanswered variable fails the run rather
276
- than hanging on a prompt, and the failure names every environment variable that would satisfy it,
277
- most specific first:
157
+ A recipe publishes variable definitions; you supply answers. Subscribing asks whatever is
158
+ unanswered, reports whatever it inherited from an answer already in scope, and never re-asks
159
+ something that already fits. [Recipe variables](repositories-variables.md#answer-the-questions)
160
+ covers the question screen, the advanced view and the ladder; what follows is what a subscribe
161
+ needs.
278
162
 
279
- ```text
280
- One variable still needs an answer, and there is no terminal to ask on.
281
-
282
- Set one of the environment variables listed under each variable, or run
283
- 'sous vars ask' from a terminal.
284
-
285
- apiUrl (workflow/task-files): Where does the API live?
286
- SOUS_VAR_WORKFLOW_TASK_FILES_API_URL (recipe scope)
287
- SOUS_VAR_WORKFLOW_API_URL (namespace scope)
288
- SOUS_VAR_API_URL (shared scope)
289
- ```
163
+ ### Answering questions ahead of time
290
164
 
291
- Set any one of those names in the environment your pipeline runs in and the run proceeds.
292
- [Recipe variables](repositories-variables.md) covers the ladder, the two env files, mapping
293
- records and the `sous vars` commands in full.
294
-
295
- ## Answering questions ahead of time
296
-
297
- A script, a pipeline or a coding agent has no terminal and usually knows every answer already, so
298
- it supplies them with the subscription rather than being asked for them. It takes two commands:
299
- one to see the questions, one to answer them all.
300
-
301
- First, ask what the subscription wants to know. A dry run installs nothing and writes nothing; it
302
- prints the plan, and then every question the closure would ask, grouped by the recipe that
303
- publishes it:
165
+ List the questions with a dry run, which installs nothing, then supply them all in one command:
304
166
 
305
167
  ```bash
306
- sous subscription add workflow/task-files --dry-run --non-interactive
307
- ```
308
-
309
- ```text
310
- Questions these recipes ask
311
-
312
- These recipes ask 2 questions, 1 of which nothing answers yet.
313
-
314
- workflow/task-files asks 2 questions:
315
-
316
- taskFileRoot
317
- about : The directory holding one task file per git branch.
318
- example : .sous/tasks
319
- stored-as : SOUS_VAR_TASK_FILE_ROOT
320
- storage-path: /home/you/project/.sous/.env
321
- answered : no, and this recipe requires an answer
322
- answer-with : --answer taskFileRoot=<value>
323
-
324
- apiUrl
325
- about : The service every request this recipe generates is sent to.
326
- example : https://api.example.com
327
- stored-as : SOUS_VAR_API_URL
328
- storage-path: /home/you/project/.sous/.env
329
- answered : yes, from the shared scope name SOUS_VAR_API_URL, from the .env file
330
- answer-with : --answer apiUrl=<value>
168
+ sous subscription add workflow/qa-variables --dry-run --non-interactive
169
+ sous subscription add workflow/qa-variables --yes --answer qaAgentName=QA \
170
+ --answer qaReviewDepth=thorough
331
171
  ```
332
172
 
333
- Then do the whole thing in one command, with an answer for each question and `--yes` for the
334
- confirmation:
173
+ Every answer is checked before anything is installed, so a run stores all of them or none:
335
174
 
336
- ```bash
337
- sous subscription add workflow/task-files --yes \
338
- --answer taskFileRoot=.sous/tasks \
339
- --answer apiUrl=https://api.example.com
340
- ```
341
-
342
- The rules are deliberately strict, because nobody reads a supplied answer before it is stored:
343
-
344
- - Every answer is checked against its definition before anything is installed or written, so a run
345
- either stores all of them or none of them. A value that does not fit fails the run naming the
346
- constraint it violated and the publisher's example of a real answer.
347
- - A name no recipe declares fails the run and lists every variable that is in play, grouped by
348
- recipe, so a typo can never become a stored value under a name nothing reads.
349
- - An answer for a variable that already has one replaces it, where that answer lives, and the
350
- report says what it replaced.
351
- - Anything left unanswered is asked for as usual, or, with no terminal, fails naming the
352
- environment variables that would answer it.
353
-
354
- The name is spelled exactly as the recipe declares it, in camelCase; the full
355
- `namespace/recipe.name` key works too, which is what you use when two recipes publish the same
356
- name. Everything after the first `=` is the answer, so a value may contain as many more as it
357
- likes.
358
-
359
- Answers can also come from a file, which suits a longer list or a value with spaces in it. It is
360
- YAML or JSON (comments allowed), one entry per variable, and an `--answer` on the command line
361
- wins over the same name in the file:
362
-
363
- ```yaml
364
- # answers.yaml
365
- taskFileRoot: .sous/tasks
366
- apiUrl: https://api.example.com
367
- ```
368
-
369
- ```bash
370
- sous subscription add workflow/task-files --yes --answers-file ./answers.yaml
175
+ ```text
176
+ Error: The answer given for 'qaReviewDepth' does not fit the definition workflow/qa-variables
177
+ publishes.
178
+ qaReviewDepth must be one of: light, standard, thorough.
179
+ For example: thorough
180
+ The answer given with --answer <name>=<value> was: deep
181
+ Nothing was written; fix the answer and run the command again.
371
182
  ```
372
183
 
373
- ?> A dry run downloads nothing, so a recipe your machine does not hold yet has no manifest to
374
- read and its questions cannot be listed. The run still succeeds and names the recipes it could
375
- not describe; install them, or answer their questions when they are asked.
376
-
377
- ## Look at what you have
184
+ A name no recipe declares fails the run and lists every variable in play, so a typo cannot become a
185
+ stored value nothing reads. Names are camelCase, as the recipe declares them; the full
186
+ `namespace/recipe.name` key works too when two recipes publish the same name, and everything after
187
+ the first `=` is the answer. `--answers-file answers.yaml` reads a YAML or JSON file of them.
378
188
 
379
- ```bash
380
- sous repo list
381
- sous subscription list
382
- sous search task
383
- sous repo search browser --limit 50
384
- ```
189
+ ?> A dry run downloads nothing, so a recipe your machine does not hold yet has no manifest to read
190
+ and its questions cannot be listed; the run still succeeds and names them.
385
191
 
386
- All three read only what is already on disk, so all three work offline and none of them downloads
387
- anything. `repo list` shows each trusted repository with its location, provider, namespaces,
388
- recipe count, and whether it is currently linked to a working copy. `subscription list` shows
389
- every subscription the project declares, with the range it resolves within, the versions the
390
- lockfile pins for it, where it came from, and whether it is on. `repo search`, which is also the
391
- top-level `sous search`, matches text against recipe names, namespace names and descriptions
392
- across every cached index. A repository whose index has never been fetched is reported as such
393
- rather than silently left out; run `sous repo add` on it again to refresh the index.
394
-
395
- `sous lock show` prints the other half of the picture: every recipe version your lockfile pins, the
396
- repository it came from, and who holds it. When that file has drifted from your config, through a
397
- hand edit or a bad merge, `sous lock rebuild` recomputes it from the subscriptions you declare and
398
- drops whatever nothing holds any more; `--dry-run` shows the same summary and writes nothing.
399
-
400
- ## When sous cannot ask
401
-
402
- Every question in sous is gated by one rule. Sous treats a run as non-interactive, and so asks
403
- nothing at all, when any of these is true:
404
-
405
- - the `--non-interactive` flag is passed (every command that works on a project accepts it;
406
- the three that run inside a recipe repository do not, because they have no `.sous/` to find
407
- and take none of the project flags);
408
- - the `CI` environment variable is set to anything other than `0`, `false`, `no` or `off`, which
409
- is what every continuous integration runner does;
410
- - stdin or stdout is not a terminal, which is what piping or scripting a command looks like.
411
-
412
- A run like that fails rather than guessing, and the failure names the question that could not be
413
- asked along with the flag that would have answered it ahead of time: `--yes` for the subscribe
414
- confirmation and for the trust question (`-y`, `--force`, `-f` and `--trust` all mean the same
415
- thing), `--accept-first` for the choice between candidate refs, and the exact environment
416
- variables for a variable question. The command's own help is printed underneath the error, so
417
- every other flag is in front of you, and `sous help <command>` prints the same screen on demand:
192
+ ## See what you have
418
193
 
419
194
  ```term
420
- $ CI=true sous subscription add workflow/task-files
421
- Sous has to ask whether to go ahead with subscribing to
422
- 'sous-recipes:workflow/task-files', and it is not running where it can ask.
423
- Why: the 'CI' environment variable is set to 'true'.
424
- Answer it ahead of time: pass '--yes' (spelled '-y', '--force' or '--trust' if
425
- you prefer) to accept the plan above without being asked.
426
- ```
427
-
428
- ?> The error and the help both go to stderr, so piping a command's output somewhere
429
- (`sous config show | jq`) keeps working whether or not the run fails.
430
-
431
- ## Remove a subscription
432
-
433
- ```bash
434
- sous subscription remove workflow/task-files
435
- sous subscription remove workflow/task-files --dry-run
436
- sous subscription remove workflow/task-files --no-build
195
+ $ sous subscription list
196
+ Subscription Range Pinned version Origin Enabled
197
+ --------------------- ----------- ------------------------------------------- -------- -------
198
+ core 0.2.0 core/sous-skills 0.2.0 built in yes
199
+ workflow/qa-variables any version workflow/qa-variables 0.1.0 user yes
437
200
  ```
438
201
 
439
- Removal is refcounted. Every lockfile entry records who holds it, so unsubscribing removes what
440
- that subscription alone brought in and leaves anything another subscription or another recipe
441
- still needs, reporting what stayed and why:
202
+ A namespace subscription names every recipe it holds, each with the version the lockfile pins. A
203
+ subscription that has never been built has nothing pinned yet, and its cell reads `pinned on first
204
+ build` instead.
442
205
 
443
- ```text
444
- What stayed, and why
206
+ `sous repo list` shows each trusted repository with its provider, origin, whether it is linked, its
207
+ recipe count and its URL; `--verbose` adds a `Namespaces:` line under each row. `sous lock show`
208
+ prints the other half: every version your lockfile pins, where it came from and who holds it. When
209
+ that file has drifted, `sous lock rebuild` recomputes it from your subscriptions.
445
210
 
446
- Recipe Still held by
447
- tooling/formatter workflow/needs-extras
448
- ```
211
+ ## Remove a subscription
449
212
 
450
- The repositories those recipes came from stay trusted; withdrawing trust is a separate,
451
- deliberate act.
213
+ `sous subscription remove workflow/qa-variables` takes `--dry-run` and `--no-build` too, and it
214
+ removes only what that subscription alone brought in. Anything another subscription or recipe still
215
+ needs stays, under a `What stayed, and why` heading naming each recipe and what holds it. Like
216
+ adding one, it finishes by building, so the files it used to write are pruned before it returns.
452
217
 
453
- Like adding one, removing a subscription finishes by building the project, so the files it used to
454
- write are pruned before the command returns. `--no-build` leaves them where they are until the next
455
- `sous build`. A build that fails does not put the subscription back; it is already gone from the
456
- config and the lockfile, and the message says so.
218
+ ### Stop trusting a repository
457
219
 
458
- ## Stop trusting a repository
220
+ Removing a repository withdraws the trust that adding it granted, and everything held through it
221
+ goes too. Before writing anything, the command says exactly what that means here:
459
222
 
460
- ```bash
461
- sous repo remove my-recipes
462
- sous repo remove my-recipes --dry-run
463
- sous repo remove my-recipes --yes
223
+ ```term
224
+ $ sous repo remove qa-recipes --dry-run
225
+ The entry for 'qa-recipes' at https://github.com/example/qa-recipes is removed from this
226
+ project's repositories layer, so sous stops reading anything from it.
227
+ Here is what goes with it:
228
+ One subscription resolves into it and is removed: workflow/qa-variables.
229
+ 2 locked recipes are held only through those subscriptions, and they leave the lockfile:
230
+ workflow/qa-helper, workflow/qa-variables.
231
+ 2 files those recipes compiled are pruned by the build that follows:
232
+ .claude/skills/qa-variables/SKILL.md
233
+ .claude/skills/qa-variables/references/note-template.md
464
234
  ```
465
235
 
466
- Removing a repository withdraws the trust that adding it granted, and everything the project held
467
- through it goes at the same time. Before anything is written the command says exactly what that
468
- means here: the entry it takes out of the managed repositories layer, every subscription that
469
- resolves into the repository, every locked recipe those subscriptions alone held, the output files
470
- the next build prunes, and the checkout a link points at, if there is one. Then it asks once, and
471
- `--yes` (also `-y`, `--force` and `-f`) answers ahead of time for a run with no terminal.
236
+ Then it asks once, and `--yes` answers ahead for a run with no terminal. Each subscription is
237
+ checked the same way, so a recipe something else still needs stays and is reported with whoever
238
+ holds it. A link is removed but its checkout stays on disk, and a subscription written in your own
239
+ config file rather than the managed layer is named and left alone.
472
240
 
473
- Each subscription is removed through the same refcounted path `sous subscription remove` uses, so a
474
- recipe another subscription or another recipe still needs stays, and is reported with whoever is
475
- holding it. A link to the repository is removed with it; the checkout itself stays on disk, because
476
- it is a working copy sous did not necessarily put there. Like the subscription commands, this one
477
- finishes by building the project, so the files those recipes wrote are pruned before it returns;
478
- `--no-build` leaves them until the next `sous build`.
241
+ ## Opt out of `core`
479
242
 
480
- A subscription written in your own config file, rather than in the managed layer sous writes, is
481
- named and left alone: sous never edits a config file you wrote.
243
+ The `core` subscription is an ordinary config entry, explained under
244
+ [the built-in core](repositories.md#the-official-repository-and-the-built-in-core); writing
245
+ `subscriptions: { core: { enabled: false } }` into your config removes it.
482
246
 
483
- Removing the built-in `sous-recipes` repository records `sous-recipes: { enabled: false }` in the
484
- managed repositories layer instead of deleting an entry, for the same reason the `core` opt-out
485
- below is recorded rather than deleted: the entry sous provides comes back on the next run.
247
+ `sous subscription remove core` writes exactly that into the managed subscriptions layer, because
248
+ there is no entry to delete: the one sous provides comes back on the next run, so only a recorded
249
+ opt-out outlives it. `sous subscription add core` clears it again. If you remove `core`, make sure
250
+ something else tells your agents not to edit generated files.
486
251
 
487
252
  ## Restore a fresh clone
488
253
 
489
- A clone has the lockfile and the subscriptions, and no store. `sous build` restores exactly what
490
- the lockfile pins, with no prompts and no version drift, then compiles:
254
+ A clone has the lockfile and the subscriptions, and no store. `sous build` restores what the
255
+ lockfile pins, with no prompts and no version drift, then compiles:
491
256
 
492
257
  ```term
493
- $ git clone git@github.com:my-team/my-project.git
494
- >> 100%
495
258
  $ sous build
496
- Restoring recipes
497
- restored: workflow/task-files
498
- compiled 12 targets
259
+ ▶ Restoring recipes:
260
+ This project's lockfile pins recipes that are not in the store on this machine, so they are
261
+ being fetched at exactly the versions it records.
262
+ restored: workflow/qa-helper
263
+ restored: workflow/qa-variables
499
264
  ```
500
265
 
501
- If any variable the recipes need has no answer in the committed `.sous/.env` and none in the
502
- environment, that is where the build stops, with the message shown above.
266
+ A variable with no answer in the committed `.sous/.env` and none in the environment renders empty
267
+ rather than stopping the build; run `sous vars ask` from a terminal to fill it in.
503
268
 
504
- ## Collect the store
269
+ ## Control freshness and the store
505
270
 
506
- ```bash
507
- sous repo gc
508
- sous repo gc --dry-run
509
- sous repo gc --max-bytes 268435456
510
- ```
271
+ A build holds the versions the lockfile pins and does not talk to the network on every run. Sous
272
+ asks a repository for a newer index only when it has never asked, when the freshness window has
273
+ lapsed (`store.freshnessSeconds`, five minutes by default), or when a command forces it; a failed
274
+ check never breaks a build, because the cached index is used instead. Always-pull changes what
275
+ happens after that check, not how often it happens: a repository or subscription marked
276
+ `alwaysPull` takes a newer in-range version rather than the locked one; set it with
277
+ `--always-pull`, or on either entry in the config.
511
278
 
512
- The store is machine-wide and disposable: everything in it is re-fetchable from the pins in a
513
- lockfile. `repo gc` collects it back down to its size cap, evicting the least recently used
279
+ The store is machine-wide and disposable, because everything in it is re-fetchable from a
280
+ lockfile's pins. `sous repo gc` collects it back to its size cap, evicting least recently used
514
281
  entries first, and protects everything this project's lockfile pins whatever that does to the
515
- total. Entries other projects on the machine pin are re-fetchable too, so a pass may evict them;
516
- the next build that needs one downloads it again.
282
+ total. The cap is `store.maxBytes`, one gigabyte by default; `--max-bytes 268435456` (256
283
+ megabytes) overrides it for one run, and `--dry-run` reports what would go.
517
284
 
518
- The cap is `store.maxBytes` in your config, one gigabyte by default, and `--max-bytes` overrides
519
- it for one run.
285
+ ## Run sous in CI, or from an agent
520
286
 
521
- ## Opt out of `core`
522
-
523
- The `core` namespace is auto-subscribed in every project, at the version matching the sous CLI
524
- you are running, and seeded from inside the sous package so it works with no network. It carries
525
- the skills that teach an agent what sous manages and why generated files must not be hand-edited,
526
- which is why it arrives by default.
527
-
528
- It is still an ordinary config entry, and one line removes it:
529
-
530
- ```yaml
531
- subscriptions:
532
- core:
533
- enabled: false
534
- ```
535
-
536
- `sous subscription remove core` writes exactly that line into the managed subscriptions layer for
537
- you, because there is no entry to delete: the one sous provides comes back on the next run, so
538
- only a recorded opt-out outlives it. `sous subscription add core` clears the opt-out again.
539
- Either way the `sous-recipes` repository stays trusted and keeps appearing in `repo list` as
540
- built in.
287
+ ### When sous cannot ask
541
288
 
542
- Disabling the built-in `sous-recipes` repository entry the same way switches off the auto-
543
- subscription along with everything else that repository provides. Removing `core` means your
544
- agents lose those instructions; if you remove it, make sure something else tells them not to edit
545
- generated files.
289
+ Sous treats a run as non-interactive, and asks nothing at all, when any of these is true:
546
290
 
547
- ## Use a repository on this machine
291
+ - `--non-interactive` is passed (every command that works on a project accepts it);
292
+ - the `CI` environment variable is set to anything but an empty value, `0`, `false`, `no` or `off`
293
+ (case and surrounding spaces are ignored);
294
+ - stdin or stdout is not a terminal, which is what piping or scripting looks like.
548
295
 
549
- A repository does not have to be hosted. Give `sous repo add` a path, relative or absolute, or
550
- the same path in `file:///` form, and sous reads it through the built-in `local` provider:
296
+ Such a run fails rather than guessing, naming both the question and the flag that answers it:
551
297
 
552
- ```bash
553
- sous repo add /home/me/Projects/my-recipes --name my-recipes --trust
554
- sous repo add ../my-recipes --name my-recipes --trust
298
+ ```term
299
+ $ CI=true sous subscription add quality/qa-pattern
300
+ Error: Sous has to ask whether to go ahead with subscribing to 'quality/qa-pattern', and it is
301
+ not running where it can ask.
302
+ Why: the 'CI' environment variable is set to 'true'.
303
+ Answer it ahead of time: pass '--yes' (spelled '-y', '--force' or '--trust' if you prefer) to
304
+ accept the plan above without being asked.
555
305
  ```
556
306
 
557
- A relative path is resolved against the working directory before anything else happens, and the
558
- absolute form is what lands in the config; a repository on this machine is machine-specific
559
- either way.
307
+ An unanswered variable fails the same way, naming every environment variable that answers it:
560
308
 
561
- It is meant for local development and for tests: authoring a repository, trying a recipe before
562
- publishing it, or running a whole workflow with no network at all. The index is read from the
563
- working tree when the file is there, so an index you are still writing is picked up without a
564
- commit. A recipe's files come from the version's tag in the local git repository; a directory
565
- that is not a git repository has no versions to honor, so its working tree is copied instead.
566
-
567
- !> A local path is trusted through the same ceremony as a hosted repository. Its recipes still
568
- run on this machine, and "it is already on my disk" is not a reason to skip the question.
309
+ ```text
310
+ Error: One variable still needs an answer, and there is no terminal to ask on.
311
+ Set one of the environment variables listed under each variable, or run
312
+ 'sous vars ask' from a terminal.
313
+ qaAgentName (workflow/qa-variables): What name should agents sign their review notes with?
314
+ SOUS_VAR_WORKFLOW_QA_VARIABLES_QA_AGENT_NAME (recipe scope)
315
+ SOUS_VAR_WORKFLOW_QA_AGENT_NAME (namespace scope)
316
+ SOUS_VAR_QA_AGENT_NAME (shared scope)
317
+ ```
569
318
 
570
- For editing a repository you are already subscribed to, reach for
571
- [`sous repo link`](repositories-authoring.md#edit-a-repository-in-place) instead; it redirects
572
- one repository's resolution at a working copy without changing what your project subscribes to.
319
+ So a pipeline or an agent needs three things and nothing else: `--yes` for the confirmations,
320
+ `--accept-first` for an ambiguous one-word ref, and either `--answer` or those environment
321
+ variables. The command's help prints under the error, on stderr, and the error itself on stdout.
573
322
 
574
323
  ## Where to go next
575
324
 
576
- - [Recipe variables](repositories-variables.md): answers, the resolution ladder, `sous vars`
325
+ - [Troubleshooting](repositories-troubleshooting.md): what a failed add, subscribe or build is
326
+ telling you
327
+ - [Recipe variables](repositories-variables.md): answers, the ladder, `sous vars`
328
+ - [Providers](repositories-providers.md): how a URL is matched, and reading a path on this machine
577
329
  - [Authoring a repository](repositories-authoring.md): publishing recipes of your own
578
- - [Repository file formats](repositories-file-formats.md): every schema, including
579
- [`recipeOutputs`](repositories-file-formats.md#recipeoutputs-where-the-files-land)
330
+ - [Repository file formats](repositories-file-formats.md): every schema
580
331
  - [Command reference](commands.md): every command and flag