@sous-io/sous 0.2.1 → 0.2.3

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,205 +1,286 @@
1
1
  # Repositories
2
2
 
3
3
  A **repository** publishes shared agent configuration that any project can subscribe to. It is
4
- an ordinary git repository holding a few manifest files and some markdown, and sous reads it
5
- the way a package manager reads a registry: an index says what exists, a project says what it
6
- wants, and a lockfile records exactly what it got.
4
+ an ordinary git repository holding a few manifest files and some markdown, and sous reads it the
5
+ way a package manager reads a registry: an index says what exists, a project says what it wants,
6
+ and a lockfile records exactly what it got.
7
7
 
8
- This page explains the model and the guarantees. The task-oriented guides are
9
- [Consuming recipes](repositories-consuming.md) and
10
- [Authoring a repository](repositories-authoring.md), and every file schema lives in
11
- [Repository file formats](repositories-file-formats.md).
8
+ This page is the map: the vocabulary the system is built from, the official repository every
9
+ project starts with, how a single build fits them together, and where every file lands on disk.
10
+ The task-oriented guides are listed at the bottom.
12
11
 
13
- ## Repositories, namespaces, recipes
12
+ ## The vocabulary
14
13
 
15
- Three nouns carry the whole system.
14
+ ### Repositories
16
15
 
17
- - A **repository** is the unit of trust and the unit of distribution. You add one to a project,
18
- which is also how you trust it, and everything else flows from that.
19
- - A **namespace** groups related recipes inside a repository. It is a plain name, it is not
20
- versioned, and a project can subscribe to a whole namespace at once.
21
- - A **recipe** is the unit you subscribe to and the unit that carries a version. It may hold
22
- skills, memories, prompts, config layers, variable definitions, or any mixture of them.
23
- Subscribing to a recipe gets everything in it.
16
+ A repository is the unit of distribution and the unit of trust; you add one to a project, and
17
+ everything else flows from that. Its root holds `sous.repo.yaml`, which names the repository,
18
+ describes its namespaces and lists where its recipes live.
24
19
 
25
- ```text
26
- repository github.com/sous-io/sous-recipes
27
- namespace workflow
28
- recipe task-files 1.2.0
29
- recipe github-projects 1.0.0
30
- namespace communication
31
- recipe control-flow 1.0.0
20
+ ```yaml
21
+ # sous.repo.yaml
22
+ formatVersion: 1
23
+ name: agent-recipes
24
+ namespaces:
25
+ workflow:
26
+ description: Recipes about how work moves through a project.
27
+ recipes:
28
+ - recipes/workflow/task-files
29
+ ```
30
+
31
+ ### Namespaces
32
+
33
+ A namespace groups related recipes inside one repository. It is a plain name, it is never
34
+ versioned, and a project can subscribe to a whole namespace in one command.
35
+
36
+ ```term
37
+ $ sous namespace list
38
+ ▶ Namespaces in the repositories this project trusts:
39
+
40
+ Namespace Repository Recipes Subscribed What it is
41
+ ---------- ---------- ------- ---------- ------------------------------------------
42
+ quality qa 2 no Recipes that exercise the edges.
43
+ workflow qa 2 no Recipes about how work moves.
44
+ ```
45
+
46
+ ### Recipes
47
+
48
+ A recipe is the unit you subscribe to and the only thing that carries a version. It may hold
49
+ skills, memories, prompts, config layers and variable definitions in any mixture, and
50
+ subscribing to it gets all of them.
51
+
52
+ ```yaml
53
+ # recipes/workflow/task-files/sous.recipe.yaml
54
+ formatVersion: 1
55
+ namespace: workflow
56
+ name: task-files
57
+ version: 1.0.2
58
+ contents:
59
+ - kind: skills
60
+ include:
61
+ - skills/**/*.md
32
62
  ```
33
63
 
34
64
  A recipe is named by a **ref**: `workflow` for a whole namespace, `workflow/task-files` for one
35
65
  recipe, `workflow/task-files@^1.2.0` to constrain the version, and
36
- `sous-recipes:workflow/task-files` when two added repositories publish the same ref and sous
37
- needs to be told which one you meant. Refs resolve across the cached indexes of every repository
38
- the project has added; a genuine conflict is an error asking for the qualified form, never a
39
- silent first match. The full grammar is in
40
- [Refs: how anything is named](repositories-file-formats.md#refs-how-anything-is-named).
41
-
42
- A ref of one word is a guess at a name, and sous works out what it meant: it looks for a
43
- namespace with that name first, and for a recipe with that name second, across every repository
44
- the project trusts. One match is used and reported by its full ref; several are a question. See
45
- [Subscribe to a recipe](repositories-consuming.md#subscribe-to-a-recipe).
46
-
47
- ## Trust
48
-
49
- Adding a repository **is** trusting it. There is no separate trust command, no trusted-but-not-
50
- added state, and no way to look inside a repository before deciding: until a repository is added,
51
- sous downloads nothing from it, not even its index. That is deliberate. A trust decision made
52
- by browsing content sous fetched from an untrusted source is not really a trust decision; the
53
- decision rests on the URL and on who publishes it, and both of those are things you inspect
54
- outside sous. Trusting a repository trusts every namespace and every recipe in it, including
55
- recipes published later. Trusting alone executes nothing, but subscribing to something inside a
56
- trusted repository can and probably will run scripts on your machine, so the trust question is
57
- the last gate before that happens. Sous cannot tell you whether a repository deserves trust,
58
- and it says so rather than implying otherwise.
59
-
60
- `sous repo add` asks that question inline, with that wording. Resolution can also turn up a
61
- repository a recipe depends on that your project has not added; those are asked about in one
62
- consolidated question per round, each shown with its URL and the recipe that requires it. Any
63
- refusal aborts the whole install, because sous installs a dependency closure whole or not at all.
66
+ `sous-recipes:workflow/task-files` when two trusted repositories publish the same ref and sous
67
+ needs to be told which one you meant. A one-word ref is a guess at a name, and sous works it out:
68
+ a namespace first, a recipe second, across every repository the project trusts. The full grammar
69
+ is in [Refs: how anything is named](repositories-file-formats.md#refs-how-anything-is-named).
70
+
71
+ ### Subscriptions
72
+
73
+ A subscription is your project saying it wants a recipe or a namespace. It is recorded in a
74
+ config layer sous writes, `.sous/conf.d/510-subscriptions.jsonc`, so it is committed and travels
75
+ with the project.
76
+
77
+ ```jsonc
78
+ {
79
+ "subscriptions": {
80
+ "workflow/task-files": {
81
+ "addedAt": "2026-09-12T07:01:30.123Z",
82
+ "addedBy": "user"
83
+ }
84
+ }
85
+ }
86
+ ```
87
+
88
+ ### Dependencies
89
+
90
+ A recipe manifest can declare two relationships to other recipes, and they differ in exactly one
91
+ respect: whose files end up in your project. `depends` fetches, pins and trust-gates the target
92
+ and makes it addressable while rendering, but keeps its files out of your output; `subscribes`
93
+ does all of that and lands the target's files in your project too, along with the variable
94
+ questions it publishes (see [Recipe variables](repositories-variables.md)).
95
+
96
+ ```yaml
97
+ depends:
98
+ - workflow/qa-helper # a sibling in this repository
99
+ - github://sous-io/sous-recipes/workflow/task-files@^1.0 # one in another repository
100
+ subscribes:
101
+ - quality/code-review
102
+ ```
103
+
104
+ Both name their targets by location, written as a **locator URL**
105
+ ([the grammar](repositories-file-formats.md#dependencies-named-by-location)) when the target lives
106
+ in another repository, never by a short name a consuming project chose; a curated bundle is simply
107
+ a recipe made mostly of `subscribes` entries. Because both lists are
108
+ declarative YAML rather than code, sous can read the whole dependency closure before fetching any
109
+ of it, which is what makes the trust decision answerable up front.
110
+
111
+ ### The lockfile
112
+
113
+ `.sous/sous.lock.json` records the exact version, content hash and holder of everything the
114
+ project uses. It is committed, and a fresh clone rebuilds precisely what it describes.
115
+
116
+ ```json
117
+ {
118
+ "formatVersion": 1,
119
+ "recipes": {
120
+ "workflow/qa-helper": {
121
+ "hash": "sha256-78660ab9889e707a38befd193ad565f7d76fbf017e049f1ae87372bb2c69698d",
122
+ "kind": "subscribes",
123
+ "repo": "qa",
124
+ "requestedBy": ["project"],
125
+ "version": "0.1.0"
126
+ }
127
+ },
128
+ "repos": {
129
+ "qa": { "identity": "localhost/home/me/projects/qa", "url": "/home/me/Projects/qa" }
130
+ }
131
+ }
132
+ ```
133
+
134
+ Restore decides nothing: it never resolves a range, never picks a newer version and never
135
+ prompts. Anything that would change what is installed changes the lockfile first, as a diff you
136
+ can read in review.
137
+
138
+ ### The store
139
+
140
+ The store is one machine-wide cache of fetched recipes, at `~/.sous/cache`, keyed by the
141
+ repository's identity rather than by the short name any one project gave it. It is disposable:
142
+ every entry is re-fetchable from the pins in some project's lockfile, so deleting it costs a
143
+ download and nothing else.
144
+
145
+ ```text
146
+ ~/.sous/cache/github.com/sous-io/sous-recipes/core/sous-skills/0.2.0/
147
+ ~/.sous/cache/_indexes/github.com/sous-io/sous-recipes.json
148
+ ```
149
+
150
+ `sous repo gc` collects the store back to a size cap, least recently used first, never evicting
151
+ an entry the lockfile of the project you run it in still pins; entries another project pins may
152
+ go, because they are re-fetchable from that project's lockfile.
153
+
154
+ ### Trust
155
+
156
+ Adding a repository **is** trusting it. There is no separate trust command and no
157
+ trusted-but-not-added state: until a repository is added, sous downloads nothing from it, not
158
+ even its index.
64
159
 
65
160
  ```term
66
- $ sous repo add https://github.com/sous-io/sous-recipes
161
+ $ sous repo add https://github.com/my-team/agent-recipes
67
162
  // sous prints the repository, its location and what trusting it means
68
163
  Do you trust this repository? (y/N)
69
164
  ```
70
165
 
71
- Where there is no terminal to ask on, such as continuous integration, the run fails and names
72
- both the repositories and the exact command that grants the trust. `--trust` acknowledges
73
- without being asked, and is the flag a script uses; it is one spelling of the shared
74
- confirmation flag, alongside `-y`, `--yes`, `-f` and `--force`:
166
+ Trusting a repository trusts every namespace and recipe in it, including recipes published later,
167
+ and it is the last gate before a recipe can run scripts on your machine; sous cannot tell you
168
+ whether one deserves that, and says so rather than implying otherwise. Where there is no terminal
169
+ to ask on, the run fails and names the repositories and the exact command that grants the trust:
75
170
 
76
- ```bash
77
- sous repo add https://github.com/sous-io/sous-recipes --trust
78
- ```
171
+ ```text
172
+ Error: One repository has to be trusted before this can continue, and sous is not
173
+ running where it can ask.
174
+ Why: the '--non-interactive' flag was passed.
175
+
176
+ agent-recipes: https://github.com/my-team/agent-recipes
177
+ agent-recipes (required by project)
178
+
179
+ Trusting a repository trusts every namespace and recipe in it, and
180
+ subscribing to something inside it can run scripts on this machine.
181
+ Add each repository deliberately, with its URL:
79
182
 
80
- Trust is project-level and lives in your project's config, so a colleague who clones the project
81
- inherits it along with everything else. Trust plus the lockfile is the supply-chain defense:
82
- nothing new enters a project except through an explicit, visible change to files under version
83
- control.
183
+ sous repo add https://github.com/my-team/agent-recipes --name agent-recipes --trust
184
+ ```
84
185
 
85
- !> Trust semantics do not soften for a repository that is already on your disk. A local path
86
- added through the `local` provider goes through the same ceremony, because its recipes still run
87
- on this machine.
186
+ !> Trust semantics do not soften for a repository already on your disk. A local path added
187
+ through the `local` provider goes through the same ceremony, because its recipes still run on
188
+ this machine.
88
189
 
89
- ## The official repository, and `core`
190
+ ## The official repository, and the built-in core
90
191
 
91
192
  Sous publishes one official repository, [`sous-io/sous-recipes`](https://github.com/sous-io/sous-recipes).
92
193
  Its namespaces are drawn from the canonical [skill categories](skill-categories.md), plus one
93
194
  extra namespace called `core`.
94
195
 
95
- `core` is the exception to everything else on this page. It holds the skills that teach an agent
96
- what sous is, why generated files must not be edited by hand, and where the source of a managed
97
- file lives; without them an agent will cheerfully edit a compiled `CLAUDE.md` and wonder why the
98
- change keeps disappearing. So `core` is auto-subscribed in every project, at the version that
99
- matches the sous CLI you are running, and its source ships inside the sous package itself and
100
- seeds the machine-wide store on first run. A fresh install therefore works with no network at
101
- all, and the release pipeline pushes the same content to the official repository under the same
102
- version number, so the built-in copy and the published copy are the same bytes.
196
+ `core` is the exception. It holds the skills that teach an agent what sous is, why generated files
197
+ must not be hand-edited and where the source of a managed file lives; without them an agent will
198
+ cheerfully edit a compiled `CLAUDE.md`. So the official repository is added and `core` is
199
+ subscribed in every project, pinned to the sous CLI version you are running, and its source ships
200
+ inside the package and seeds the store on first run, so a fresh install needs no network at all.
103
201
 
104
- Both wirings are ordinary config entries, and both can be switched off:
202
+ ```term
203
+ $ sous subscription list
204
+ ▶ Subscriptions:
105
205
 
106
- ```yaml
107
- subscriptions:
108
- core:
109
- enabled: false
206
+ Subscription Range Pinned version Origin Enabled
207
+ ------------------ ----------- ------------------------ -------- -------
208
+ core 0.2.0 core/sous-skills 0.2.0 built in yes
209
+ workflow/qa-helper any version workflow/qa-helper 0.1.0 user yes
110
210
  ```
111
211
 
112
- Everything else in the official repository is opt-in, one `sous subscription add` at a time, and
113
- `sous subscription remove core` records that opt-out for you.
114
-
115
- ?> Namespace subscriptions are a first-class feature and are worth reaching for on other
116
- repositories, especially a team repository whose namespace is genuinely one coherent set. In the
117
- official repository they are not what you want: any arrangement of its content yields either
118
- one-recipe namespaces or a namespace of unrelated recipes, so subscribe to official recipes one
119
- at a time. `core` is the deliberate exception.
212
+ Both wirings are ordinary config entries a person could have written by hand, and either can be
213
+ switched off: `sous subscription remove core` writes `subscriptions.core.enabled: false` for you,
214
+ and `sous subscription add core` clears it again. See
215
+ [Opt out of `core`](repositories-consuming.md#opt-out-of-core) for what you give up.
120
216
 
121
- ## `depends` versus `subscribes`
217
+ ?> Subscribe to a whole namespace when it is one coherent set your team owns end to end. In the
218
+ official repository, prefer subscribing to recipes one at a time, so a recipe published later
219
+ does not arrive in your project unasked. `core` is the deliberate exception.
122
220
 
123
- A recipe manifest can declare two different relationships to other recipes, and the difference
124
- is exactly one thing: whose files end up in your project.
221
+ ## One build, end to end
125
222
 
126
- | Relationship | Fetched and pinned | Trust-gated | Addressable from the declaring recipe | Files enter your project |
127
- |--------------|--------------------|-------------|---------------------------------------|--------------------------|
128
- | `depends` | yes | yes | yes | no |
129
- | `subscribes` | yes | yes | yes | yes |
223
+ Four steps take a team repository from nothing to compiled skills.
130
224
 
131
- `depends` is a build dependency: shared partials, shared variable definitions, anything a recipe
132
- reads while rendering its own files. `subscribes` is a co-subscription: subscribing to the recipe
133
- subscribes your project to the listed targets with full semantics, so their questions run and
134
- their files land in your output. A curated bundle is simply a recipe made mostly of `subscribes`
135
- entries; there is no special bundle type.
225
+ **Add the repository**, which asks you to trust it and fetches its index, nothing more:
136
226
 
137
- Both name their targets by LOCATION. A recipe in the same repository is a bare ref
138
- (`workflow/sat`); a recipe in another repository is a locator URL whose scheme is the provider
139
- (`github://sous-io/sous-recipes/workflow/sat@^1.1`), whose last two path segments are always the
140
- namespace and the recipe. A project's own short name for a repository never appears in a
141
- published manifest, because it is a label that project chose. The full grammar is in
142
- [Repository file formats](repositories-file-formats.md#dependencies-named-by-location).
143
-
144
- A release records what each version was published against, so installing a version installs the
145
- versions it was released with rather than whatever its ranges reach today.
227
+ ```term
228
+ $ sous repo add https://github.com/my-team/agent-recipes
229
+ ▶ Adding a repository:
230
+ Repository: agent-recipes
231
+ Location : https://github.com/my-team/agent-recipes
232
+ Provider : github
233
+ Namespaces: quality, workflow
234
+ Recipes : 4
235
+ This project now trusts 'agent-recipes'. Nothing from it has been installed.
236
+ ```
146
237
 
147
- Both are declarative, and that is load-bearing rather than stylistic. Because the entire
148
- dependency closure is readable from manifests alone, sous can show you every repository an
149
- install would reach before it fetches any of them. Configuration that could subscribe by running
150
- code would break that, which is why manifests are YAML or JSON and never JavaScript.
238
+ **Subscribe to a recipe.** Sous resolves the dependency closure, shows what it will install,
239
+ asks about any repository a dependency needs that you have not trusted, records the subscription
240
+ and the pins, then builds:
151
241
 
152
- Removal is refcounted. Unsubscribing removes what that subscription alone brought in and leaves
153
- anything another subscription or another recipe still holds, and says which of those holders
154
- kept it.
242
+ ```term
243
+ $ sous subscription add workflow/task-files
244
+ ➔ What was installed:
245
+ Recipe Version Repository Why
246
+ ------------------- ------- ------------- ----------------------
247
+ workflow/task-files 1.0.2 agent-recipes you subscribed to it
248
+ ➔ Lockfile:
249
+ Adding workflow/task-files version 1.0.2
250
+ ```
155
251
 
156
- ## The lockfile
252
+ **Answer the questions** the recipe publishes. A question is asked only when a subscribed recipe
253
+ needs a variable and no valid answer is already in scope, and the answers land in this project's
254
+ env files. See [Recipe variables](repositories-variables.md).
157
255
 
158
- `.sous/sous.lock.json` records the exact version and content hash of everything the project uses,
159
- along with who holds each entry. It is committed. A fresh clone with no store on the machine
160
- rebuilds precisely what the lockfile describes, fetching those versions and no others, and asking
161
- nothing:
256
+ **Build.** Every later build reads the pins, restores anything missing from the store, then
257
+ compiles the recipe's files into your project's agent directories alongside your own templates:
162
258
 
163
259
  ```term
164
- $ git clone git@github.com:my-team/my-project.git
165
- >> 100%
166
260
  $ sous build
167
- Restoring recipes
261
+ ▶ Restoring recipes:
168
262
  This project's lockfile pins recipes that are not in the store on this machine,
169
- so they are being fetched at exactly the versions it records.
170
- restored: workflow/task-files
263
+ so they are being fetched at exactly the versions it records.
264
+ restored: workflow/task-files
265
+ ▶ Building:
266
+ ▷ SKILL.tpl.md:
267
+ Entry Point: ~/.sous/cache/github.com/my-team/agent-recipes/workflow/task-files/1.0.2/skills/start-task/SKILL.tpl.md
268
+ ✓ /home/me/my-project/.claude/skills/start-task/SKILL.md (~1,996 tokens)
171
269
  ```
172
270
 
173
- Restore decides nothing. It never resolves a range, never picks a newer version, and never
174
- prompts. Anything that would change what is installed changes the lockfile first, as a diff you
175
- can read in review.
176
-
177
- ## Providers
271
+ Skills default to `<project root>/.claude/skills`; every other content kind needs a destination in
272
+ the [`recipeOutputs`](repositories-file-formats.md#recipeoutputs-where-the-files-land) block.
178
273
 
179
- A provider is everything sous knows about one kind of repository host, and it is the only place
180
- a host-specific fact is allowed to live. Sous ships three: `github`, `gitlab` and `local`.
181
-
182
- A provider has two sides:
183
-
184
- - **The read side**, which every provider answers: recognize a repository URL, take it apart into
185
- host, owner and name, hand back the repository's `sous.index.json`, and fetch one recipe's
186
- subtree at one tag. Nothing here clones a whole repository.
187
- - **The write side**, which only a provider that can propose a change answers: report whether its
188
- command line tool is installed and signed in, say whether you can push to the repository
189
- itself, fork it onto your account, and open the proposal. Each call answers with plain data, so
190
- the command driving it never learns what tool ran.
191
-
192
- Each provider declares the features it really has, `fetch` and `submit`, and sous consults that
193
- list rather than a provider's name. `local` declares `fetch` only: a repository on your own disk
194
- is edited directly, so asking sous to propose a change to it is refused with a message naming the
195
- provider and the feature. A provider that supports a feature only partly says so plainly rather
196
- than guessing; GitLab, for instance, reports that it cannot tell whether you may push instead of
197
- sending you down a fork path it cannot finish.
198
-
199
- ?> Adding a provider is one file. A class extending `ProviderBase` inherits the subprocess, token
200
- and refusal plumbing, implements the read path, declares its features, and overrides the write
201
- calls it supports; adding it to the built-in list is the only other change. The interface is
202
- internal for now, not a published plugin API.
274
+ By default a build uses what the lockfile pins and does not talk to the network. Two things
275
+ change that: **always-pull**, which installs a newer in-range version whenever one exists, and
276
+ the **freshness window** (`store.freshnessSeconds`, five minutes by default), which decides how
277
+ often sous looks upstream at all. A failed check never breaks a build; the last good index stands
278
+ and the build says so. See
279
+ [Control freshness and the store](repositories-consuming.md#control-freshness-and-the-store).
280
+ A **linked** repository sits outside all of it: `sous repo link` points one repository's
281
+ resolution at a working copy on your machine, bypassing versions, the lockfile and freshness
282
+ checks, so every build announces it loudly. See
283
+ [Edit a repository in place](repositories-authoring.md#edit-a-repository-in-place).
203
284
 
204
285
  ## Where everything lives
205
286
 
@@ -216,88 +297,41 @@ internal for now, not a published plugin API.
216
297
  | `~/.sous/sous.links.json` | the machine-wide links map | not in a project at all |
217
298
 
218
299
  The user-level directory is `~/.sous`, and `SOUS_HOME` moves it. Unlike `SOUS_CONFIG` and
219
- `SOUS_DIR`, `SOUS_HOME` does not decide which project is active, so it may be set in
220
- `.sous/.env.local` or `.sous/.env` as well as in the shell.
221
-
222
- The three files in the `500` to `599` band are written by sous, and the band exists precisely so
223
- that machine-written layers never collide with the config you wrote. Sous edits them by key, so
224
- your comments, your key order and your formatting survive a write, and you may edit them
225
- yourself. Sous never edits your primary config. You may
226
- hand-write `repos:`, `subscriptions:` and `varMappings:` there yourself, and by the time anything
227
- reads them the two are one merged map. See
228
- [Managed config layers](repositories-file-formats.md#managed-config-layers).
300
+ `SOUS_DIR`, it does not decide which project is active, so it may be set in `.sous/.env.local` or
301
+ `.sous/.env` as well as in the shell.
229
302
 
230
- Every directory sous creates for its own bookkeeping explains itself. The first time sous
231
- creates one (`.sous/conf.d/`, `.sous/repos/`, `~/.sous` and everything under it), it writes a
232
- short `README.md` there saying what the directory is, who writes to it, whether you may edit or
233
- delete what is inside, and whether it is committed, plus an `AGENTS.md` and a `CLAUDE.md` holding
234
- one line each pointing at that README. None of the three is ever overwritten, so anything you
235
- write in them stays. Directories that hold rendered output are deliberately left alone: what
236
- lands there is yours.
303
+ The three files in the `500` to `599` band are written by sous, by key, so your comments and
304
+ formatting survive a write; you may edit them, and you may hand-write `repos:`, `subscriptions:`
305
+ and `varMappings:` in your primary config instead, which sous never touches. See
306
+ [Managed config layers](repositories-file-formats.md#managed-config-layers).
237
307
 
238
- The store is disposable by design. Every entry in it is re-fetchable from the pins in some
239
- project's lockfile, so deleting `~/.sous/cache` costs a download and nothing else. `sous repo gc`
240
- collects it back to a size cap, least recently used first, and never evicts an entry this
241
- project's lockfile still pins.
308
+ Every directory sous creates for its own bookkeeping explains itself: the first time it creates
309
+ one it writes a short `README.md` saying what the directory is, who writes to it, whether you may
310
+ edit it and whether it is committed, plus an `AGENTS.md` and a `CLAUDE.md` pointing at that
311
+ README. None is ever overwritten, and directories holding rendered output are left alone.
242
312
 
243
313
  ## Including recipe files in your own templates
244
314
 
245
- A recipe's files are addressable from a template through the reserved `~` include sigil:
246
-
247
- ```markdown
248
- @~workflow/task-files/_partials/shared.md
249
- ```
250
-
251
- The `~` is required. A bare `@path` in an include is always a relative path or a declared alias,
252
- with no namespace fallback, so an include line can never quietly stop meaning a file on disk and
253
- start meaning a recipe. Inside a recipe's own files, `~<namespace>` resolves against that
254
- recipe's declared dependencies at their pinned versions; in your project's templates it resolves
255
- against your project's subscriptions.
256
-
257
- A `~namespace` reference addresses a recipe's own files and nothing else, so the path after the
258
- recipe name may not contain `.` or `..` segments and may not be absolute. One that tries to leave
259
- the recipe directory is refused with an error saying so, exactly as every other path sous reads
260
- refuses `..`.
261
-
262
- ## Freshness, always-pull, and links
263
-
264
- By default a build uses what the lockfile pins and does not talk to the network. Two things
265
- change that.
266
-
267
- **Always-pull** installs a newer in-range version whenever one exists, rather than holding the
268
- locked one. It is set per repository or per subscription, or asked for once with
269
- `sous subscription add --always-pull`. It never widens the range a subscription or a dependency
270
- declared; it re-resolves within it. The lockfile is still regenerated every time, so it always
271
- records what the last build actually used, and a project using always-pull simply accepts
272
- routine lockfile diffs as the record of what changed.
273
-
274
- **The freshness window** decides how often sous bothers to look upstream at all: five minutes by
275
- default, configurable as `store.freshnessSeconds`, with `store.watchPollSeconds` doing the same
276
- job for watch mode. A check that fails never breaks a build. The last good index stands, the
277
- build says what happened, and it carries on.
278
-
279
- A **linked** repository sits outside all of this. `sous repo link` points one repository's
280
- resolution at a working copy on your machine, which is how a maintainer edits recipes; edits
281
- happen in a checkout, never in the store. A link bypasses versions, the lockfile and freshness
282
- checks, and those bypasses belong to one person's machine rather than to the team, so every
283
- build announces a linked repository loudly:
315
+ A template may pull in a file from a recipe through the reserved `~` sigil, naming the recipe's
316
+ published identity and then the path inside it, on a line of its own:
284
317
 
285
318
  ```text
286
- One repository is LINKED to a working copy on this machine.
287
- Their recipes are read from those checkouts, so versions, the lockfile and
288
- freshness checks do not apply to them.
319
+ @~workflow/qa-helper/_partials/review-steps.md
289
320
  ```
290
321
 
322
+ The `~` is required. A bare `@path` is always a relative path or a declared alias, so an include
323
+ line can never quietly stop meaning a file on disk. Inside a recipe, `~<namespace>` resolves only
324
+ against that recipe's own `depends` and `subscribes` at the versions the lockfile pins, and the
325
+ path after the recipe name may not be absolute or hold a `.` or `..` segment.
326
+
291
327
  ## Where to go next
292
328
 
293
- - [Consuming recipes](repositories-consuming.md): adding, subscribing, building, and what lands
294
- where
295
- - [Authoring a repository](repositories-authoring.md): `sous repo init`, writing recipes,
296
- releasing, and contributing
297
- - [Recipe variables](repositories-variables.md): the resolution ladder, answers, and the
298
- `sous vars` commands
299
- - [Repository file formats](repositories-file-formats.md): every manifest, index and lockfile
300
- schema
301
- - [Skill categories](skill-categories.md): the canonical category list the official repository
302
- uses as namespaces
329
+ - [Quickstart](repositories-quickstart.md): an empty project to a compiled skill, in order
330
+ - [Consuming recipes](repositories-consuming.md): adding, subscribing, building, removing
331
+ - [Authoring a repository](repositories-authoring.md): writing recipes, releasing, contributing
332
+ - [Recipe variables](repositories-variables.md): the resolution ladder and the question flow
333
+ - [Providers](repositories-providers.md): `github`, `gitlab` and `local`, and what each one can do
334
+ - [Repository file formats](repositories-file-formats.md): every manifest, index and schema
303
335
  - [Command reference](commands.md): every command and flag
336
+ - [Troubleshooting](repositories-troubleshooting.md): what the errors mean and how to clear them
337
+ - [Skill categories](skill-categories.md): the category list the official repository uses
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sous-io/sous",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Compiles AI coding agent configuration (CLAUDE.md, skills, memories) from LiquidJS templates",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -11,7 +11,7 @@ formatVersion: 1
11
11
 
12
12
  namespace: core
13
13
  name: sous-skills
14
- version: 0.2.1
14
+ version: 0.2.3
15
15
 
16
16
  description: >-
17
17
  The skills that teach an agent what sous is and how it works: which files sous
@@ -3,6 +3,7 @@ import { BaseCommand } from "../base-command.js";
3
3
  import { CompilationService } from "../lib/markdown-compiler.js";
4
4
  import { resolveCompilation, resolveRootScope } from "../lib/settings.js";
5
5
  import {
6
+ resolveProjectAnswers,
6
7
  resolveRecipeTargets,
7
8
  resolveStateFilePath,
8
9
  withRecipeTargets,
@@ -57,8 +58,9 @@ export default class Compile extends BaseCommand {
57
58
  // The recipes this project subscribes to contribute compile targets
58
59
  // alongside its own; both go through the same compiler.
59
60
  const withRecipes = () => {
60
- const scope = resolveRootScope(this.settings, this.configContext);
61
- const recipes = resolveRecipeTargets(this.settings, scope, this.configContext);
61
+ const answers = resolveProjectAnswers(this.settings, this.configContext);
62
+ const scope = resolveRootScope(this.settings, this.configContext, { answers });
63
+ const recipes = resolveRecipeTargets(this.settings, scope, this.configContext, answers);
62
64
  for (const notice of recipes.warnings) warning(notice);
63
65
  return withRecipeTargets(
64
66
  resolveCompilation(this.settings, scope),