@sous-io/sous 0.2.36 → 1.0.1

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.
Files changed (244) hide show
  1. package/README.md +14 -256
  2. package/dist/bin.js +11 -0
  3. package/dist/sous.js +1093 -0
  4. package/docs/markdown/README.md +5 -4
  5. package/docs/markdown/_sidebar.md +21 -1
  6. package/docs/markdown/commands.md +294 -431
  7. package/docs/markdown/config-discovery.md +25 -14
  8. package/docs/markdown/config-inspection.md +53 -31
  9. package/docs/markdown/config-layers.md +168 -66
  10. package/docs/markdown/config-variables.md +44 -11
  11. package/docs/markdown/configuration.md +104 -17
  12. package/docs/markdown/design-principles.md +4 -1
  13. package/docs/markdown/repositories-authoring.md +30 -0
  14. package/docs/markdown/repositories-consuming.md +72 -14
  15. package/docs/markdown/repositories-file-formats.md +31 -13
  16. package/docs/markdown/repositories-providers.md +48 -57
  17. package/docs/markdown/repositories-quickstart.md +3 -2
  18. package/docs/markdown/repositories-troubleshooting.md +27 -1
  19. package/docs/markdown/repositories-variables.md +6 -1
  20. package/docs/markdown/repositories.md +18 -8
  21. package/package.json +48 -107
  22. package/recipes/core/sous-skills/skills/about-liquid-templates/SKILL.tpl.md +4 -6
  23. package/recipes/core/sous-skills/skills/about-liquid-templates/references/liquid-filters.tpl.md +2 -0
  24. package/recipes/core/sous-skills/skills/about-sous/SKILL.tpl.md +10 -9
  25. package/recipes/core/sous-skills/skills/about-sous-configuration/SKILL.tpl.md +35 -14
  26. package/recipes/core/sous-skills/skills/about-sous-recipes/SKILL.tpl.md +51 -20
  27. package/recipes/core/sous-skills/sous.recipe.yaml +1 -1
  28. package/sous.config.schema.json +351 -132
  29. package/bin/run.js +0 -51
  30. package/src/base-command.ts +0 -457
  31. package/src/commands/build.ts +0 -181
  32. package/src/commands/clear.ts +0 -79
  33. package/src/commands/compile.ts +0 -155
  34. package/src/commands/config/get.ts +0 -110
  35. package/src/commands/config/show.ts +0 -32
  36. package/src/commands/config/validate.ts +0 -53
  37. package/src/commands/help.ts +0 -46
  38. package/src/commands/init.ts +0 -289
  39. package/src/commands/launch.ts +0 -139
  40. package/src/commands/lock/rebuild.ts +0 -239
  41. package/src/commands/lock/show.ts +0 -115
  42. package/src/commands/namespace/list.ts +0 -138
  43. package/src/commands/namespace/show.ts +0 -130
  44. package/src/commands/prune.ts +0 -43
  45. package/src/commands/recipe/list.ts +0 -112
  46. package/src/commands/recipe/show.ts +0 -439
  47. package/src/commands/repo/add.ts +0 -145
  48. package/src/commands/repo/contribute.ts +0 -604
  49. package/src/commands/repo/gc.ts +0 -173
  50. package/src/commands/repo/init.ts +0 -136
  51. package/src/commands/repo/link.ts +0 -899
  52. package/src/commands/repo/list.ts +0 -236
  53. package/src/commands/repo/release.ts +0 -685
  54. package/src/commands/repo/remove.ts +0 -194
  55. package/src/commands/repo/search.ts +0 -243
  56. package/src/commands/repo/submit.ts +0 -341
  57. package/src/commands/repo/unlink.ts +0 -461
  58. package/src/commands/subscription/add.ts +0 -286
  59. package/src/commands/subscription/list.ts +0 -208
  60. package/src/commands/subscription/remove.ts +0 -182
  61. package/src/commands/subscription/update.ts +0 -216
  62. package/src/commands/update.ts +0 -178
  63. package/src/commands/vars/ask.ts +0 -417
  64. package/src/commands/vars/index.ts +0 -79
  65. package/src/commands/vars/list.ts +0 -67
  66. package/src/commands/vars/show.ts +0 -77
  67. package/src/config-command.ts +0 -30
  68. package/src/lib/build-preparation.ts +0 -128
  69. package/src/lib/build-service.ts +0 -580
  70. package/src/lib/config-discovery.ts +0 -378
  71. package/src/lib/config-inspect.ts +0 -145
  72. package/src/lib/config-kernel.mjs +0 -379
  73. package/src/lib/config-schema.ts +0 -401
  74. package/src/lib/env-file.ts +0 -328
  75. package/src/lib/env-local.ts +0 -212
  76. package/src/lib/errors.ts +0 -32
  77. package/src/lib/glob-pattern.ts +0 -24
  78. package/src/lib/include-resolver.ts +0 -523
  79. package/src/lib/interactive.ts +0 -165
  80. package/src/lib/markdown-compiler.ts +0 -947
  81. package/src/lib/package-info.ts +0 -25
  82. package/src/lib/pid-service.ts +0 -99
  83. package/src/lib/project-install.d.mts +0 -44
  84. package/src/lib/project-install.mjs +0 -234
  85. package/src/lib/project-scaffold/index.ts +0 -332
  86. package/src/lib/project-scaffold/templates.ts +0 -240
  87. package/src/lib/repos/catalog-display.ts +0 -215
  88. package/src/lib/repos/catalog-inputs.ts +0 -288
  89. package/src/lib/repos/catalog.ts +0 -842
  90. package/src/lib/repos/contribute.ts +0 -452
  91. package/src/lib/repos/core-recipe.ts +0 -105
  92. package/src/lib/repos/declarations.ts +0 -187
  93. package/src/lib/repos/defaults.ts +0 -175
  94. package/src/lib/repos/formats/common.ts +0 -440
  95. package/src/lib/repos/formats/index-file.ts +0 -253
  96. package/src/lib/repos/formats/links-map.ts +0 -98
  97. package/src/lib/repos/formats/lockfile.ts +0 -169
  98. package/src/lib/repos/formats/patterns.ts +0 -57
  99. package/src/lib/repos/formats/recipe-manifest.ts +0 -205
  100. package/src/lib/repos/formats/repo-manifest.ts +0 -97
  101. package/src/lib/repos/formats/store-entry.ts +0 -84
  102. package/src/lib/repos/formats/variable-definition.ts +0 -268
  103. package/src/lib/repos/freshness.ts +0 -264
  104. package/src/lib/repos/git-clone.ts +0 -850
  105. package/src/lib/repos/identity.ts +0 -89
  106. package/src/lib/repos/index.ts +0 -57
  107. package/src/lib/repos/links.ts +0 -356
  108. package/src/lib/repos/load-manifest.ts +0 -237
  109. package/src/lib/repos/lock-service.ts +0 -454
  110. package/src/lib/repos/locked-namespace-resolver.ts +0 -232
  111. package/src/lib/repos/locked-recipe-lookup.ts +0 -123
  112. package/src/lib/repos/locked-recipes.ts +0 -254
  113. package/src/lib/repos/managed-layer.ts +0 -422
  114. package/src/lib/repos/memories-hash-name.ts +0 -33
  115. package/src/lib/repos/namespace-resolver.ts +0 -527
  116. package/src/lib/repos/providers/base.ts +0 -268
  117. package/src/lib/repos/providers/git.ts +0 -237
  118. package/src/lib/repos/providers/github.ts +0 -589
  119. package/src/lib/repos/providers/gitlab.ts +0 -332
  120. package/src/lib/repos/providers/http.ts +0 -128
  121. package/src/lib/repos/providers/index-cache.ts +0 -421
  122. package/src/lib/repos/providers/index.ts +0 -106
  123. package/src/lib/repos/providers/local.ts +0 -391
  124. package/src/lib/repos/providers/provider.ts +0 -615
  125. package/src/lib/repos/recipe-config-layers.ts +0 -316
  126. package/src/lib/repos/recipe-key-matcher.ts +0 -62
  127. package/src/lib/repos/recipe-memories.ts +0 -178
  128. package/src/lib/repos/recipe-targets.ts +0 -236
  129. package/src/lib/repos/ref-search.ts +0 -46
  130. package/src/lib/repos/release/bump.ts +0 -161
  131. package/src/lib/repos/release/changelog.ts +0 -461
  132. package/src/lib/repos/release/git-state.ts +0 -460
  133. package/src/lib/repos/release/index-builder.ts +0 -1108
  134. package/src/lib/repos/release/index.ts +0 -19
  135. package/src/lib/repos/release/plan.ts +0 -521
  136. package/src/lib/repos/release/settle.ts +0 -154
  137. package/src/lib/repos/release/submissions.ts +0 -214
  138. package/src/lib/repos/release/submit-checkout.ts +0 -290
  139. package/src/lib/repos/release/submit-questions.ts +0 -153
  140. package/src/lib/repos/release/submit-service.ts +0 -898
  141. package/src/lib/repos/release/tags.ts +0 -239
  142. package/src/lib/repos/release/validate.ts +0 -463
  143. package/src/lib/repos/resolver.ts +0 -1028
  144. package/src/lib/repos/scaffold/index.ts +0 -245
  145. package/src/lib/repos/scaffold/templates.ts +0 -517
  146. package/src/lib/repos/seed.ts +0 -473
  147. package/src/lib/repos/store/contract.ts +0 -64
  148. package/src/lib/repos/store/hash.ts +0 -191
  149. package/src/lib/repos/store/recipe-store.ts +0 -599
  150. package/src/lib/repos/store/settings.ts +0 -58
  151. package/src/lib/repos/subscription-service.ts +0 -3859
  152. package/src/lib/repos/trust.ts +0 -447
  153. package/src/lib/repos/update-plan.ts +0 -235
  154. package/src/lib/self-update/index.ts +0 -14
  155. package/src/lib/self-update/installs.ts +0 -412
  156. package/src/lib/self-update/managers.ts +0 -126
  157. package/src/lib/self-update/plan.ts +0 -315
  158. package/src/lib/self-update/registry.ts +0 -99
  159. package/src/lib/self-update/run.ts +0 -506
  160. package/src/lib/self-update/versions.ts +0 -243
  161. package/src/lib/settings.ts +0 -1105
  162. package/src/lib/sous-home.ts +0 -104
  163. package/src/lib/state.ts +0 -167
  164. package/src/lib/vars/answers.ts +0 -192
  165. package/src/lib/vars/ask.ts +0 -1152
  166. package/src/lib/vars/definition-source.ts +0 -261
  167. package/src/lib/vars/display.ts +0 -231
  168. package/src/lib/vars/index.ts +0 -20
  169. package/src/lib/vars/ladder.ts +0 -282
  170. package/src/lib/vars/mappings.ts +0 -260
  171. package/src/lib/vars/names.ts +0 -94
  172. package/src/lib/vars/preanswers.ts +0 -395
  173. package/src/lib/vars/question-plan.ts +0 -241
  174. package/src/lib/vars/report.ts +0 -228
  175. package/src/lib/vars/safe-regex.ts +0 -235
  176. package/src/lib/vars/secrets.ts +0 -131
  177. package/src/lib/vars/validate.ts +0 -312
  178. package/src/lib/version-report.ts +0 -90
  179. package/src/lib/watch-loop.ts +0 -148
  180. package/src/lib/watch-service.ts +0 -116
  181. package/src/services/ref-resolver/container.ts +0 -107
  182. package/src/services/ref-resolver/dependency.ts +0 -60
  183. package/src/services/ref-resolver/format.ts +0 -197
  184. package/src/services/ref-resolver/glob.ts +0 -115
  185. package/src/services/ref-resolver/hash-names.ts +0 -155
  186. package/src/services/ref-resolver/index.ts +0 -55
  187. package/src/services/ref-resolver/injectable.ts +0 -36
  188. package/src/services/ref-resolver/location.ts +0 -62
  189. package/src/services/ref-resolver/lookups/cached-index-lookup.ts +0 -53
  190. package/src/services/ref-resolver/lookups/catalog-lookup.ts +0 -23
  191. package/src/services/ref-resolver/lookups/catalog-matcher.ts +0 -261
  192. package/src/services/ref-resolver/lookups/chained-lookup.ts +0 -27
  193. package/src/services/ref-resolver/lookups/env-var-lookup.ts +0 -65
  194. package/src/services/ref-resolver/lookups/fetched-index-lookup.ts +0 -150
  195. package/src/services/ref-resolver/lookups/recorded-dependency-lookup.ts +0 -62
  196. package/src/services/ref-resolver/lookups/ref-lookup.ts +0 -54
  197. package/src/services/ref-resolver/lookups/variable-lookup.ts +0 -123
  198. package/src/services/ref-resolver/named-refs.ts +0 -144
  199. package/src/services/ref-resolver/parser/env-var-splitter.ts +0 -27
  200. package/src/services/ref-resolver/parser/location-splitter.ts +0 -239
  201. package/src/services/ref-resolver/parser/name-path-splitter.ts +0 -200
  202. package/src/services/ref-resolver/parser/partial-ref.ts +0 -84
  203. package/src/services/ref-resolver/parser/query-splitter.ts +0 -61
  204. package/src/services/ref-resolver/parser/range-splitter.ts +0 -52
  205. package/src/services/ref-resolver/parser/ref-parser.ts +0 -115
  206. package/src/services/ref-resolver/parser/repo-qualifier-splitter.ts +0 -51
  207. package/src/services/ref-resolver/parts.ts +0 -115
  208. package/src/services/ref-resolver/pruners/command-line-pruner.ts +0 -47
  209. package/src/services/ref-resolver/pruners/include-pruner.ts +0 -66
  210. package/src/services/ref-resolver/pruners/manifest-pruner.ts +0 -130
  211. package/src/services/ref-resolver/pruners/ref-pruner.ts +0 -135
  212. package/src/services/ref-resolver/pruners/rule-helpers.ts +0 -44
  213. package/src/services/ref-resolver/pruners/stored-key-pruner.ts +0 -136
  214. package/src/services/ref-resolver/ref-helpers.ts +0 -62
  215. package/src/services/ref-resolver/ref-picker-service.ts +0 -204
  216. package/src/services/ref-resolver/ref-report.ts +0 -84
  217. package/src/services/ref-resolver/ref-resolve-arguments.ts +0 -47
  218. package/src/services/ref-resolver/ref-resolve-result.ts +0 -36
  219. package/src/services/ref-resolver/ref-resolver-service.ts +0 -323
  220. package/src/services/ref-resolver/source.ts +0 -48
  221. package/src/services/ref-resolver/tokens.ts +0 -25
  222. package/src/services/ref-resolver/types.ts +0 -155
  223. package/src/templating/filters/bullet-list.ts +0 -9
  224. package/src/templating/filters/index.ts +0 -8
  225. package/src/templating/init-liquid-engine.ts +0 -139
  226. package/src/templating/lib/glob-files.ts +0 -74
  227. package/src/templating/lib/import-export.ts +0 -32
  228. package/src/templating/lib/secret-scope.ts +0 -41
  229. package/src/templating/lib/tag-args.ts +0 -19
  230. package/src/templating/tags/exportScalarVarsJs.ts +0 -46
  231. package/src/templating/tags/getFiles.ts +0 -89
  232. package/src/templating/tags/index.ts +0 -14
  233. package/src/templating/tags/listFiles.ts +0 -54
  234. package/src/templating/tags/showVars.ts +0 -28
  235. package/src/utils/choice-prompt.ts +0 -143
  236. package/src/utils/command-errors.ts +0 -186
  237. package/src/utils/command-help.ts +0 -45
  238. package/src/utils/confirm-prompt.ts +0 -110
  239. package/src/utils/flags.ts +0 -177
  240. package/src/utils/formatting.ts +0 -849
  241. package/src/utils/prompts.ts +0 -53
  242. package/src/utils/sous-directory.ts +0 -245
  243. package/src/utils/table.ts +0 -603
  244. package/src/utils/value-prompt.ts +0 -119
@@ -7,7 +7,7 @@ user-level config (nothing is read from `~/.sous`) and no multi-project map.
7
7
  ## The config file
8
8
 
9
9
  Exactly one primary config lives inside `.sous/`: `sous.config.js`, `sous.config.mjs`,
10
- `sous.config.json`, `sous.config.jsonc`, or `sous.config.yaml`. Two or more candidates is an
10
+ `sous.config.ts`, `sous.config.json`, `sous.config.jsonc`, or `sous.config.yaml`. Two or more candidates is an
11
11
  error, never a silent first-match. A typical config:
12
12
 
13
13
  ```js
@@ -16,8 +16,7 @@ export const config = {
16
16
  name: "My Project", // optional display name
17
17
  _env: { userHome: "HOME" }, // map config vars from env vars (top-level only)
18
18
  _vars: {
19
- projectRoot: "${sousDir}/..", // ${sousDir} is the discovered .sous/ dir
20
- docsDir: "${projectRoot}/docs", // declaration order never matters
19
+ docsDir: "${projectRoot}/docs", // projectRoot is set by sous; order never matters
21
20
  },
22
21
  _aliases: { myDocs: "${docsDir}" }, // extra @include bases (optional)
23
22
  compilation: {
@@ -38,9 +37,16 @@ export const config = {
38
37
  };
39
38
  ```
40
39
 
40
+ `${projectRoot}` is the directory that holds `.sous/`. Sous sets it, and setting `projectRoot` in any
41
+ `_vars` block is an error; see [Variables](config-variables.md#auto-vars).
42
+
43
+ The `.claude/skills` target above compiles the project's own skills. Skills from subscribed recipes
44
+ need no target of their own: the built-in Claude Code plugin sends them to `.claude/skills`, and
45
+ `recipes.skills.outputs` adds destinations of your own (see
46
+ [Consuming recipes](repositories-consuming.md)).
47
+
41
48
  A JSON config may set `"$schema"` to bind the `sous.config.schema.json` artifact shipped with
42
- sous for editor autocompletion; sous accepts and ignores the key. `sous init --format json`
43
- writes one already bound to the copy of that artifact published for the running version.
49
+ sous for editor autocompletion; sous accepts and ignores the key.
44
50
 
45
51
  ?> Configs use `${var}` syntax. Template files use LiquidJS double-brace syntax instead; the two
46
52
  are resolved at different stages and never mix.
@@ -72,32 +78,113 @@ A path may use glob syntax (`*`, `**`, `?`, `[..]`, `{a,b}`) and then includes e
72
78
  path order. The same file may be included in more than one place; a circular include is an error, as is a line
73
79
  that looks like an include (`@docs/notes.txt`, `@~workflow/missing`) but cannot be resolved, which fails the
74
80
  build naming the file and the line. A line inside a fenced code block is left as written. An alias name may not
75
- start with `~` or `#`. Reads of recipe files go through the
76
- [ref resolver](adrs/0013-one-ref-resolver.md); [Including recipe files in your own templates](repositories.md#including-recipe-files-in-your-own-templates)
81
+ start with `~` or `#`. [Including recipe files in your own templates](repositories.md#including-recipe-files-in-your-own-templates)
77
82
  has the namespace rules.
78
83
 
79
84
  ## Composition
80
85
 
81
- The primary config is optionally extended by drop-in layers: every `*.js|mjs|json|jsonc|yaml` file
82
- directly inside `.sous/conf.d/` is loaded after the primary config and deep-merged over it. A
83
- `.js`/`.mjs` config or layer can also compose programmatically through a `configure()` function.
84
- [Layers and merging](config-layers.md) covers the full semantics.
86
+ The primary config is optionally extended by drop-in files: every `*.js|mjs|ts|json|jsonc|yaml`
87
+ file directly inside `.sous/conf.d/` is applied after the primary config, in the order of the
88
+ number its name starts with (`500-repos.jsonc` before `610-mac.jsonc`), and merged into it. Recipes and plugins can change the config too, and your own files are always
89
+ applied again after them, so a value you set wins. A
90
+ `.js`, `.mjs` or `.ts` file can also change the config through a `configure()` function, and a
91
+ `.patch.json` or `.patch.jsonl` file holds raw edits. [Layers and merging](config-layers.md) covers
92
+ the full semantics.
93
+
94
+ ## Config expressions
95
+
96
+ A value in a JSON, JSONC or YAML config file may be an expression object, decided when sous builds
97
+ the config instead of when the file is written. The reserved keys are `$expr`, `$if`, `$elseif` and
98
+ `$else` (each `$if` and `$elseif` may carry a `:<name>` qualifier, and `$expr` a `:<language>` one).
99
+ Liquid is the expression language. An expression can read `host.platform`, `env.<NAME>`, variables
100
+ by their bare name, and recipe answers (except under `subscriptions` and `repos`).
101
+
102
+ A subscription switched off on a Mac only:
103
+
104
+ ```jsonc
105
+ // conf.d/610-mac.jsonc
106
+ {
107
+ "subscriptions": {
108
+ "tool-usage/automated-browser-tasks": {
109
+ "enabled": {
110
+ "$if": {
111
+ "expr": "host.platform == 'darwin'",
112
+ "then": false
113
+ }
114
+ }
115
+ }
116
+ }
117
+ }
118
+ ```
119
+
120
+ On a Mac, `enabled` is `false`. Elsewhere the condition is false and there is no `$else`, so the key
121
+ is dropped and the subscription stays on. An expression may appear at any depth, including inside
122
+ arrays. An object mixing a reserved key with any other key is an error, so a typo such as `"$iff"`
123
+ never passes through as config. The full rules are in
124
+ [the design of config expressions](architecture/specifications/S5-config-expressions.md).
125
+
126
+ ## Plugins
127
+
128
+ Sous is a small engine plus plugins; the CLI, recipes, the compiler, skills, memories, Claude Code
129
+ support and the package-manager and repository-host integrations are all built-in plugins. Which
130
+ plugins load is an ordinary config key, so it may be set in any config file, a recipe's included.
131
+ There is no separate bootstrap config and no `"sous"` key in `package.json`.
132
+
133
+ - `plugins.discover` (`"dependencies" | "scan" = "dependencies"`): `"dependencies"` loads the
134
+ built-ins, the plugins in the project's `package.json` dependencies (and the workspace root's) and
135
+ those under `load`; `"scan"` also looks for installed plugins through the package managers.
136
+ - `plugins.load` (`string[]`): plugins to load though `package.json` does not list them.
137
+ - `plugins.disabled` (`string[]`): plugins never to load, built-ins included.
138
+ - `plugins.options` (`Record<string, object>`): each plugin's own options, keyed by plugin name.
139
+
140
+ ```jsonc
141
+ // .sous/sous.config.jsonc
142
+ {
143
+ "plugins": {
144
+ "load": [
145
+ "sous-plugin-bun"
146
+ ],
147
+ "disabled": [
148
+ "sous-plugin-old-thing"
149
+ ],
150
+ "options": {
151
+ "sous-plugin-claude-code": {
152
+ "model": "opus"
153
+ }
154
+ }
155
+ }
156
+ }
157
+ ```
158
+
159
+ Two more keys settle what plugins do:
160
+
161
+ - `services.replace` picks the winner when two plugins both take over the same piece of sous's
162
+ work. Each piece has a name, such as `git.runner` (how sous runs git), and two plugins replacing
163
+ the same one stops sous with an error until you choose one:
164
+ `"services": { "replace": { "git.runner": "sous-plugin-fast-git" } }`. Sous then warns on every
165
+ run that the other plugin may not work as expected.
166
+ - `budgets` changes how long a step may take before sous warns that it is slow; a budget set to
167
+ `false` turns that warning off. `--timings` on any command prints how long every step took.
168
+
169
+ `sous plugins show` lists every plugin and what it adds. Recipes may ship plugins too; see
170
+ [where plugins come from](architecture/specifications/S1-engine.md#where-plugins-come-from).
85
171
 
86
172
  ## Where to go next
87
173
 
88
174
  - [Discovery and overrides](config-discovery.md): how sous finds the config, and the flags and
89
175
  environment variables that override it
90
- - [Layers and merging](config-layers.md): `conf.d/` ordering, merge semantics, the JS
91
- `configure()` contract
92
- - [Variables](config-variables.md): the `${var}` scope chain, auto-vars, fixpoint resolution
176
+ - [Layers and merging](config-layers.md): the order files apply in, how values merge, the
177
+ `configure()` function, patch files
178
+ - [Variables](config-variables.md): where `${var}` values come from, the variables sous sets
179
+ (`projectRoot` among them), and variables that refer to other variables
93
180
  - [Inspecting and validating](config-inspection.md): the `sous config` commands and the
94
181
  validation pipeline
95
182
 
96
183
  ## Rules of the road
97
184
 
98
- - Hand-written config belongs in the primary file or your own `conf.d/` layers. The
99
- `conf.d/500-*` through `conf.d/599-*` band is reserved for layers the sous CLI itself writes.
185
+ - Hand-written config belongs in the primary file or your own `conf.d/` files. The
186
+ `conf.d/500-*` through `conf.d/599-*` band is reserved for files the sous CLI itself writes.
100
187
  Sous edits those by key, so you may edit them too and your comments, key order and formatting
101
- survive; put a layer of your own outside the band.
188
+ survive; put a file of your own outside the band.
102
189
  - Any config problem halts sous with a `ConfigError` naming the offending file; there is no
103
190
  warn-and-continue.
@@ -52,7 +52,10 @@ sous at runtime.*
52
52
  Everything gets an escape hatch, within practicality. The happy path stays maximally simple;
53
53
  the escape hatches are advanced usage and may require the docs, but they must exist and they
54
54
  still get thoughtful ergonomics. Projects control everything granularly, and an individual
55
- user can override every project decision through their own configuration layers.
55
+ user can override every project decision through their own config files. Plugins get the same
56
+ reach: nearly everything sous does, the CLI included, is a plugin, and a plugin can change the
57
+ config, wrap or replace another plugin's work, and add commands. Where a choice is risky, sous
58
+ reports the problem and lets it happen rather than forbidding it.
56
59
 
57
60
  *Example: subscribing to a recipe is one line and pulls in everything the recipe provides;
58
61
  excluding one skill from that subscription may take a documented setting, but the setting is
@@ -99,6 +99,36 @@ template may include a file from a recipe this one depends on through the reserv
99
99
  this recipe's own `depends` and `subscribes` at their pinned versions; see
100
100
  [Including recipe files in your own templates](repositories.md#including-recipe-files-in-your-own-templates).
101
101
 
102
+ ### Recipes that ship sous plugins
103
+
104
+ A recipe may ship a sous plugin: code that extends sous itself, with the same plugin interface an npm
105
+ plugin uses. It is one self-contained bundled file, because sous never runs `npm install` for a recipe.
106
+ The same rule holds for anything else in a recipe that runs, such as a helper script or a hook. The
107
+ manifest names the bundle, and optionally its type declarations:
108
+
109
+ ```yaml
110
+ # sous.recipe.yaml
111
+ plugin:
112
+ bundle: plugin/index.js
113
+ types: plugin/index.d.ts # optional
114
+ ```
115
+
116
+ Nothing in a repository runs until the subscriber trusts the repository, so the trust question is the
117
+ gate for a recipe's plugin as for its other content. Sous copies a recipe plugin's `.d.ts` file into the
118
+ subscriber's `.sous/types/`, so other plugins in that project see its types; a plugin with no `.d.ts`
119
+ still works. A recipe may also require plugins from npm, each with a version range:
120
+
121
+ ```yaml
122
+ # sous.recipe.yaml
123
+ requires:
124
+ plugins:
125
+ sous-plugin-claude-code: "^1.0.0"
126
+ ```
127
+
128
+ A subscriber is offered the missing plugin when they subscribe; see
129
+ [Subscribe to a recipe](repositories-consuming.md#subscribe-to-a-recipe). How a plugin is written is in
130
+ [the engine specification](architecture/specifications/S1-engine.md), under "Plugins".
131
+
102
132
  ## Declare the variables a recipe needs
103
133
 
104
134
  A recipe that needs a value from the project asks through a **definition**: a published specification, never a
@@ -112,16 +112,16 @@ recorded in its canonical form, `namespace/recipe`.
112
112
  The whole dependency closure resolves before anything is downloaded, and only then is anything
113
113
  written; an install is whole or not at all.
114
114
 
115
- | Flag | What it does |
116
- |------|--------------|
117
- | `-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 |
118
- | `--accept-first` | When a ref matches several things, take the first one listed |
119
- | `--answer <name>=<value>` | Answer one question ahead of time. Repeat it per answer, or read a whole file of them with `--answers-file <path>` |
120
- | `--always-pull` | Install a newer in-range version whenever one exists, rather than holding the locked one |
121
- | `--dry-run` | Print what would be installed, writing and downloading nothing |
122
- | `--no-build` | Record the subscription without rebuilding the project |
123
- | `--prerelease` | Let prerelease versions take part in version range matching |
124
- | `--non-interactive` | Never ask; fail instead, naming the flag that would have answered |
115
+ A few flags change how it runs: `--yes` answers yes to the subscribe confirmation and the trust
116
+ question, `--answer <name>=<value>` answers a recipe's question ahead of time, `--dry-run` prints what
117
+ would be installed without writing or downloading anything, and `--non-interactive` fails instead of
118
+ asking, naming the flag that would have answered. `sous subscription add --help` lists every flag.
119
+
120
+ A recipe may require sous plugins from npm, each with a version range. When a required plugin is
121
+ missing or outside its range, `sous subscription add` refuses the recipe; run interactively, it offers
122
+ to install the plugin, and `--install-plugins` does that without asking. A build fails when a
123
+ subscribed recipe's requirement breaks later, for example after the plugin was removed, and the
124
+ message names the plugin and the command that installs it.
125
125
 
126
126
  ?> `sous subscribe` and `sous unsubscribe` are accepted spellings of these two commands, and every
127
127
  topic answers to both spellings of its name (`sous subscriptions add`, `sous repos list`).
@@ -175,10 +175,21 @@ like `workflow/qa-helper` above, is fetched and pinned but stays out of your out
175
175
 
176
176
  Recipe files compile under the [`.tpl.` convention](configuration.md#templates-and-the-tpl-convention): a file
177
177
  with `.tpl.` in its name is rendered through LiquidJS and loses `.tpl.` on the way out, and anything else is copied
178
- verbatim. Skills are written to the directories named by the
179
- [`recipes`](repositories-file-formats.md#recipes-what-each-content-kind-does) config key; for example
180
- `recipes: { skills: { outputs: ["${projectRoot}/.claude/skills", "${projectRoot}/.codex/skills"] } }`. The
181
- default is `<project root>/.claude/skills`, because that is where every agent looks. Written skills are tracked
178
+ verbatim. Skills are written to every skills directory a plugin for an agent tool names, and to the directories you name
179
+ under the [`recipes`](repositories-file-formats.md#recipes-what-each-content-kind-does) config key. The
180
+ Claude Code plugin built into sous adds `<project root>/.claude/skills`, so that is where skills land unless
181
+ you disable that plugin (see [Plugins](configuration.md#plugins)). To add a destination of your own:
182
+
183
+ ```js
184
+ recipes: {
185
+ skills: {
186
+ outputs: ["${projectRoot}/.codex/skills"],
187
+ },
188
+ }
189
+ ```
190
+
191
+ `projectRoot` is set by sous (the directory holding `.sous/`), so you never define it. When no plugin names a skills directory
192
+ and you name none, skills go nowhere, and the build says so and how to fix it. Written skills are tracked
182
193
  like every other file sous writes, so `sous prune` removes what an unsubscribed recipe used to write and
183
194
  `sous clear` removes all of it; neither reaches into a linked checkout.
184
195
 
@@ -214,6 +225,11 @@ When a build finds a memory an active recipe publishes that no include of any ou
214
225
  or the view) and `exclude` does not name, it warns, naming the memory and the line to add. Subscribing alone
215
226
  includes nothing, and the warning is how that is never silent.
216
227
 
228
+ ?> In sous 1.0 an agent's plugin can also name the instruction file that agent reads in every session
229
+ (`CLAUDE.md` for the Claude Code plugin built into sous). Sous then writes that file with one `@` include
230
+ line for each memory `recipes.memories` selects, so the memories reach the agent without a line of your own.
231
+ How that file and an instruction file your own templates write fit together is still being decided.
232
+
217
233
  ## Answer the questions
218
234
 
219
235
  A recipe publishes variable definitions; you supply answers. Subscribing asks whatever is
@@ -354,6 +370,48 @@ there is no entry to delete: the one sous provides comes back on the next run, s
354
370
  opt-out outlives it. `sous subscription add core` clears it again. If you remove `core`, make sure
355
371
  something else tells your agents not to edit generated files.
356
372
 
373
+ ## Switch a subscription off on one machine
374
+
375
+ A subscription stays in the config with `enabled: false`, so the opt-out is visible. A switched-off
376
+ subscription is not built: the recipes reached only through it are skipped, the outputs they wrote are
377
+ pruned, and their variables are neither asked nor required. Their pins stay in the committed lockfile,
378
+ so switching a recipe off on your machine never removes it for anyone else.
379
+
380
+ ```bash
381
+ sous subscription disable tool-usage/automated-browser-tasks
382
+ sous subscription enable tool-usage/automated-browser-tasks
383
+ ```
384
+
385
+ Both edit `.sous/conf.d/510-subscriptions.jsonc` by key. To switch one off only on some machines, write
386
+ `enabled` in a `conf.d` file of your own that sorts after 510; a later file wins over the managed one:
387
+
388
+ ```jsonc
389
+ // .sous/conf.d/610-mac.jsonc
390
+ {
391
+ "subscriptions": {
392
+ "tool-usage/automated-browser-tasks": {
393
+ "enabled": {
394
+ "$if": {
395
+ "expr": "host.platform == 'darwin'",
396
+ "then": false
397
+ }
398
+ }
399
+ }
400
+ }
401
+ }
402
+ ```
403
+
404
+ The build prints one line per skipped recipe, saying why:
405
+
406
+ ```text
407
+ $ sous build
408
+ Skipped tool-usage/automated-browser-tasks: its subscription is switched off (conf.d/610-mac.jsonc).
409
+ Pruned 4 outputs it had written.
410
+ ```
411
+
412
+ The `$if` object is a config expression, decided each time sous builds the config; see
413
+ [Config expressions](configuration.md#config-expressions).
414
+
357
415
  ## Restore a fresh clone
358
416
 
359
417
  A clone has the lockfile and the subscriptions, and no store. `sous build` restores what the
@@ -79,11 +79,23 @@ contents: # what a subscriber actually receives
79
79
  | `depends`, `subscribes` | no | Build dependencies and co-subscriptions; within either list, no entry may repeat |
80
80
  | `contents`, `variables` | no | Contents default to an empty list, which is what a curated bundle publishes; a duplicated variable name, or two definitions claiming one `env`, is an error |
81
81
  | `submissions` | no | Whether this recipe takes proposed changes, winning over the repository's block; see [The submissions block](#the-submissions-block) |
82
+ | `plugin` | no | A sous plugin the recipe ships: `bundle`, the path of one self-contained bundled file, and optionally `types`, its `.d.ts` file; see [Recipes that ship sous plugins](repositories-authoring.md#recipes-that-ship-sous-plugins) |
83
+ | `requires` | no | `requires.plugins` maps npm sous plugin names to version ranges the recipe needs |
82
84
 
83
- `contents[].kind` is `skills`, `memories`, `prompts` or `config`; there is no kind for a sous plugin. `skills` are
85
+ ```yaml
86
+ plugin:
87
+ bundle: plugin/index.js
88
+ types: plugin/index.d.ts
89
+ requires:
90
+ plugins:
91
+ sous-plugin-claude-code: "^1.0.0"
92
+ ```
93
+
94
+ `contents[].kind` is `skills`, `memories`, `prompts` or `config`; a sous plugin is not a content kind but the
95
+ `plugin` field above. `skills` are
84
96
  written to the destinations named by [`recipes`](#recipes-what-each-content-kind-does); `memories` are included
85
97
  into your instruction files by one line (see [Recipe memories](repositories-consuming.md#recipe-memories));
86
- `config` entries become config layers; nothing takes `prompts` yet, and the build warns about a recipe that
98
+ each `config` file joins the subscriber's config, under the project's own files so the project wins; nothing takes `prompts` yet, and the build warns about a recipe that
87
99
  publishes them. `include` needs at least one glob, and both lists are recipe-relative.
88
100
 
89
101
  ### The submissions block
@@ -126,9 +138,8 @@ two readings that both publish what is named fail it too, naming the spellings t
126
138
 
127
139
  ### Ref forms
128
140
 
129
- Every place a ref is written reads it with one ref resolver (`src/services/ref-resolver/`): a parser that
130
- returns every reading of the text, a pruner for the place that drops the readings the place refuses, and a
131
- lookup that keeps the readings that exist. These forms are recognized everywhere; the table after them says
141
+ Every place a ref is written reads it the same way: sous lists every way the text can be read, drops the
142
+ readings that place does not accept, and keeps the ones that name something that exists. These forms are recognized everywhere; the table after them says
132
143
  which each place allows. A refused form is an error saying what to write there instead.
133
144
 
134
145
  | Form | Example | Names |
@@ -359,7 +370,7 @@ These live at the top level of the merged config; [The config file](configuratio
359
370
  repos:
360
371
  sous-recipes:
361
372
  url: https://github.com/sous-io/sous-recipes
362
- provider: github # inferred from the URL when omitted; github, gitlab or local
373
+ provider: github # inferred from the URL when omitted; github, gitlab, local, or a host a plugin adds
363
374
  enabled: true # false opts out of a repository sous provides itself
364
375
  alwaysPull: false # install a newer in-range version rather than holding the lock
365
376
  addedBy: sous # provenance; "user", "sous" for an entry sous provides itself, or the
@@ -373,7 +384,7 @@ store: # every value is a default you can change
373
384
  freshnessSeconds: 300 # how long a fetched index stays fresh
374
385
  watchPollSeconds: 300 # how often watch mode polls upstream
375
386
  recipes: # what each content kind a subscribed recipe publishes does
376
- skills: { outputs: ["${projectRoot}/.claude/skills", "${projectRoot}/.codex/skills"] }
387
+ skills: { outputs: ["${projectRoot}/.codex/skills"] } # added to the skills directories agent plugins name
377
388
  memories: { first: ["communication/*"], exclude: ["tool-usage/automated-browser-tasks"] }
378
389
  varMappings:
379
390
  QA_SERVICE_TOKEN: workflow/qa-variables/qaServiceToken
@@ -395,15 +406,18 @@ the default stands.
395
406
 
396
407
  ### recipes: what each content kind does
397
408
 
398
- One block per content kind a subscribed recipe publishes. A recipe's `config` contents become layers, not files,
399
- and a recipe's own config layer may set `recipes.skills` only; `recipes.memories` chooses what the project
409
+ One block per content kind a subscribed recipe publishes. A recipe's `config` contents join the config, not the
410
+ output files, and a recipe's own config file may set `recipes.skills` only; `recipes.memories` chooses what the project
400
411
  includes, so it is dropped from a recipe's layer with a warning.
401
412
 
402
- - `recipes.skills.outputs` (`string[]`, default `["<project root>/.claude/skills"]`): the directories recipe skill
403
- bundles are written into, `${var}` substituted as in any config path. Name several to feed more than one agent.
413
+ - `recipes.skills.outputs` (`string[]`, default none): your own directories recipe skill bundles are written
414
+ into, besides every skills directory a plugin for an agent tool names. `${var}` is substituted as in any config path. The
415
+ built-in Claude Code plugin adds `<project root>/.claude/skills`, so skills land there with no setting. When no
416
+ plugin names a skills directory and this list is empty, skills go nowhere, and the build says why. `${projectRoot}` is set by sous;
417
+ setting `projectRoot` in any `_vars` is an error.
404
418
  - `recipes.memories.first` (`string[]`, default none): recipes whose memories lead the `#memories` view. An entry
405
419
  is a glob over `namespace/recipe` keys (`communication/*`), or a regular expression written between two slashes
406
- (`/^tool-usage\//`); a string, because a config layer is JSON and cannot hold a `RegExp`.
420
+ (`/^tool-usage\//`); a string, because the config is plain JSON data and cannot hold a `RegExp`.
407
421
  - `recipes.memories.exclude` (`string[]`, default none): recipes whose memories the view leaves out, and the
408
422
  build's unincluded-memory warning never lists. The same entry forms as `first`.
409
423
 
@@ -418,7 +432,7 @@ are `.jsonc` so they can carry real comments, and each opens with a header sayin
418
432
  | Layer | Holds | Written by |
419
433
  |-------|-------|-----------|
420
434
  | `500-repos.jsonc` | `repos` | `sous repo add`, `sous repo remove` |
421
- | `510-subscriptions.jsonc` | `subscriptions` | `sous subscription add`, `sous subscription remove` |
435
+ | `510-subscriptions.jsonc` | `subscriptions` | `sous subscription add`, `remove`, `enable` and `disable` |
422
436
  | `520-var-mappings.jsonc` | `varMappings` | `sous vars ask` |
423
437
 
424
438
  ```jsonc
@@ -438,6 +452,10 @@ so an interrupted write leaves the previous layer rather than a truncated one. A
438
452
  old `.json` name is read as a fallback and migrates on the next write: the `.jsonc` file is written and the
439
453
  `.json` one removed, so a project never ends up with both, which would be a hard config error.
440
454
 
455
+ No file is special to sous when it reads the config: a `conf.d` file of your own that sorts after 510 can
456
+ override a subscription the managed file lists, such as switching it off on one machine (see
457
+ [Switch a subscription off on one machine](repositories-consuming.md#switch-a-subscription-off-on-one-machine)).
458
+
441
459
  ## Where to go next
442
460
 
443
461
  - [Repositories](repositories.md): the model, trust, and the lockfile
@@ -4,9 +4,9 @@ A **provider** is everything sous knows about one kind of repository host. It is
4
4
  a host-specific fact is allowed to live, so every command above it (add, subscribe, build,
5
5
  release, submit) talks about repositories in the same terms whatever host they live on.
6
6
 
7
- Sous ships three: `github`, `gitlab` and `local`. This page covers how one is chosen for a URL,
8
- what a URL becomes once a provider has taken it apart, how private repositories authenticate,
9
- what each provider can do when you propose a change, and what writing a fourth one involves.
7
+ Sous ships three providers, `github`, `gitlab` and `local`, and a sous plugin can add another. This
8
+ page covers how a provider is chosen for a URL, what a URL becomes once a provider has taken it apart, how private repositories authenticate,
9
+ what each provider can do when you propose a change, and what adding another one involves.
10
10
 
11
11
  ## What a provider answers
12
12
 
@@ -132,8 +132,8 @@ absolute directory, because that is what git is given when a recipe is fetched.
132
132
  whose scheme is the provider's identifier: `github://sous-io/sous-recipes/workflow/sat@^1.1`. A
133
133
  first segment containing a dot is read as the host, so `gitlab://gitlab.example.com/group/project/-/ns/recipe`
134
134
  addresses a self-hosted instance; without one, the provider's public host is assumed. Where a
135
- repository path ends, and what a browser URL's path looks like, is each provider's own rule (its
136
- `readLocation`), so a URL copied from either host's file view works too. `local://` is refused,
135
+ repository path ends, and what a browser URL's path looks like, is each provider's own rule, so a URL
136
+ copied from either host's file view works too. `local://` is refused,
137
137
  because a path on your disk is not a published location. Every form is in
138
138
  [Ref forms](repositories-file-formats.md#ref-forms).
139
139
 
@@ -141,37 +141,48 @@ because a path on your disk is not a published location. Every form is in
141
141
 
142
142
  Sous reads a repository in two very different ways, and they authenticate differently.
143
143
 
144
- **The index** is one plain HTTPS GET of a raw file, carrying a bearer token when sous has one:
144
+ **The index** is read by trying three ways in order, so reading works with whatever access you have.
145
+ Neither `gh` nor `glab` is ever required to read a repository; they are needed only for
146
+ `sous repo contribute` and `sous repo submit`.
147
+
148
+ ```text
149
+ Reading sous.index.json from a repository
150
+ 1. HTTP with a token if a token is found (see below)
151
+ 2. HTTP with no token public repositories
152
+ 3. git a shallow, sparse fetch of the one file, using your own git
153
+ credentials (SSH keys, credential helpers)
154
+ ```
155
+
156
+ For GitLab, the HTTP requests use the API address, with the whole project path encoded, so a nested
157
+ group's `/` becomes `%2F`:
145
158
 
146
159
  ```text
147
160
  https://raw.githubusercontent.com/<owner>/<name>/HEAD/sous.index.json
148
- https://<gitlab host>/<owner>/<name>/-/raw/HEAD/sous.index.json
161
+ https://<gitlab host>/api/v4/projects/<owner>%2F<name>/repository/files/sous.index.json/raw?ref=HEAD
149
162
  ```
150
163
 
151
- A token is looked for in two places, in this order:
164
+ A token is looked for in these places, in this order, and a missing one is skipped:
152
165
 
153
166
  1. the environment: `GITHUB_TOKEN` for GitHub, `GITLAB_TOKEN` for GitLab;
154
- 2. the host's own command line tool, `gh auth token` or `glab auth token`, when it is installed
155
- and signed in.
167
+ 2. `git credential fill`, the credential git already stores for `https://<host>`;
168
+ 3. the host's own command line tool, only when it is installed: `gh` for GitHub, and
169
+ `glab config get token --host <host>` for GitLab.
156
170
 
157
- Neither is required. A public repository needs no token at all, and a missing `gh` or `glab` is
158
- never an error on the read path; sous simply fetches without one. When the host refuses, the
159
- error says which of the two situations you are in:
171
+ A command's output counts as a token only when the command exits `0` and prints exactly one non-empty
172
+ line holding no whitespace.
160
173
 
161
- ```text
162
- Sous could not fetch the repo index from https://raw.githubusercontent.com/acme/recipes/HEAD/sous.index.json.
163
- The server answered 404 Not Found.
164
- Either the repository publishes no sous index yet, or the URL names a repository that does
165
- not exist.
166
- ```
167
-
168
- A 401 or 403 says the repository is private or was not authorized, and points at the two sources:
174
+ Sous remembers, per repository, which way worked, in `$SOUS_HOME/cache/read-strategies.json`, and tries it
175
+ first next time; after a failure it forgets it and walks the whole list again. When every way fails, one
176
+ error lists each attempt and why it failed, then names the fixes:
169
177
 
170
178
  ```text
171
- Sous could not fetch the repo index from https://raw.githubusercontent.com/acme/recipes/HEAD/sous.index.json.
172
- The server answered 403 Forbidden.
173
- The repository is private or the request was not authorized. Sous uses a token from the
174
- environment, or from the provider's command line tool when one is installed and signed in.
179
+ Error: could not read gitlab.com/acme/private-recipes.
180
+ HTTP with token no token found (GITLAB_TOKEN unset; git has no stored credential for gitlab.com)
181
+ HTTP without token 302 to sign-in page: the repository is private
182
+ git Permission denied (publickey)
183
+ Fix one of these:
184
+ export GITLAB_TOKEN=<a token with read_repository>
185
+ set up git access to gitlab.com (an SSH key, or a credential helper)
175
186
  ```
176
187
 
177
188
  **A recipe's files** come from git itself: a shallow, blobless, sparse clone at the version's
@@ -180,9 +191,8 @@ apply, exactly as they would for a manual clone: credential helpers, the SSH age
180
191
  `insteadOf` rewrites are all inherited from your git configuration, and sous adds nothing of its
181
192
  own. Cloning a working copy with `sous repo link` works the same way.
182
193
 
183
- ?> For a private repository, make sure both halves work. `gh auth login` (or `GITHUB_TOKEN`)
184
- covers the index, and a git credential helper or an SSH key covers the recipe files. A build that
185
- finds the index but cannot clone has only the second half missing.
194
+ ?> For a private repository, git access (a credential helper or an SSH key) covers both the index
195
+ and the recipe files. A token in the environment makes reading the index faster.
186
196
 
187
197
  ## Proposing a change
188
198
 
@@ -238,12 +248,12 @@ provider once, on the repository entry:
238
248
  }
239
249
  ```
240
250
 
241
- That block goes at the top level of your primary config or of a `conf.d/` layer of your own; sous
242
- writes its own entries to the `conf.d/500-repos.jsonc` layer it manages. Every key is listed under
251
+ That block goes at the top level of your primary config or of a `conf.d/` file of your own; sous
252
+ writes its own entries to the `conf.d/500-repos.jsonc` file it manages. Every key is listed under
243
253
  [configuration keys](repositories-file-formats.md#configuration-keys).
244
254
 
245
255
  Everything downstream then works normally: the index is read from that host's own raw endpoint,
246
- `GITLAB_TOKEN` or `glab` supplies the token, and the identity is
256
+ a token comes from the sources above, and the identity is
247
257
  `git.example.com/platform/recipes`. Group paths of any depth are preserved, so
248
258
  `group/subgroup/project` stays one repository rather than being mistaken for a namespace.
249
259
 
@@ -296,32 +306,13 @@ one repository's resolution at a working copy without changing what your project
296
306
 
297
307
  ## Adding a provider
298
308
 
299
- Adding a provider is one file plus one line. The interface is internal for now, not a published
300
- plugin API, so it can still change shape; what follows is what a new provider writes today. A
301
- provider class extends `ProviderBase`, which carries the plumbing no provider should repeat:
302
- running a subprocess through the injectable runner, checking whether a tool exited cleanly,
303
- capturing its output, finding a host token, and refusing every write-path call by name until a
304
- subclass overrides it. Each member is documented where it lives, in
305
- `src/lib/repos/providers/base.ts`.
306
-
307
- The subclass supplies the rest:
308
-
309
- - `id`, the identifier a repository entry and a locator scheme use;
310
- - `features`, the ones it genuinely answers (`fetch`; `submit` only if all four write calls are
311
- real; `proposals` only if `findProposal`, `proposalStatus` and `updateProposal` are);
312
- - `matches(url)` and `canonicalize(url)`, the URL half;
313
- - `fetchIndex(repo, options)` and `fetchRecipeTree(repo, recipePath, tag, destDir, options)`, the
314
- read half;
315
- - `cli` and `proposalNoun` when it submits, so messages can name the tool and call a proposal
316
- what the host calls it;
317
- - overrides of the four write-path calls when it submits, and of the three proposals calls when
318
- it can follow a proposal up. Each answers with plain data (a proposal's id, address, state,
319
- title, review and check counts), never with a host's own vocabulary.
320
-
321
- Every call takes an options object carrying the testing seams (`cwd`, `env`, `fetchImpl`, `run`),
322
- which is why no provider reaches for `spawn` or the global `fetch` directly and no test in this
323
- layer touches the network. The last change is adding the class to the built-in provider list in
324
- `providers/index.ts`; nothing above the provider layer learns its name.
309
+ A provider is a sous plugin. It holds everything specific to its host: recognizing and taking apart the
310
+ host's URLs, the ways it reads an index and where it finds a token, and, when it supports them, proposing
311
+ changes and the CI workflow `sous repo init` writes. Nothing else in sous knows a host's name. A plugin can
312
+ also add a way of reading to an existing provider, for example for a host with its own sign-in.
313
+
314
+ ?> The exact interface a provider plugin implements is not published yet; it is settled as sous 1.0
315
+ lands. The plugin design is in [the engine specification](architecture/specifications/S1-engine.md).
325
316
 
326
317
  ## Where to go next
327
318
 
@@ -23,8 +23,9 @@ cd ~/projects/my-project
23
23
  echo 'name: my-project' > .sous/sous.config.yaml
24
24
  ```
25
25
 
26
- Everything else has a default: skills compile into `<project root>/.claude/skills`, and the
27
- lockfile, the state file and the config layers sous writes land under `.sous/`. Outside a
26
+ Everything else has a default: skills compile into `<project root>/.claude/skills` (where the
27
+ Claude Code plugin built into sous puts them), and the lockfile, the state file and the `conf.d`
28
+ files sous writes land under `.sous/`. Outside a
28
29
  walkthrough, `sous init` writes a fuller starting point (a commented config, a starter prompt,
29
30
  the env files and the ignore block) and runs the first build for you; the one-line config is
30
31
  used here so that each step shows one thing. `SOUS_HOME` puts