@sous-io/sous 0.1.1 → 0.2.0

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 (205) hide show
  1. package/README.md +115 -35
  2. package/bin/run.js +10 -1
  3. package/docs/markdown/README.md +27 -0
  4. package/docs/markdown/_sidebar.md +18 -0
  5. package/docs/markdown/commands.md +308 -0
  6. package/docs/markdown/config-discovery.md +74 -0
  7. package/docs/markdown/config-inspection.md +69 -0
  8. package/docs/markdown/config-layers.md +92 -0
  9. package/docs/markdown/config-variables.md +79 -0
  10. package/docs/markdown/configuration.md +71 -0
  11. package/docs/markdown/design-principles.md +59 -0
  12. package/docs/markdown/repositories-authoring.md +408 -0
  13. package/docs/markdown/repositories-consuming.md +580 -0
  14. package/docs/markdown/repositories-file-formats.md +1084 -0
  15. package/docs/markdown/repositories-variables.md +387 -0
  16. package/docs/markdown/repositories.md +303 -0
  17. package/docs/markdown/skill-categories.md +58 -0
  18. package/package.json +72 -8
  19. package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/SKILL.tpl.md +20 -20
  20. package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/examples/about-something.md +2 -2
  21. package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/examples/do-something.md +1 -1
  22. package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/references/advanced-patterns.md +6 -6
  23. package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/references/commands.md +5 -5
  24. package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/references/frontmatter.md +3 -3
  25. package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-liquid-templates/SKILL.tpl.md +40 -25
  26. package/recipes/core/sous-skills/skills/about-sous/SKILL.tpl.md +70 -0
  27. package/recipes/core/sous-skills/skills/about-sous-configuration/SKILL.tpl.md +75 -0
  28. package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/create-skill/SKILL.tpl.md +8 -9
  29. package/recipes/core/sous-skills/sous.recipe.yaml +45 -0
  30. package/sous.config.schema.json +337 -0
  31. package/src/base-command.ts +220 -67
  32. package/src/commands/build.ts +150 -73
  33. package/src/commands/clear.ts +23 -15
  34. package/src/commands/compile.ts +74 -16
  35. package/src/commands/config/get.ts +110 -0
  36. package/src/commands/config/show.ts +32 -0
  37. package/src/commands/config/validate.ts +53 -0
  38. package/src/commands/help.ts +46 -0
  39. package/src/commands/launch.ts +36 -14
  40. package/src/commands/lock/rebuild.ts +241 -0
  41. package/src/commands/lock/show.ts +115 -0
  42. package/src/commands/namespace/list.ts +117 -0
  43. package/src/commands/namespace/show.ts +110 -0
  44. package/src/commands/prune.ts +3 -11
  45. package/src/commands/recipe/list.ts +95 -0
  46. package/src/commands/recipe/show.ts +301 -0
  47. package/src/commands/repo/add.ts +145 -0
  48. package/src/commands/repo/gc.ts +172 -0
  49. package/src/commands/repo/init.ts +136 -0
  50. package/src/commands/repo/link.ts +500 -0
  51. package/src/commands/repo/list.ts +179 -0
  52. package/src/commands/repo/release.ts +619 -0
  53. package/src/commands/repo/remove.ts +193 -0
  54. package/src/commands/repo/search.ts +189 -0
  55. package/src/commands/repo/submit.ts +133 -0
  56. package/src/commands/repo/unlink.ts +147 -0
  57. package/src/commands/subscription/add.ts +285 -0
  58. package/src/commands/subscription/list.ts +129 -0
  59. package/src/commands/subscription/remove.ts +181 -0
  60. package/src/commands/vars/ask.ts +374 -0
  61. package/src/commands/vars/index.ts +79 -0
  62. package/src/commands/vars/list.ts +67 -0
  63. package/src/commands/vars/show.ts +77 -0
  64. package/src/config-command.ts +30 -0
  65. package/src/lib/build-service.ts +206 -54
  66. package/src/lib/config-discovery.ts +220 -27
  67. package/src/lib/config-inspect.ts +145 -0
  68. package/src/lib/config-kernel.mjs +377 -0
  69. package/src/lib/config-schema.ts +361 -0
  70. package/src/lib/env-file.ts +328 -0
  71. package/src/lib/env-local.ts +18 -1
  72. package/src/lib/errors.ts +32 -0
  73. package/src/lib/include-resolver.ts +108 -15
  74. package/src/lib/interactive.ts +165 -0
  75. package/src/lib/markdown-compiler.ts +118 -37
  76. package/src/lib/package-info.ts +25 -0
  77. package/src/lib/pid-service.ts +32 -21
  78. package/src/lib/refs/find.ts +589 -0
  79. package/src/lib/refs/index.ts +12 -0
  80. package/src/lib/refs/pick.ts +147 -0
  81. package/src/lib/refs/scopes.ts +61 -0
  82. package/src/lib/repos/catalog-display.ts +116 -0
  83. package/src/lib/repos/catalog-inputs.ts +160 -0
  84. package/src/lib/repos/catalog.ts +722 -0
  85. package/src/lib/repos/core-recipe.ts +105 -0
  86. package/src/lib/repos/defaults.ts +175 -0
  87. package/src/lib/repos/formats/common.ts +389 -0
  88. package/src/lib/repos/formats/index-file.ts +215 -0
  89. package/src/lib/repos/formats/links-map.ts +96 -0
  90. package/src/lib/repos/formats/lockfile.ts +167 -0
  91. package/src/lib/repos/formats/patterns.ts +57 -0
  92. package/src/lib/repos/formats/recipe-manifest.ts +395 -0
  93. package/src/lib/repos/formats/repo-manifest.ts +88 -0
  94. package/src/lib/repos/formats/store-entry.ts +84 -0
  95. package/src/lib/repos/freshness.ts +208 -0
  96. package/src/lib/repos/git-clone.ts +312 -0
  97. package/src/lib/repos/identity.ts +89 -0
  98. package/src/lib/repos/index.ts +58 -0
  99. package/src/lib/repos/links.ts +353 -0
  100. package/src/lib/repos/load-manifest.ts +236 -0
  101. package/src/lib/repos/lock-service.ts +453 -0
  102. package/src/lib/repos/locked-namespace-resolver.ts +90 -0
  103. package/src/lib/repos/locked-recipes.ts +254 -0
  104. package/src/lib/repos/managed-layer.ts +422 -0
  105. package/src/lib/repos/namespace-resolver.ts +370 -0
  106. package/src/lib/repos/providers/base.ts +206 -0
  107. package/src/lib/repos/providers/git.ts +233 -0
  108. package/src/lib/repos/providers/github.ts +294 -0
  109. package/src/lib/repos/providers/gitlab.ts +263 -0
  110. package/src/lib/repos/providers/http.ts +102 -0
  111. package/src/lib/repos/providers/index-cache.ts +382 -0
  112. package/src/lib/repos/providers/index.ts +106 -0
  113. package/src/lib/repos/providers/local.ts +391 -0
  114. package/src/lib/repos/providers/provider.ts +401 -0
  115. package/src/lib/repos/recipe-config-layers.ts +287 -0
  116. package/src/lib/repos/recipe-targets.ts +223 -0
  117. package/src/lib/repos/ref-search.ts +46 -0
  118. package/src/lib/repos/ref.ts +513 -0
  119. package/src/lib/repos/reference-report.ts +122 -0
  120. package/src/lib/repos/release/bump.ts +161 -0
  121. package/src/lib/repos/release/git-state.ts +305 -0
  122. package/src/lib/repos/release/index-builder.ts +635 -0
  123. package/src/lib/repos/release/index.ts +16 -0
  124. package/src/lib/repos/release/plan.ts +512 -0
  125. package/src/lib/repos/release/submit-service.ts +496 -0
  126. package/src/lib/repos/release/tags.ts +243 -0
  127. package/src/lib/repos/release/validate.ts +463 -0
  128. package/src/lib/repos/resolver.ts +789 -0
  129. package/src/lib/repos/scaffold/index.ts +238 -0
  130. package/src/lib/repos/scaffold/templates.ts +413 -0
  131. package/src/lib/repos/seed.ts +414 -0
  132. package/src/lib/repos/store/contract.ts +64 -0
  133. package/src/lib/repos/store/hash.ts +114 -0
  134. package/src/lib/repos/store/recipe-store.ts +599 -0
  135. package/src/lib/repos/store/settings.ts +58 -0
  136. package/src/lib/repos/subscription-service.ts +2678 -0
  137. package/src/lib/repos/trust.ts +447 -0
  138. package/src/lib/settings.ts +546 -189
  139. package/src/lib/sous-home.ts +104 -0
  140. package/src/lib/state.ts +52 -20
  141. package/src/lib/vars/ask.ts +1152 -0
  142. package/src/lib/vars/definition-source.ts +252 -0
  143. package/src/lib/vars/display.ts +233 -0
  144. package/src/lib/vars/index.ts +18 -0
  145. package/src/lib/vars/ladder.ts +282 -0
  146. package/src/lib/vars/mappings.ts +265 -0
  147. package/src/lib/vars/names.ts +94 -0
  148. package/src/lib/vars/preanswers.ts +395 -0
  149. package/src/lib/vars/question-plan.ts +218 -0
  150. package/src/lib/vars/report.ts +228 -0
  151. package/src/lib/vars/safe-regex.ts +235 -0
  152. package/src/lib/vars/validate.ts +312 -0
  153. package/src/lib/watch-loop.ts +148 -0
  154. package/src/templating/init-liquid-engine.ts +58 -16
  155. package/src/utils/choice-prompt.ts +143 -0
  156. package/src/utils/command-errors.ts +186 -0
  157. package/src/utils/command-help.ts +45 -0
  158. package/src/utils/confirm-prompt.ts +110 -0
  159. package/src/utils/flags.ts +153 -0
  160. package/src/utils/formatting.ts +540 -55
  161. package/src/utils/prompts.ts +35 -1
  162. package/src/utils/sous-directory.ts +245 -0
  163. package/src/utils/table.ts +603 -0
  164. package/src/utils/value-prompt.ts +119 -0
  165. package/shared-prompts/_partials/resume-task.md +0 -51
  166. package/shared-prompts/_partials/sub-agent-delegation.md +0 -32
  167. package/shared-prompts/_partials/update-task-file.md +0 -52
  168. package/shared-prompts/memories/automated-browser-tasks/INDEX.tpl.md +0 -52
  169. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/SKILL.tpl.md +0 -102
  170. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/auth-failure-handling.mjs +0 -81
  171. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/chained-workflow.mjs +0 -126
  172. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/simple-fetch.mjs +0 -92
  173. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/architecture.md +0 -61
  174. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/auth-and-sessions.md +0 -65
  175. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/ctx-api.md +0 -96
  176. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/installation.md +0 -104
  177. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/script-conventions.md +0 -243
  178. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/chrome-state.mjs +0 -148
  179. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/debug.mjs +0 -383
  180. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/debug.spec.mjs +0 -267
  181. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/eslint.config.mjs +0 -56
  182. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/harness.mjs +0 -169
  183. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/keyring.mjs +0 -59
  184. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/logger.mjs +0 -25
  185. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/params.mjs +0 -140
  186. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/run.mjs +0 -140
  187. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/settings.tpl.mjs +0 -1
  188. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/utils.mjs +0 -185
  189. package/shared-prompts/skills/automated-browser-tasks/create-automated-browser-task/SKILL.tpl.md +0 -52
  190. package/shared-prompts/skills/automated-browser-tasks/running-automated-browser-tasks/SKILL.tpl.md +0 -59
  191. package/shared-prompts/skills/automated-browser-tasks/update-automated-browser-task/SKILL.tpl.md +0 -47
  192. package/shared-prompts/skills/control-flow/approve/SKILL.tpl.md +0 -26
  193. package/shared-prompts/skills/control-flow/opine/SKILL.tpl.md +0 -58
  194. package/shared-prompts/skills/control-flow/repeat/SKILL.tpl.md +0 -27
  195. package/shared-prompts/skills/control-flow/research/SKILL.tpl.md +0 -34
  196. package/shared-prompts/skills/sous-skills/about-sous/SKILL.tpl.md +0 -51
  197. package/shared-prompts/skills/task-files/about-task-files/SKILL.tpl.md +0 -122
  198. package/shared-prompts/skills/task-files/continue-task-in-new-branch/SKILL.tpl.md +0 -80
  199. package/shared-prompts/skills/task-files/go/SKILL.tpl.md +0 -14
  200. package/shared-prompts/skills/task-files/resume-task/SKILL.tpl.md +0 -13
  201. package/shared-prompts/skills/task-files/start-task/SKILL.tpl.md +0 -93
  202. package/shared-prompts/skills/task-files/update/SKILL.tpl.md +0 -14
  203. package/shared-prompts/skills/task-files/update-task-file/SKILL.tpl.md +0 -13
  204. /package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/references/substitutions.md +0 -0
  205. /package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-liquid-templates/references/liquid-filters.md +0 -0
package/README.md CHANGED
@@ -1,9 +1,20 @@
1
+ <p align="center">
2
+ <a href="https://sous.io">
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/sous-io/sous/main/docs/img/logo-on-dark-sm.png">
5
+ <img src="https://raw.githubusercontent.com/sous-io/sous/main/docs/img/logo-on-white-sm.png" alt="Sous" height="180">
6
+ </picture>
7
+ </a>
8
+ </p>
9
+
1
10
  # sous
2
11
 
3
12
  sous compiles AI coding agent configuration from templates. You write skills, memories, and
4
13
  instructions once as LiquidJS templates in layered sources, then sous renders them into the
5
14
  formats agents actually read: `.claude/` plus `CLAUDE.md` for Claude Code, `.codex/` plus
6
- `AGENTS.md` for Codex. The CLI binary is named `xcv`.
15
+ `AGENTS.md` for Codex. The CLI binary is named `sous`.
16
+
17
+ **New to sous? Watch the animated introduction at [sous.io](https://sous.io).**
7
18
 
8
19
  ## Why
9
20
 
@@ -31,7 +42,7 @@ npm link
31
42
  ```
32
43
 
33
44
  Then set up a project. A project needs a `.sous/` directory holding a config file, named
34
- `sous.config.js`, `sous.config.mjs`, or `sous.config.json`:
45
+ `sous.config.js`, `sous.config.mjs`, `sous.config.json`, or `sous.config.yaml`:
35
46
 
36
47
  ```bash
37
48
  cd /path/to/your/project
@@ -43,32 +54,28 @@ A config that compiles one file:
43
54
 
44
55
  ```js
45
56
  export const config = {
46
- projects: {
47
- myproject: {
48
- name: "My Project",
49
- _vars: { projectRoot: "${sousDir}/.." },
50
- compilation: {
51
- targets: [
52
- {
53
- entryPoint: "${sousDir}/AGENTS.md",
54
- outputs: [{ destinationFile: "${projectRoot}/CLAUDE.md" }],
55
- },
56
- ],
57
+ name: "My Project",
58
+ _vars: { projectRoot: "${sousDir}/.." },
59
+ compilation: {
60
+ targets: [
61
+ {
62
+ entryPoint: "${sousDir}/AGENTS.md",
63
+ outputs: [{ destinationFile: "${projectRoot}/CLAUDE.md" }],
57
64
  },
58
- },
65
+ ],
59
66
  },
60
67
  };
61
68
  ```
62
69
 
63
70
  `${sousDir}` is the `.sous/` directory sous found, so a config can name paths relative to
64
- itself without hardcoding anything machine-specific. With a single project defined you do
65
- not need `defaultProject`; sous uses the only one. Then build:
71
+ itself without hardcoding anything machine-specific. One config describes one project. Then
72
+ build:
66
73
 
67
74
  ```bash
68
- xcv build
75
+ sous build
69
76
  ```
70
77
 
71
- `xcv build` compiles every configured target and prunes outputs that are no longer in the
78
+ `sous build` compiles every configured target and prunes outputs that are no longer in the
72
79
  config. Config discovery walks up from the current directory until it finds a `.sous/`
73
80
  directory holding a config, so you can run it from anywhere inside the project. Pass
74
81
  `--config <path>` to point at one explicitly instead.
@@ -81,19 +88,22 @@ anything resolves. There are two layers:
81
88
  - `.sous/.env.local` is gitignored. Put machine-specific values and secrets here.
82
89
 
83
90
  Precedence, highest first: your shell environment, then `.env.local`, then `.env`. So
84
- `FOO=bar xcv build` beats both files, and `.env.local` beats `.env` per key. This repo
91
+ `FOO=bar sous build` beats both files, and `.env.local` beats `.env` per key. This repo
85
92
  ships `.sous/.env.local.example` documenting the layer.
86
93
 
87
94
  Useful commands:
88
95
 
89
96
  | Command | What it does |
90
97
  |---|---|
91
- | `xcv build` | Compile, then prune stale outputs |
92
- | `xcv build --watch` | Rebuild on source changes |
93
- | `xcv compile` | Compile only |
94
- | `xcv prune` | Remove outputs no longer in the config |
95
- | `xcv clear` | Delete every file sous wrote for the project |
96
- | `xcv launch claude` | Build, then start the agent |
98
+ | `sous build` | Compile, then prune stale outputs |
99
+ | `sous build --watch` | Rebuild on source changes |
100
+ | `sous compile` | Compile only |
101
+ | `sous prune` | Remove outputs no longer in the config |
102
+ | `sous clear` | Delete every file sous wrote for the project |
103
+ | `sous launch claude` | Build, then start the agent |
104
+ | `sous config show` | Print the merged config (all layers) as JSON |
105
+ | `sous config get <path>` | Read one value by dot-path; `--layers` shows which file set it |
106
+ | `sous config validate` | Validate the merged config: schema, then variable resolution |
97
107
 
98
108
  ## How it works
99
109
 
@@ -119,28 +129,98 @@ each level can define variables:
119
129
  }
120
130
  ```
121
131
 
122
- Variables resolve later-wins across scopes: auto-injected, env, root, project, compilation,
132
+ Variables resolve later-wins across scopes: auto-injected, env, config, compilation,
123
133
  target, output. Templates read them as `{{ varName }}`; config files reference them as `${varName}`.
124
134
  The auto-injected ones include `${sousDir}` and `${sousConfigPath}` for the discovered
125
135
  config, and `${sousTemplatePath}` for the template being rendered.
126
136
 
127
137
  Sources come in three tiers, each able to build on the one above it:
128
138
 
129
- 1. **Built-in** shared prompts that ship with sous, covering skill authoring, templating, and
130
- the sous conventions themselves.
131
- 2. **Team-shared**, a repo your team owns, holding the configs everyone should get.
139
+ 1. **Published recipes**, fetched from a recipe repository and pinned in your project's
140
+ lockfile. The official repository publishes the `core` namespace, which teaches an agent
141
+ about sous itself and which every project gets without asking, plus recipes for skill
142
+ authoring, task tracking and more.
143
+ 2. **Team-shared**, a recipe repository your team owns, holding the recipes everyone should
144
+ get.
132
145
  3. **Per-project**, the project's own `.sous/` directory, for anything specific to it.
133
146
 
134
- The built-in tier is reachable without knowing where sous is installed. `@include` paths
135
- accept aliases, and two are always defined: `~sous-shared` for the shared prompts that ship
136
- with sous, and `~project` for the project root. So `@~sous-shared/_partials/sub-agent-delegation.md`
137
- composes a built-in block into your own instruction file, and an `entryGlob` can point at a
138
- built-in skill bundle to compile it into your project. Define your own aliases with an
139
- `_aliases` block to do the same for a team-shared repo.
147
+ A recipe's files are reachable without knowing where anything is installed. An `@include`
148
+ path may name a recipe by its namespace, so
149
+ `@~workflow/task-files/_partials/resume-task.md` composes a block published by the recipe
150
+ `workflow/task-files`, at the version your project has pinned, into your own instruction
151
+ file. `@~project/...` names your project's root, and you can define your own aliases with
152
+ an `_aliases` block.
140
153
 
141
154
  Sous records every file and directory it writes in a state file, `.sous/sous.state.json` by
142
155
  default, which is what lets `prune` and `clear` clean up precisely instead of guessing.
143
156
 
157
+ ## Composing config from layers
158
+
159
+ One config can grow large, so sous lets you split it. Alongside the primary
160
+ `sous.config.*`, any file matching `.sous/conf.d/*.{js,mjs,json,yaml}` is a layer. Sous loads
161
+ the primary first, then the `conf.d/` files sorted bytewise-lexicographically (identical order
162
+ on every machine), and merges them into one config: objects deep-merge key by key, scalars are
163
+ later-wins, and arrays concatenate in load order. Every layer is forced back to plain JSON
164
+ before merging, so functions, `RegExp`, `Date`, and `undefined` do not survive a layer.
165
+
166
+ ```
167
+ .sous/
168
+ sous.config.js # base config
169
+ conf.d/
170
+ 100-tools.json # adds or overrides tools
171
+ 200-skills.yaml # adds compilation targets
172
+ ```
173
+
174
+ For anything JSON cannot express, a `.js`/`.mjs` layer may export a `configure` function
175
+ instead of (or alongside) a `config` object. Sous calls it with the cumulative config so far
176
+ and a small builder, and merges the result:
177
+
178
+ ```js
179
+ export function configure(currentConfig, builder) {
180
+ // currentConfig is the live merged config; mutate it by reference, or return
181
+ // an object to merge. builder.env(name, fallback), builder.loadConfig(path),
182
+ // builder.loadConfigs(glob), and builder.merge(obj) are available.
183
+ currentConfig._vars.apiBase = builder.env("API_BASE", "https://example.com");
184
+ }
185
+ ```
186
+
187
+ `configure` may be async. Builder paths (`loadConfig`/`loadConfigs`) run before variable
188
+ resolution, so they accept only the auto-vars `${sousDir}`, `${sousConfDir}`, `${sousRootPath}`,
189
+ and `${sousVersion}`; any other `${var}` in a builder path is an error.
190
+
191
+ ## Locating the config
192
+
193
+ By default sous walks up from the current directory to find the `.sous/` holding a config.
194
+ You can point it elsewhere with an environment variable or a flag; every command accepts these:
195
+
196
+ | Flag | Env var | What it overrides |
197
+ |---|---|---|
198
+ | `--config` / `-c` / `--sous-config` | `SOUS_CONFIG` | The exact primary config file |
199
+ | `--sous-dir` | `SOUS_DIR` | The `.sous/` directory to use |
200
+ | `--sous-confd` | `SOUS_CONFD` | The `conf.d/` directory (defaults to `<sousDir>/conf.d`) |
201
+
202
+ Precedence, highest first: flag, then env var, then walk-up discovery. These location inputs
203
+ are read from the real environment only, never from `.env` or `.env.local` (those files are
204
+ found by discovery, so they cannot decide where discovery looks).
205
+
206
+ ## Validating and inspecting config
207
+
208
+ `sous config validate` runs the full pipeline: it merges every layer, checks it against the
209
+ schema, then resolves variables, reporting the first failure with a readable message.
210
+ `sous config show` prints the merged config as JSON, and `sous config get <dot.path>` reads a
211
+ single value; add `--layers` to see which layer file set it and to what.
212
+
213
+ Config is validated against a JSON Schema on every load. A config may declare a `version`
214
+ field; omit it or set it to `1`. JSON layers can point an editor at the shipped schema with a
215
+ `"$schema"` key for autocompletion and inline validation:
216
+
217
+ ```json
218
+ {
219
+ "$schema": "./sous.config.schema.json",
220
+ "version": 1
221
+ }
222
+ ```
223
+
144
224
  ## Platform support
145
225
 
146
226
  - **Ubuntu** is where sous is developed and tested.
package/bin/run.js CHANGED
@@ -14,4 +14,13 @@ const { execute, settings } = await import("@oclif/core");
14
14
  // the (unshipped) typescript devDependency is missing.
15
15
  settings.enableAutoTranspile = false;
16
16
 
17
- await execute({ development: true, dir: import.meta.url });
17
+ // oclif's development mode turns on its debug setting, which makes every error
18
+ // it prints a raw stack trace. Sous reports its own errors as sentences (see
19
+ // src/utils/command-errors.ts), so development mode is switched on only when
20
+ // SOUS_DEBUG asks for the traces; anything that gets past a command's own
21
+ // reporting then prints its stack too.
22
+ const debugRequested = !["", "0", "false", "no", "off"].includes(
23
+ (process.env.SOUS_DEBUG ?? "").trim().toLowerCase()
24
+ );
25
+
26
+ await execute({ development: debugRequested, dir: import.meta.url });
@@ -0,0 +1,27 @@
1
+ # Sous Documentation
2
+
3
+ Sous is an agent configuration manager for LLM coding tools: it compiles markdown templates,
4
+ aggregates configuration from many sources, and keeps the files your coding agents rely on
5
+ current. The CLI is called `sous` and ships on npm as
6
+ [`@sous-io/sous`](https://www.npmjs.com/package/@sous-io/sous). Earlier releases installed the
7
+ same CLI under the name `xcv`; every command below is unchanged apart from that name.
8
+
9
+ ```term
10
+ $ npm install -g @sous-io/sous
11
+ >> 100%
12
+ $ sous build
13
+ building "My Project"...
14
+ compiled 4 targets, pruned 1 stale file
15
+ ```
16
+
17
+ ?> These docs are young. **Configuration** and **Repositories** are the reference material so
18
+ far; more will follow.
19
+
20
+ ## Where to look
21
+
22
+ - Watch the [animated introduction](../) for the full pitch
23
+ - Learn [how sous is configured](configuration.md)
24
+ - Share configuration between projects with [repositories](repositories.md)
25
+ - Look up a command in the [command reference](commands.md)
26
+ - Read the [design principles](design-principles.md) that constrain every feature
27
+ - Read the [source on GitHub](https://github.com/sous-io/sous)
@@ -0,0 +1,18 @@
1
+ - [Overview](/)
2
+ - [Design principles](design-principles.md)
3
+ - [Skill categories](skill-categories.md)
4
+ - [Command reference](commands.md)
5
+ - **Configuration**
6
+ - [The config file](configuration.md)
7
+ - [Discovery and overrides](config-discovery.md)
8
+ - [Layers and merging](config-layers.md)
9
+ - [Variables](config-variables.md)
10
+ - [Inspecting and validating](config-inspection.md)
11
+ - **Repositories**
12
+ - [Overview](repositories.md)
13
+ - [Consuming recipes](repositories-consuming.md)
14
+ - [Recipe variables](repositories-variables.md)
15
+ - [Authoring a repository](repositories-authoring.md)
16
+ - [File formats](repositories-file-formats.md)
17
+ - **ADRs**
18
+ - [0001: Repositories](adrs/0001-repositories.md)
@@ -0,0 +1,308 @@
1
+ # Command Reference
2
+
3
+ Every command the `sous` CLI ships, with its arguments and its own flags. Run any of them with
4
+ `--help` for the same information in your terminal.
5
+
6
+ ## The flags every project command shares
7
+
8
+ These four locate the configuration and are accepted by every command that works on a project.
9
+ They are listed once here rather than repeated in every table below.
10
+
11
+ | Flag | What it does |
12
+ |------|--------------|
13
+ | `-c, --config <path>` | Path to a sous config file, or to a directory holding one. Overrides `.sous/` discovery |
14
+ | `--sous-config <path>` | Alias of `--config` |
15
+ | `--sous-dir <path>` | Path to the `.sous` directory to use, overriding walk-up discovery |
16
+ | `--sous-confd <path>` | Path to the `conf.d/` drop-in layer directory, overriding `<sousDir>/conf.d` |
17
+
18
+ The environment variables `SOUS_CONFIG`, `SOUS_DIR` and `SOUS_CONFD` do the same jobs; a flag
19
+ beats the matching variable, and both beat walk-up discovery.
20
+ [Discovery and overrides](config-discovery.md) covers the precedence in full. One more variable,
21
+ `SOUS_DEBUG`, is read by every command: it turns stack traces back on when something fails
22
+ (see [Exit behavior](#exit-behavior)).
23
+
24
+ ?> Three commands take none of these, because they run inside a recipe repository rather than
25
+ inside a project: `sous repo init`, `sous repo release` and `sous repo submit`. A recipe
26
+ repository has no `.sous/` directory to discover. All three do take `--non-interactive`:
27
+ `repo init` and `repo submit` accept it without ever having a question to suppress, and
28
+ `repo release` reads the flag, the terminal and the `CI` environment variable to decide whether
29
+ it may ask, with `--ci` or `--yes` settling it outright.
30
+
31
+ ## Flags common to many commands
32
+
33
+ A few flags mean the same thing wherever they appear, so the tables below name them without
34
+ explaining them again.
35
+
36
+ | Flag | What it does |
37
+ |------|--------------|
38
+ | `-y, --yes` | Answers yes to every confirmation the command would ask. `--force` and `-f` are the same flag; so is `--trust` on the commands that trust a repository |
39
+ | `--non-interactive` | The opposite instruction: never ask anything. A run that would have prompted fails instead, naming the question and the flag that would have answered it |
40
+ | `--dry-run` | Prints what the command would do and writes, downloads and asks nothing |
41
+ | `-h, --help` | Prints the command's own help and exits |
42
+
43
+ `sous clear` is the one command whose primary spelling is `--force` rather than `--yes`, because
44
+ that is the spelling it has always had; `-y` and `--yes` are aliases of it there and behave
45
+ identically. `sous repo init --force` is a different flag with a different meaning (overwrite an
46
+ existing repository), and it is not a confirmation.
47
+
48
+ Help is available in four forms, all of which draw the same screen:
49
+
50
+ ```bash
51
+ sous --help
52
+ sous repo add --help
53
+ sous repo add -h
54
+ sous help repo add
55
+ ```
56
+
57
+ `sous help` on its own lists the topics and commands, and `sous help <topic>` lists one topic's
58
+ commands.
59
+
60
+ ## Singular and plural
61
+
62
+ Every topic answers to both spellings of its name, so nothing hinges on remembering which one
63
+ sous prefers: `repo` and `repos`, `subscription` and `subscriptions`, `namespace` and
64
+ `namespaces`, `recipe` and `recipes`, `lock` and `locks`, `var` and `vars`, `config`
65
+ and `configs`. The tables below print the spelling `sous --help` shows; the other one runs
66
+ exactly the same command.
67
+
68
+ ## Building
69
+
70
+ | Command | Arguments | Own flags |
71
+ |---------|-----------|-----------|
72
+ | `sous build` | none | `--no-prune`, `--no-compile`, `--rebuild`, `--dry-run`, `--strict`, `-w, --watch` |
73
+ | `sous compile` | none | `--strict`, `--rebuild`, `--dry-run`, `-w, --watch` |
74
+ | `sous prune` | none | `--dry-run` |
75
+ | `sous clear` | none | `-f, --force` (also `-y, --yes`) |
76
+ | `sous launch` | `TOOL...` | `--no-build`, `--continuous` |
77
+
78
+ `build` is compile plus prune, and is the command you want almost always. `--rebuild` ignores
79
+ cached hashes and reprocesses every output; `--strict` fails on the first compilation error
80
+ rather than reporting and continuing; `--watch` rebuilds on every change to a source file, a
81
+ config layer, or a linked recipe checkout.
82
+
83
+ `clear` deletes every file and directory sous has written for the project, and asks first unless
84
+ you pass `--force` (or `-y`, or `--yes`). Neither `prune` nor `clear` ever reaches into a linked checkout or the
85
+ machine-wide recipe store.
86
+
87
+ `launch` builds and then spawns a coding agent configured under `tools` in your config. Any
88
+ argument it does not recognize is forwarded to the tool. A flag that collides with one of sous's
89
+ own goes after a bare `--`, which forwards everything following it verbatim:
90
+
91
+ ```bash
92
+ sous launch claude --resume
93
+ sous launch claude -- -c
94
+ ```
95
+
96
+ ## Inspecting the configuration
97
+
98
+ | Command | Arguments | Own flags |
99
+ |---------|-----------|-----------|
100
+ | `sous config show` | none | none |
101
+ | `sous config get` | `PATH` | `--layers` |
102
+ | `sous config validate` | none | none |
103
+
104
+ `show` and `get` write machine-readable output to standard output, with the decorative header and
105
+ any error block routed to standard error, so `sous config show | jq` works even when the config is
106
+ broken. `PATH` is a dot-path with `[n]` for array indices, such as
107
+ `compilation.targets[0].entryPoint`, and `--layers` prints one `old -> new` line per config layer
108
+ that changed the value. `validate` runs the resolvers that schema validation alone cannot,
109
+ surfacing reference cycles and undefined `${var}` references.
110
+
111
+ See [Inspecting and validating](config-inspection.md).
112
+
113
+ ## Repositories
114
+
115
+ | Command | Arguments | Own flags |
116
+ |---------|-----------|-----------|
117
+ | `sous repo add` | `URL` | `--name <name>`, `--provider github\|gitlab\|local`, `-y, --yes` (also `--trust`), `--dry-run` |
118
+ | `sous repo remove` | `REPO` | `-y, --yes` (also `-f, --force`), `--dry-run`, `--no-build` |
119
+ | `sous repo list` | none | `--verbose` |
120
+ | `sous repo search` | `TEXT` | `--limit <n>` (default 25) |
121
+ | `sous repo gc` | none | `--max-bytes <n>`, `--dry-run` |
122
+ | `sous repo link` | `REPO` (a short name, a URL or a path) `[PATH]` | `--global`, `-y, --yes` (also `--trust`), `--dry-run` |
123
+ | `sous repo unlink` | `REPO` | `--global`, `--dry-run` |
124
+
125
+ `repo add` is the trust ceremony; it asks inline, and the confirmation flag is how a run with no
126
+ terminal acknowledges instead. `--trust` is the spelling the ceremony reads best with, and it is
127
+ the same flag as `-y`, `--yes`, `-f` and `--force`. `URL` may be an address or an absolute path to a repository on this
128
+ machine. `list` and `search` read only what is already cached, so both work offline and neither
129
+ downloads anything.
130
+
131
+ `repo remove` is the reverse of `repo add`: it stops trusting a repository. Before it writes
132
+ anything it prints what goes with it, so the decision is made on facts: the entry itself, every
133
+ subscription that resolves into the repository, every locked recipe those subscriptions alone held,
134
+ the output files the next build prunes, and the linked checkout, if one points at it. Then it asks
135
+ once, and the confirmation flag answers ahead of time. The link entry is removed with the
136
+ repository; the checkout itself stays on disk. The command finishes by building the project, the
137
+ same way the subscription commands do, so the files those recipes wrote are gone when it returns;
138
+ `--no-build` leaves the outputs alone. Removing the built-in `sous-recipes` repository records
139
+ `sous-recipes: { enabled: false }` in the managed repositories layer rather than deleting an entry,
140
+ because the entry sous provides comes back on every run.
141
+
142
+ `repo search` is also a top-level `sous search`, because searching is how you find something to
143
+ subscribe to before you know what any of it is called.
144
+
145
+ ## Browsing what a project trusts
146
+
147
+ | Command | Arguments | Own flags |
148
+ |---------|-----------|-----------|
149
+ | `sous namespace list` | none | none |
150
+ | `sous namespace show` | `REF` (a namespace, optionally `repo:namespace`) | none |
151
+ | `sous recipe list` | none | none |
152
+ | `sous recipe show` | `REF` (a recipe, a recipe name on its own, or either with a `repo:` qualifier) | none |
153
+
154
+ All four read the cached repository indexes and the lockfile, so they work offline and download
155
+ nothing. A trusted repository whose index has never been fetched is named at the end of a listing
156
+ rather than left out of it.
157
+
158
+ `namespace list` shows every namespace, how many recipes it holds, and how much of it this project
159
+ subscribes to: the whole namespace, some recipes, or none. `namespace show` adds every recipe in
160
+ one namespace, with the latest published version, the version this project pins, and whether it is
161
+ subscribed.
162
+
163
+ `recipe list` shows the same per-recipe columns across every namespace. `recipe show` describes one
164
+ recipe completely: the repository and its location, every published version labeled as the latest
165
+ one, the pinned one or an earlier one, what the version depends on (both as the recipe's manifest
166
+ declares it and as its repository's index resolved it at release time), the questions it asks with
167
+ the environment variable each answer is stored under, and the directories its files are written
168
+ into. The questions and the file list come from the recipe's own manifest, so a recipe this machine
169
+ does not hold yet is described from its index alone and says so.
170
+
171
+ ## The lockfile
172
+
173
+ | Command | Arguments | Own flags |
174
+ |---------|-----------|-----------|
175
+ | `sous lock show` | none | none |
176
+ | `sous lock rebuild` | none | `--dry-run` |
177
+
178
+ `lock show` prints what `.sous/sous.lock.json` pins: the recipe, the version, the repository it came
179
+ from, and who holds it (this project, or the recipes that require it).
180
+
181
+ `lock rebuild` recomputes the whole file from the subscriptions the config declares and the cached
182
+ indexes, then writes it. It starts from an empty lockfile, so an entry nothing holds any more is
183
+ dropped rather than carried through; that makes it the repair for a file that has drifted from the
184
+ config through a hand edit or a bad merge. It asks nothing and grants no trust: a subscription whose
185
+ closure reaches a repository this project has not added fails, naming the repository. It downloads
186
+ nothing, so a recipe whose files are not on this machine has its own dependencies left out, and is
187
+ named when that happens. `--dry-run` prints the same summary and writes nothing.
188
+
189
+ ## Subscriptions
190
+
191
+ | Command | Arguments | Own flags |
192
+ |---------|-----------|-----------|
193
+ | `sous subscription list` | none | none |
194
+ | `sous subscription add` | `REF` | `--prerelease`, `--always-pull`, `-y, --yes` (also `--trust`), `--accept-first`, `--answer <name>=<value>`, `--answers-file <path>`, `--dry-run`, `--no-build` |
195
+ | `sous subscription remove` | `REF` | `--dry-run`, `--no-build` |
196
+
197
+ `sous subscribe` and `sous unsubscribe` are the original spellings of `subscription add` and
198
+ `subscription remove`, and both still work.
199
+
200
+ Adding or removing a subscription changes what the project compiles, so both commands finish by
201
+ building it: the same compile and prune `sous build` runs, so a newly subscribed recipe's files
202
+ are on disk when the command returns and a removed one's files are gone. `--no-build` changes the
203
+ subscription and leaves the outputs alone. A build that fails leaves the subscription change in
204
+ place, since it is already written and locked, and says so.
205
+
206
+ `REF` is a ref: `namespace`, `namespace/recipe`, either with an `@<range>`, and optionally
207
+ qualified with `repo:`. See
208
+ [Refs: how anything is named](repositories-file-formats.md#refs-how-anything-is-named).
209
+
210
+ `subscription add --dry-run` installs nothing and, after the plan, prints every question the
211
+ recipes would ask: what each variable is for, where its answer would be stored, and whether
212
+ anything answers it already. `--answer <name>=<value>`, repeated, answers those questions ahead of
213
+ time, and `--answers-file <path>` reads the same pairs from a YAML or JSON file; together they are
214
+ how a run with no terminal subscribes to a recipe that asks questions. See
215
+ [Answering questions ahead of time](repositories-consuming.md#answering-questions-ahead-of-time).
216
+
217
+ `subscription list` reads the config and the lockfile only, so it works offline. It reports every
218
+ subscription the project declares, switched-off ones included, with the range it resolves within,
219
+ the versions the lockfile pins for it, where it came from, and whether it is on.
220
+
221
+ Removing the `core` subscription sous provides itself records `core: { enabled: false }` in the
222
+ managed subscriptions layer rather than deleting an entry, because the default would otherwise
223
+ come back on the next run. Adding it back clears the opt-out. Either way the `sous-recipes`
224
+ repository stays trusted and keeps appearing in `repo list` as built in.
225
+
226
+ `repo link` is written three ways. `REPO` on its own, as the short name of a repository this
227
+ project has already added, clones it into `.sous/repos/<owner>/<name>`, or into
228
+ `$SOUS_HOME/repos/<owner>/<name>` with `--global`. `REPO` followed by a `PATH` links the checkout
229
+ at that path to that repository and clones nothing. A path in the `REPO` slot, on its own, links
230
+ the checkout already at that path where it is, under the short name its repo manifest suggests.
231
+ Naming a repository this project has not added, by URL or by path, runs the same trust ceremony
232
+ `repo add` runs, since a linked repository's recipes are read with no version, lockfile or hash
233
+ check; it asks inline, and `--trust` (or any other spelling of the confirmation flag)
234
+ acknowledges instead for a run with no terminal.
235
+
236
+ ## Authoring a repository
237
+
238
+ These three run inside a recipe repository and take none of the config-locating flags.
239
+
240
+ | Command | Arguments | Own flags |
241
+ |---------|-----------|-----------|
242
+ | `sous repo init` | `[DIRECTORY]` | `--name <name>`, `--namespace <name>`, `--force`, `--dry-run` |
243
+ | `sous repo release` | none | `--namespace <ns>`, `--recipe <ns/name>`, `--bump patch\|minor\|major\|prerelease`, `--no-bump`, `--include-unchanged`, `--tag`, `--push`, `--yes`, `--check`, `--ci`, `--dry-run` |
244
+ | `sous repo submit` | none | `--title <text>`, `--body <text>`, `--draft`, `--dry-run` |
245
+
246
+ `sous repo release` plans first, asks once, and then bumps, regenerates the index, commits and
247
+ tags in one run. `--namespace` and `--recipe` are repeatable and narrow the run; `--check` reads
248
+ only and cannot be combined with `--bump`, `--tag` or `--push`; `--ci` is the merge preset and
249
+ implies `--no-bump`. See [Authoring a repository](repositories-authoring.md).
250
+
251
+ ## Variables
252
+
253
+ | Command | Arguments | Own flags |
254
+ |---------|-----------|-----------|
255
+ | `sous vars list` | none | `--file <path>` |
256
+ | `sous vars show` | `NAME` | `--file <path>` |
257
+ | `sous vars ask` | `[NAME]` | `--repo <name>`, `--namespace <name>`, `--var <name>`, `--accept-first`, `--all`, `--file <path>`, `--answer <name>=<value>`, `--answers-file <path>`, `--dry-run` |
258
+
259
+ `vars list` prints every variable in play, with the environment variable that answered each one
260
+ and where the value came from. `vars show` prints one variable in full, including every
261
+ environment variable on the resolution ladder and which rung answered. `NAME` is a bare variable
262
+ name or a full `namespace/recipe.name` key.
263
+
264
+ On `vars ask`, `NAME` is a reference like any other: a variable, an environment variable that
265
+ answers one, a recipe, a namespace or a repository, at any level of qualification, and anything
266
+ larger than a variable asks every question it publishes. `--repo`, `--namespace` and `--var`
267
+ (repeatable) narrow the same way, and `--accept-first` takes the first candidate when the name
268
+ means more than one thing. `--file` reads definitions from a standalone
269
+ definitions file instead of the project's subscribed recipes. `--answer` and `--answers-file`
270
+ answer questions ahead of time, exactly as they do on `subscription add`.
271
+
272
+ Bare `sous vars` is shorthand for `vars list`, and `sous vars <name>` for `vars show <name>`. A
273
+ variable whose name is also a subcommand name (`list`, `show` or `ask`) has to be reached the
274
+ long way, as `sous vars show list`.
275
+
276
+ See [Recipe variables](repositories-variables.md).
277
+
278
+ ## Tables and terminal width
279
+
280
+ Every listing sous prints fits itself to the terminal it is running in. Columns shrink toward
281
+ their minimums, a long description wraps onto more lines, and a path or a URL is cut in the
282
+ middle so the host and the last segment both survive. On a terminal too narrow to hold
283
+ everything, the columns that matter least step aside, and one line under the table names them:
284
+ `Hidden at this width: URL. Widen the terminal to see it.` Nothing is hidden when the output is
285
+ not a terminal (a pipe, a file, a CI log), which is laid out at a fixed width instead, so a
286
+ recorded run always shows every column.
287
+
288
+ `sous repo list --verbose` adds the namespaces each repository publishes, on a dim line under
289
+ that repository's row.
290
+
291
+ ## Exit behavior
292
+
293
+ Every command exits non-zero on a configuration problem and prints a plain-language error block
294
+ naming the file at fault. There is no warn-and-continue: a broken config halts sous rather than
295
+ producing output built on a guess.
296
+
297
+ What a failure prints is the message and nothing else. Forget an argument, misspell a flag, or
298
+ pass a value a flag does not accept, and sous prints the sentence describing the mistake and
299
+ then that command's own help, so the flag you wanted is on the screen already. No expected
300
+ failure prints a stack trace.
301
+
302
+ Set the `SOUS_DEBUG` environment variable to anything but `0`, `false`, `no` or `off` and every
303
+ failure prints its stack trace to standard error underneath the message. It is there for
304
+ debugging sous itself; nothing else changes when it is set.
305
+
306
+ ```bash
307
+ SOUS_DEBUG=1 sous build
308
+ ```
@@ -0,0 +1,74 @@
1
+ # Discovery and Overrides
2
+
3
+ How sous locates a project's configuration, and every way to override it. Discovery runs on
4
+ every command; there is no opt-out.
5
+
6
+ ## Locating the primary config
7
+
8
+ Precedence, highest first. Flags beat env vars; both beat walk-up discovery:
9
+
10
+ 1. `--config <path>` (`-c`), or its verbose alias `--sous-config <path>`
11
+ 2. `SOUS_CONFIG` environment variable
12
+ 3. `--sous-dir <path>` flag
13
+ 4. `SOUS_DIR` environment variable
14
+ 5. Walk UP from the working directory to the filesystem root, taking the first `.sous/`
15
+ directory that holds a primary config. A `.sous/` without one does not stop the walk.
16
+
17
+ A primary config is named `sous.config.js`, `sous.config.mjs`, `sous.config.json`,
18
+ `sous.config.jsonc` or `sous.config.yaml`. The `.jsonc` form is JSON with comments: line comments,
19
+ block comments and trailing commas are all allowed in it.
20
+
21
+ Every flag or env value resolves with the same rules. It may point at:
22
+
23
+ - a config file directly,
24
+ - a directory holding one of the primary config names, or
25
+ - a directory whose `.sous/` child holds one (so `--config .` works from a project root).
26
+
27
+ A leading `~` expands to your home directory. An empty or whitespace-only value (for example a
28
+ bare `export SOUS_CONFIG=`) is treated as unset and falls through to the next tier; it never
29
+ hijacks resolution. Error messages name the source that was actually set (`--sous-dir`,
30
+ `SOUS_CONFIG`, and so on), not a generic flag.
31
+
32
+ ## Locating the conf.d layer directory
33
+
34
+ Precedence: `--sous-confd <path>` flag, then `SOUS_CONFD` env var, then the default
35
+ `<sousDir>/conf.d`. An override flows everywhere the default would: layer enumeration, the
36
+ duplicate-baseName check, and watch-mode reload.
37
+
38
+ ## Env file layering
39
+
40
+ After discovery, sous loads two optional files from the discovered `.sous/` into the process
41
+ environment:
42
+
43
+ - `.env.local`: gitignored; machine-specific values and secrets
44
+ - `.env`: committed; team-shared defaults
45
+
46
+ Load order is `.env.local` first, then `.env`, and no load ever overwrites a value that is
47
+ already set. Effective precedence is therefore: real shell environment, then `.env.local`, then
48
+ `.env`. The syntax is deliberately small (not a shell): `KEY=value` lines, `#` comments, an
49
+ optional `export ` prefix, single or double quoted values (`\n` and `\t` expand inside double
50
+ quotes), and inline `# comment` stripped from unquoted values. Lines without `=` are ignored.
51
+
52
+ !> The location vars (`SOUS_CONFIG`, `SOUS_DIR`, `SOUS_CONFD`) are read from the real shell
53
+ environment only, never from the env files. Finding `.env.local` requires knowing `sousDir`
54
+ first, so setting a location var inside `.env.local` has no effect on discovery.
55
+
56
+ ## Discovery errors
57
+
58
+ All are hard `ConfigError`s; sous never guesses:
59
+
60
+ - **No config found**: the error lists every directory checked during the walk and shows a
61
+ minimal starter config.
62
+ - **Multiple primary configs**: two or more of `sous.config.js|mjs|json|jsonc|yaml` in the same
63
+ `.sous/` is an error naming every candidate.
64
+ - **Duplicate layer baseNames**: any two loaded files (primary or conf.d) whose names differ
65
+ only by extension is an error naming both files, because their merge order would otherwise
66
+ depend on extension. `500-repos.json` and `500-repos.jsonc` collide for the same reason, which
67
+ is what keeps a layer sous is migrating to `.jsonc` from being loaded twice.
68
+
69
+ ## Interaction with `sous launch` pass-through
70
+
71
+ `sous launch` forwards unrecognized arguments to the launched tool. The `--sous-config`,
72
+ `--sous-dir` and `--sous-confd` flags are declared on every command, so launch consumes them
73
+ rather than forwarding. To pass a literally-named flag through to the tool, put it after a bare
74
+ `--`, which forwards everything following it verbatim.