@sous-io/sous 0.1.0 → 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 (206) hide show
  1. package/README.md +121 -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 +73 -9
  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/bin/xcv +0 -5
  166. package/shared-prompts/_partials/resume-task.md +0 -51
  167. package/shared-prompts/_partials/sub-agent-delegation.md +0 -32
  168. package/shared-prompts/_partials/update-task-file.md +0 -52
  169. package/shared-prompts/memories/automated-browser-tasks/INDEX.tpl.md +0 -52
  170. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/SKILL.tpl.md +0 -102
  171. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/auth-failure-handling.mjs +0 -81
  172. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/chained-workflow.mjs +0 -126
  173. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/simple-fetch.mjs +0 -92
  174. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/architecture.md +0 -61
  175. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/auth-and-sessions.md +0 -65
  176. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/ctx-api.md +0 -96
  177. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/installation.md +0 -104
  178. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/script-conventions.md +0 -243
  179. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/chrome-state.mjs +0 -148
  180. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/debug.mjs +0 -383
  181. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/debug.spec.mjs +0 -267
  182. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/eslint.config.mjs +0 -56
  183. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/harness.mjs +0 -169
  184. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/keyring.mjs +0 -59
  185. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/logger.mjs +0 -25
  186. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/params.mjs +0 -140
  187. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/run.mjs +0 -140
  188. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/settings.tpl.mjs +0 -1
  189. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/utils.mjs +0 -185
  190. package/shared-prompts/skills/automated-browser-tasks/create-automated-browser-task/SKILL.tpl.md +0 -52
  191. package/shared-prompts/skills/automated-browser-tasks/running-automated-browser-tasks/SKILL.tpl.md +0 -59
  192. package/shared-prompts/skills/automated-browser-tasks/update-automated-browser-task/SKILL.tpl.md +0 -47
  193. package/shared-prompts/skills/control-flow/approve/SKILL.tpl.md +0 -26
  194. package/shared-prompts/skills/control-flow/opine/SKILL.tpl.md +0 -58
  195. package/shared-prompts/skills/control-flow/repeat/SKILL.tpl.md +0 -27
  196. package/shared-prompts/skills/control-flow/research/SKILL.tpl.md +0 -34
  197. package/shared-prompts/skills/sous-skills/about-sous/SKILL.tpl.md +0 -51
  198. package/shared-prompts/skills/task-files/about-task-files/SKILL.tpl.md +0 -122
  199. package/shared-prompts/skills/task-files/continue-task-in-new-branch/SKILL.tpl.md +0 -80
  200. package/shared-prompts/skills/task-files/go/SKILL.tpl.md +0 -14
  201. package/shared-prompts/skills/task-files/resume-task/SKILL.tpl.md +0 -13
  202. package/shared-prompts/skills/task-files/start-task/SKILL.tpl.md +0 -93
  203. package/shared-prompts/skills/task-files/update/SKILL.tpl.md +0 -14
  204. package/shared-prompts/skills/task-files/update-task-file/SKILL.tpl.md +0 -13
  205. /package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/references/substitutions.md +0 -0
  206. /package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-liquid-templates/references/liquid-filters.md +0 -0
@@ -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.
@@ -0,0 +1,69 @@
1
+ # Inspecting and Validating
2
+
3
+ ## The validation pipeline
4
+
5
+ Every command validates the config on load, in this order:
6
+
7
+ 1. The kernel merges all layers (any layer failure names the file).
8
+ 2. The legacy-schema guard rejects the removed multi-project keys (`projects`,
9
+ `defaultProject`) with a migration message.
10
+ 3. A strict schema validates the merged result: unknown keys at ANY level are rejected with the
11
+ full path and a typo hint; a target must have exactly one of `entryPoint` / `entryGlob`;
12
+ `version`, when present, must be `1` (anything else fails as unsupported by this sous
13
+ version).
14
+ 4. Variable resolution and substitution run as values are used; `sous config validate` runs them
15
+ eagerly (below).
16
+
17
+ Validation applies to the MERGED config only. A `conf.d/` fragment on its own does not need to
18
+ be a complete config; it only needs to use schema-known keys.
19
+
20
+ ## The JSON Schema artifact
21
+
22
+ Sous ships `sous.config.schema.json` at the package root (generated from the same schema that
23
+ validates at load time). A JSON config can bind it for editor autocompletion and external
24
+ tooling:
25
+
26
+ ```json
27
+ { "$schema": "./path/to/sous.config.schema.json" }
28
+ ```
29
+
30
+ The `$schema` key is accepted at the top level and ignored by sous itself.
31
+
32
+ ## sous config show
33
+
34
+ Prints the full merged config as pretty JSON: the config as written, after layer merging but
35
+ BEFORE variable resolution. Output is machine-readable by contract: the decorative header and
36
+ any error text go to stderr, so `sous config show | jq .` always receives either valid JSON or
37
+ nothing. Colorized only when stdout is a TTY.
38
+
39
+ ## sous config get
40
+
41
+ Prints one value by dot-path, with `[n]` for array indices:
42
+
43
+ ```bash
44
+ sous config get name
45
+ sous config get compilation.targets[0].entryPoint
46
+ ```
47
+
48
+ Scalars print raw (no quotes); objects and arrays print as pretty JSON. A missing path exits
49
+ non-zero with a message on stderr.
50
+
51
+ `--layers` adds provenance: for each loaded layer whose cumulative merge CHANGED the value at
52
+ that path, one line prints the layer file and the old and new values, starting from `(unset)`.
53
+ This answers "which file set this?" directly:
54
+
55
+ ```bash
56
+ sous config get tools.claude.command --layers
57
+ ```
58
+
59
+ ## sous config validate
60
+
61
+ Runs the whole pipeline eagerly: discovery, kernel merge, legacy guard, schema, then FULL
62
+ variable resolution including compilation, tools and watch config. This surfaces the errors
63
+ schema validation alone cannot: variable cycles, undefined `${refs}`, and bad substitutions in
64
+ entry points and destinations. On success it prints a short summary (config file, layer count,
65
+ target count, tools) and exits 0; on failure it prints the `ConfigError` and exits non-zero.
66
+
67
+ All three commands honor the same config-locating flags and env vars as every other command
68
+ (`--config`/`-c`/`--sous-config`, `--sous-dir`, `--sous-confd`, `SOUS_CONFIG`, `SOUS_DIR`,
69
+ `SOUS_CONFD`); see [Discovery and overrides](config-discovery.md).
@@ -0,0 +1,92 @@
1
+ # Layers and Merging
2
+
3
+ Every config source (the primary file plus each `conf.d/` layer) is loaded by one subprocess,
4
+ the config kernel, which merges everything into a single cumulative config and returns it as
5
+ plain JSON.
6
+
7
+ ## Load order
8
+
9
+ 1. The primary `sous.config.*` file.
10
+ 2. Every `*.js|mjs|json|jsonc|yaml` file directly inside `conf.d/` (non-recursive), sorted BYTEWISE
11
+ by filename. The sort is locale-independent and per-machine stable, but it is not numeric:
12
+ `10-a.json` sorts before `2-b.json` because the character `1` precedes `2`. Zero-pad numeric
13
+ prefixes (`02-`, `10-`) when ordering matters.
14
+
15
+ A `.jsonc` layer is JSON with comments: line comments, block comments and trailing commas are all
16
+ allowed in it, and it merges exactly like a `.json` one. The layers sous manages for you are
17
+ written that way, so each can say in the file itself what it holds.
18
+
19
+ ## JSON forcing
20
+
21
+ Each layer's contribution passes through a JSON round-trip BEFORE merging:
22
+
23
+ - Functions and `undefined` values are dropped.
24
+ - `RegExp` becomes `{}`; `Date` becomes its ISO string.
25
+ - The final merged config is round-tripped once more on the way out.
26
+
27
+ Write configs as plain data; behavior belongs in `configure()` (below), which runs inside the
28
+ kernel before serialization.
29
+
30
+ ## Deep merge semantics
31
+
32
+ Applied key by key when a later layer meets the cumulative config:
33
+
34
+ - plain object + plain object: recurse
35
+ - array + array: CONCATENATE, cumulative first, layer second. No dedupe; loading the same
36
+ fragment twice duplicates its array entries. There is no declarative way to remove or replace
37
+ an array entry; use `configure()` and mutate directly when you need that.
38
+ - anything else (scalar, mixed types, null): the later layer replaces
39
+ - an own `__proto__`, `constructor`, or `prototype` key in layer data is skipped
40
+ (prototype-pollution guard)
41
+
42
+ ## The JS/MJS layer contract
43
+
44
+ A `.js` or `.mjs` layer may export a config object, a configure function, or both:
45
+
46
+ - **Object**: `export const config = {...}`, or a default export that is a non-function object.
47
+ Merged first.
48
+ - **Function**: `export function configure(currentConfig, builder)`, or a default export that is
49
+ a function. Runs AFTER the object (if any) merges. It may be async and is awaited. It may
50
+ mutate `currentConfig` by reference freely. A returned object is merged after it resolves,
51
+ UNLESS the return value IS `currentConfig` itself (the mutate-and-return-for-chaining idiom),
52
+ which is skipped so arrays are not duplicated by a self-merge.
53
+
54
+ ## The builder
55
+
56
+ `configure` receives a builder with:
57
+
58
+ - `builder.config`: the live cumulative config (same object as `currentConfig`)
59
+ - `builder.sousDir`, `builder.confDir`: resolved directories
60
+ - `builder.currentFile`: absolute path of the layer being loaded
61
+ - `builder.env(name, fallback)`: read an environment variable (env files are already loaded at
62
+ this point)
63
+ - `builder.merge(obj)`: JSON-force `obj` and deep-merge it into the cumulative config
64
+ - `builder.loadConfig(path)`: load another file (any supported extension) under the full layer
65
+ contract, including its own nested `configure`. Async.
66
+ - `builder.loadConfigs(globPattern)`: glob, sort matches bytewise, and load each. Async.
67
+
68
+ Builder path arguments resolve BEFORE variable resolution, so user `_vars` do not exist yet.
69
+ Only the auto-vars may appear in them: `${sousDir}`, `${sousConfDir}`, `${sousRootPath}`,
70
+ `${sousVersion}`. Any other `${name}` in a builder path is a hard error listing the allowed
71
+ names. Relative paths resolve against the directory of `builder.currentFile`. Load cycles (a
72
+ file loading itself, directly or indirectly) are detected and reported.
73
+
74
+ ## Failure behavior
75
+
76
+ Any layer failure (a JSON/YAML parse error, an import error, a `configure` throw, a load cycle,
77
+ the legacy multi-project schema) halts the whole load with an error naming the layer file. There
78
+ is no warn-and-skip; a broken layer never silently drops out of the merge.
79
+
80
+ ## The managed 5xx layer band
81
+
82
+ `conf.d/500-*` through `conf.d/599-*` is reserved for layers the sous CLI writes on your behalf.
83
+ They are `.jsonc`, and each one opens with a header comment saying what it holds.
84
+
85
+ **Sous edits these files by key; you may edit them too.** A write rewrites only the bytes of the
86
+ entry that changes, so your comments, your key order and your formatting survive it, and repeated
87
+ edits produce minimal version-control diffs. Sous never edits a hand-written primary config or a
88
+ layer outside the 5xx band. To override a managed value rather than change it in place, add a
89
+ higher-sorting layer (for example `conf.d/600-overrides.json`).
90
+
91
+ Where a comment is impossible because a format really is strict JSON, the convention is a `//`
92
+ key, which sous ignores wherever it appears.
@@ -0,0 +1,79 @@
1
+ # Variables
2
+
3
+ How `${var}` references resolve inside a sous config.
4
+
5
+ ?> Template files use LiquidJS double-brace syntax instead; that is a different stage entirely.
6
+ This page is about the config side only.
7
+
8
+ ## The scope chain
9
+
10
+ Later scopes override earlier ones:
11
+
12
+ ```
13
+ auto-vars -> _env scope -> _vars -> compilation._vars -> target._vars -> output._vars
14
+ ```
15
+
16
+ Nested `_vars` blocks (on `compilation`, on a target, on an output) resolve with their parent
17
+ scope inherited, so a target var may reference a top-level var, and an output var may override
18
+ both for that output only.
19
+
20
+ ## Auto-vars
21
+
22
+ Always injected first, before `_env` and `_vars`:
23
+
24
+ | Variable | Value |
25
+ |----------|-------|
26
+ | `sousDir` | the discovered `.sous/` directory holding the active config |
27
+ | `sousConfDir` | the `conf.d/` drop-in directory for the active config |
28
+ | `sousConfigPath` | absolute path of the active primary config file |
29
+ | `sousRootPath` | absolute path of the sous CLI install directory |
30
+ | `sousVersion` | the sous CLI version string |
31
+ | `sousTemplatePath` | absolute path of the `.tpl.` file being rendered (render time only) |
32
+ | `sousTemplateDir` | directory of the `.tpl.` file being rendered (render time only) |
33
+
34
+ The `sous*` namespace is reserved: defining any var whose name starts with `sous` earns a
35
+ warning and risks colliding with a future auto-var.
36
+
37
+ ## _env
38
+
39
+ The top-level-only `_env` block maps config var names to environment variable names:
40
+ `_env: { userHome: "HOME" }` makes `${userHome}` available everywhere. Values come from the real
41
+ shell environment, `.sous/.env.local`, or `.sous/.env` (first writer wins, in that order). A
42
+ mapped env var that is not set anywhere is a hard `ConfigError` telling you which file to define
43
+ it in.
44
+
45
+ ## Fixpoint resolution
46
+
47
+ Each `_vars` block resolves by a fixpoint loop, not top-to-bottom: every round re-scans the
48
+ still-unresolved entries and finalizes any whose `${refs}` all resolve, repeating until a round
49
+ finalizes nothing. Declaration order therefore never matters; `{ file: "${root}/x", root:
50
+ "/data" }` resolves as readily as the reverse.
51
+
52
+ If entries remain unresolved when progress stops, that is a hard `ConfigError`. The message
53
+ separates:
54
+
55
+ - **Cycles**: entries that reference each other (members are named), and
56
+ - **Undefined references**: `${names}` defined nowhere (each is named along with the entries
57
+ that need it),
58
+
59
+ and then lists the variables that ARE in scope. There is no silent fallthrough: an unresolved
60
+ `${var}` never survives into a value sous acts on. Entry points, destinations, prompt files and
61
+ similar action values additionally pass a strict substitution that fails on any leftover
62
+ reference.
63
+
64
+ ## Path normalization
65
+
66
+ Absolute `entryPoint`, `entryGlob`, `globBase`, `destinationFile` and `destinationDir` values
67
+ are normalized after substitution, so the common `${sousDir}/..` idiom collapses to the real
68
+ parent directory. This matters: prune compares tracked outputs against `destinationDir` by
69
+ string prefix, and watch mode matches event paths against watched entries; both need the
70
+ normalized form.
71
+
72
+ ## Special variables sous reads
73
+
74
+ Two ordinary `_vars` entries change sous's own behavior when present:
75
+
76
+ - `stateFilePath`: overrides where the build state file is written (default
77
+ `<sousDir>/sous.state.json`)
78
+ - `pidFilePath`: overrides where the watch-mode PID file is written (default
79
+ `<sousDir>/sous.pid`)
@@ -0,0 +1,71 @@
1
+ # Configuration
2
+
3
+ Sous reads one configuration per project from the project's `.sous/` directory. One config
4
+ describes one project: every setting lives at the top level of a single flat object. There is no
5
+ user-level config (nothing is read from `~/.sous`) and no multi-project map.
6
+
7
+ ## The config file
8
+
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
11
+ error, never a silent first-match. A typical config:
12
+
13
+ ```js
14
+ export const config = {
15
+ version: 1, // optional; must be 1 when present
16
+ name: "My Project", // optional display name
17
+ _env: { userHome: "HOME" }, // map config vars from env vars (top-level only)
18
+ _vars: {
19
+ projectRoot: "${sousDir}/..", // ${sousDir} is the discovered .sous/ dir
20
+ docsDir: "${projectRoot}/docs", // declaration order never matters
21
+ },
22
+ _aliases: { myDocs: "${docsDir}" }, // extra @include bases (optional)
23
+ compilation: {
24
+ targets: [
25
+ {
26
+ entryPoint: "${projectRoot}/prompts/AGENTS.md",
27
+ outputs: [{ destinationFile: "${projectRoot}/AGENTS.md" }],
28
+ },
29
+ {
30
+ entryGlob: "${projectRoot}/prompts/skills/**/*.md",
31
+ outputs: [{ destinationDir: "${projectRoot}/.claude/skills" }],
32
+ },
33
+ ],
34
+ },
35
+ tools: {
36
+ claude: { command: "claude", promptFile: "${projectRoot}/CLAUDE.md" },
37
+ },
38
+ };
39
+ ```
40
+
41
+ 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.
43
+
44
+ ?> Configs use `${var}` syntax. Template files use LiquidJS double-brace syntax instead; the two
45
+ are resolved at different stages and never mix.
46
+
47
+ ## Composition
48
+
49
+ The primary config is optionally extended by drop-in layers: every `*.js|mjs|json|jsonc|yaml` file
50
+ directly inside `.sous/conf.d/` is loaded after the primary config and deep-merged over it. A
51
+ `.js`/`.mjs` config or layer can also compose programmatically through a `configure()` function.
52
+ [Layers and merging](config-layers.md) covers the full semantics.
53
+
54
+ ## Where to go next
55
+
56
+ - [Discovery and overrides](config-discovery.md): how sous finds the config, and the flags and
57
+ environment variables that override it
58
+ - [Layers and merging](config-layers.md): `conf.d/` ordering, merge semantics, the JS
59
+ `configure()` contract
60
+ - [Variables](config-variables.md): the `${var}` scope chain, auto-vars, fixpoint resolution
61
+ - [Inspecting and validating](config-inspection.md): the `sous config` commands and the
62
+ validation pipeline
63
+
64
+ ## Rules of the road
65
+
66
+ - Hand-written config belongs in the primary file or your own `conf.d/` layers. The
67
+ `conf.d/500-*` through `conf.d/599-*` band is reserved for layers the sous CLI itself writes.
68
+ Sous edits those by key, so you may edit them too and your comments, key order and formatting
69
+ survive; put a layer of your own outside the band.
70
+ - Any config problem halts sous with a `ConfigError` naming the offending file; there is no
71
+ warn-and-continue.
@@ -0,0 +1,59 @@
1
+ # Design Principles
2
+
3
+ These principles govern every feature sous ships. They are constraints on design, not
4
+ aspirations; when a proposal violates one, the proposal changes.
5
+
6
+ ## 1. Augment, don't compete
7
+
8
+ When a coding tool can do something natively, do it natively; sous exists for what the tool
9
+ cannot do. Sous never wraps, replaces, or re-implements a capability an agent harness already
10
+ provides well.
11
+
12
+ *Example: sous compiles `CLAUDE.md` and skill files because no harness composes them from
13
+ shared, templated sources; it does not try to replace how the harness loads them.*
14
+
15
+ ## 2. Fill the collective gaps
16
+
17
+ Sous actively tries to avoid competing with any tool, and especially with any class of tools.
18
+ Instead it fills the gaps that most or all tools either cannot or will not fill, because those
19
+ problems span every provider at once.
20
+
21
+ *Example: keeping one instruction source current across Claude, Codex, and whatever comes next
22
+ is nobody's product; it is sous's whole job.*
23
+
24
+ ## 3. Be a tool, not a framework
25
+
26
+ Sous avoids forcing opinions; that is different from having none. Beyond the few core skills
27
+ that teach agents how to work with sous itself, every built-in is opt-in only, and even the
28
+ core is opt-out-able.
29
+
30
+ *Example: the core skills arrive by default because agents need them to avoid editing
31
+ generated files, but a single explicit setting removes them entirely.*
32
+
33
+ ## 4. Be easy to enter
34
+
35
+ Sous copies plain files just as readily as it renders templates, so adopting it does not
36
+ require converting anything. Existing marketplaces and installers keep working, pointed at a
37
+ sous-managed directory.
38
+
39
+ *Example: drop an existing skill directory into a sous target unchanged; it compiles verbatim
40
+ on the next build.*
41
+
42
+ ## 5. Be easy to exit
43
+
44
+ Leaving sous must never cost more than a commit. The generated output is complete and ordinary;
45
+ commit it and delete `.sous`, and the project keeps working exactly as it did.
46
+
47
+ *Example: every file sous writes is a normal file in its final location; nothing references
48
+ sous at runtime.*
49
+
50
+ ## 6. Maximum user control
51
+
52
+ Everything gets an escape hatch, within practicality. The happy path stays maximally simple;
53
+ the escape hatches are advanced usage and may require the docs, but they must exist and they
54
+ still get thoughtful ergonomics. Projects control everything granularly, and an individual
55
+ user can override every project decision through their own configuration layers.
56
+
57
+ *Example: subscribing to a recipe is one line and pulls in everything the recipe provides;
58
+ excluding one skill from that subscription may take a documented setting, but the setting is
59
+ there.*