@sous-io/sous 0.2.1 → 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.
- package/docs/markdown/README.md +4 -0
- package/docs/markdown/_sidebar.md +4 -1
- package/docs/markdown/commands.md +293 -274
- package/docs/markdown/configuration.md +7 -0
- package/docs/markdown/repositories-authoring.md +210 -302
- package/docs/markdown/repositories-consuming.md +219 -468
- package/docs/markdown/repositories-file-formats.md +216 -980
- package/docs/markdown/repositories-providers.md +322 -0
- package/docs/markdown/repositories-quickstart.md +340 -0
- package/docs/markdown/repositories-troubleshooting.md +336 -0
- package/docs/markdown/repositories-variables.md +229 -296
- package/docs/markdown/repositories.md +262 -228
- package/package.json +1 -1
- package/recipes/core/sous-skills/sous.recipe.yaml +1 -1
|
@@ -1,580 +1,331 @@
|
|
|
1
1
|
# Consuming Recipes
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
##
|
|
44
|
+
## Find a recipe
|
|
40
45
|
|
|
41
|
-
|
|
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
|
|
46
|
-
sous namespace
|
|
47
|
-
sous
|
|
48
|
-
sous recipe
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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/
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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/
|
|
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
|
-
|
|
81
|
-
|
|
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
|
|
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
|
-
| `--
|
|
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
|
|
95
|
-
| `--no-build` |
|
|
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
|
-
|
|
98
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
•
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
|
167
|
-
|
|
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
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
|
217
|
-
|
|
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
|
-
|
|
272
|
-
taskFileRoot: .sous/tasks SOUS_VAR_TASK_FILE_ROOT in .env
|
|
273
|
-
```
|
|
155
|
+
## Answer the questions
|
|
274
156
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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/
|
|
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
|
-
|
|
334
|
-
confirmation:
|
|
173
|
+
Every answer is checked before anything is installed, so a run stores all of them or none:
|
|
335
174
|
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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
|
-
|
|
380
|
-
|
|
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
|
-
|
|
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
|
-
$
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
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
|
-
|
|
440
|
-
that
|
|
441
|
-
|
|
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
|
-
|
|
444
|
-
|
|
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
|
-
|
|
447
|
-
tooling/formatter workflow/needs-extras
|
|
448
|
-
```
|
|
211
|
+
## Remove a subscription
|
|
449
212
|
|
|
450
|
-
|
|
451
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
```
|
|
461
|
-
sous repo remove
|
|
462
|
-
|
|
463
|
-
sous
|
|
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
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
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
|
-
|
|
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
|
-
|
|
481
|
-
|
|
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
|
-
|
|
484
|
-
|
|
485
|
-
|
|
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
|
|
490
|
-
|
|
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
|
-
|
|
498
|
-
|
|
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
|
-
|
|
502
|
-
|
|
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
|
-
##
|
|
269
|
+
## Control freshness and the store
|
|
505
270
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
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
|
|
513
|
-
lockfile. `repo gc` collects it back
|
|
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.
|
|
516
|
-
|
|
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
|
-
|
|
519
|
-
it for one run.
|
|
285
|
+
## Run sous in CI, or from an agent
|
|
520
286
|
|
|
521
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
```
|
|
553
|
-
sous
|
|
554
|
-
|
|
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
|
-
|
|
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
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
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
|
-
|
|
571
|
-
|
|
572
|
-
|
|
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
|
-
- [
|
|
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
|
|
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
|