@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.
- package/README.md +121 -35
- package/bin/run.js +10 -1
- package/docs/markdown/README.md +27 -0
- package/docs/markdown/_sidebar.md +18 -0
- package/docs/markdown/commands.md +308 -0
- package/docs/markdown/config-discovery.md +74 -0
- package/docs/markdown/config-inspection.md +69 -0
- package/docs/markdown/config-layers.md +92 -0
- package/docs/markdown/config-variables.md +79 -0
- package/docs/markdown/configuration.md +71 -0
- package/docs/markdown/design-principles.md +59 -0
- package/docs/markdown/repositories-authoring.md +408 -0
- package/docs/markdown/repositories-consuming.md +580 -0
- package/docs/markdown/repositories-file-formats.md +1084 -0
- package/docs/markdown/repositories-variables.md +387 -0
- package/docs/markdown/repositories.md +303 -0
- package/docs/markdown/skill-categories.md +58 -0
- package/package.json +73 -9
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/SKILL.tpl.md +20 -20
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/examples/about-something.md +2 -2
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/examples/do-something.md +1 -1
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/references/advanced-patterns.md +6 -6
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/references/commands.md +5 -5
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/references/frontmatter.md +3 -3
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-liquid-templates/SKILL.tpl.md +40 -25
- package/recipes/core/sous-skills/skills/about-sous/SKILL.tpl.md +70 -0
- package/recipes/core/sous-skills/skills/about-sous-configuration/SKILL.tpl.md +75 -0
- package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/create-skill/SKILL.tpl.md +8 -9
- package/recipes/core/sous-skills/sous.recipe.yaml +45 -0
- package/sous.config.schema.json +337 -0
- package/src/base-command.ts +220 -67
- package/src/commands/build.ts +150 -73
- package/src/commands/clear.ts +23 -15
- package/src/commands/compile.ts +74 -16
- package/src/commands/config/get.ts +110 -0
- package/src/commands/config/show.ts +32 -0
- package/src/commands/config/validate.ts +53 -0
- package/src/commands/help.ts +46 -0
- package/src/commands/launch.ts +36 -14
- package/src/commands/lock/rebuild.ts +241 -0
- package/src/commands/lock/show.ts +115 -0
- package/src/commands/namespace/list.ts +117 -0
- package/src/commands/namespace/show.ts +110 -0
- package/src/commands/prune.ts +3 -11
- package/src/commands/recipe/list.ts +95 -0
- package/src/commands/recipe/show.ts +301 -0
- package/src/commands/repo/add.ts +145 -0
- package/src/commands/repo/gc.ts +172 -0
- package/src/commands/repo/init.ts +136 -0
- package/src/commands/repo/link.ts +500 -0
- package/src/commands/repo/list.ts +179 -0
- package/src/commands/repo/release.ts +619 -0
- package/src/commands/repo/remove.ts +193 -0
- package/src/commands/repo/search.ts +189 -0
- package/src/commands/repo/submit.ts +133 -0
- package/src/commands/repo/unlink.ts +147 -0
- package/src/commands/subscription/add.ts +285 -0
- package/src/commands/subscription/list.ts +129 -0
- package/src/commands/subscription/remove.ts +181 -0
- package/src/commands/vars/ask.ts +374 -0
- package/src/commands/vars/index.ts +79 -0
- package/src/commands/vars/list.ts +67 -0
- package/src/commands/vars/show.ts +77 -0
- package/src/config-command.ts +30 -0
- package/src/lib/build-service.ts +206 -54
- package/src/lib/config-discovery.ts +220 -27
- package/src/lib/config-inspect.ts +145 -0
- package/src/lib/config-kernel.mjs +377 -0
- package/src/lib/config-schema.ts +361 -0
- package/src/lib/env-file.ts +328 -0
- package/src/lib/env-local.ts +18 -1
- package/src/lib/errors.ts +32 -0
- package/src/lib/include-resolver.ts +108 -15
- package/src/lib/interactive.ts +165 -0
- package/src/lib/markdown-compiler.ts +118 -37
- package/src/lib/package-info.ts +25 -0
- package/src/lib/pid-service.ts +32 -21
- package/src/lib/refs/find.ts +589 -0
- package/src/lib/refs/index.ts +12 -0
- package/src/lib/refs/pick.ts +147 -0
- package/src/lib/refs/scopes.ts +61 -0
- package/src/lib/repos/catalog-display.ts +116 -0
- package/src/lib/repos/catalog-inputs.ts +160 -0
- package/src/lib/repos/catalog.ts +722 -0
- package/src/lib/repos/core-recipe.ts +105 -0
- package/src/lib/repos/defaults.ts +175 -0
- package/src/lib/repos/formats/common.ts +389 -0
- package/src/lib/repos/formats/index-file.ts +215 -0
- package/src/lib/repos/formats/links-map.ts +96 -0
- package/src/lib/repos/formats/lockfile.ts +167 -0
- package/src/lib/repos/formats/patterns.ts +57 -0
- package/src/lib/repos/formats/recipe-manifest.ts +395 -0
- package/src/lib/repos/formats/repo-manifest.ts +88 -0
- package/src/lib/repos/formats/store-entry.ts +84 -0
- package/src/lib/repos/freshness.ts +208 -0
- package/src/lib/repos/git-clone.ts +312 -0
- package/src/lib/repos/identity.ts +89 -0
- package/src/lib/repos/index.ts +58 -0
- package/src/lib/repos/links.ts +353 -0
- package/src/lib/repos/load-manifest.ts +236 -0
- package/src/lib/repos/lock-service.ts +453 -0
- package/src/lib/repos/locked-namespace-resolver.ts +90 -0
- package/src/lib/repos/locked-recipes.ts +254 -0
- package/src/lib/repos/managed-layer.ts +422 -0
- package/src/lib/repos/namespace-resolver.ts +370 -0
- package/src/lib/repos/providers/base.ts +206 -0
- package/src/lib/repos/providers/git.ts +233 -0
- package/src/lib/repos/providers/github.ts +294 -0
- package/src/lib/repos/providers/gitlab.ts +263 -0
- package/src/lib/repos/providers/http.ts +102 -0
- package/src/lib/repos/providers/index-cache.ts +382 -0
- package/src/lib/repos/providers/index.ts +106 -0
- package/src/lib/repos/providers/local.ts +391 -0
- package/src/lib/repos/providers/provider.ts +401 -0
- package/src/lib/repos/recipe-config-layers.ts +287 -0
- package/src/lib/repos/recipe-targets.ts +223 -0
- package/src/lib/repos/ref-search.ts +46 -0
- package/src/lib/repos/ref.ts +513 -0
- package/src/lib/repos/reference-report.ts +122 -0
- package/src/lib/repos/release/bump.ts +161 -0
- package/src/lib/repos/release/git-state.ts +305 -0
- package/src/lib/repos/release/index-builder.ts +635 -0
- package/src/lib/repos/release/index.ts +16 -0
- package/src/lib/repos/release/plan.ts +512 -0
- package/src/lib/repos/release/submit-service.ts +496 -0
- package/src/lib/repos/release/tags.ts +243 -0
- package/src/lib/repos/release/validate.ts +463 -0
- package/src/lib/repos/resolver.ts +789 -0
- package/src/lib/repos/scaffold/index.ts +238 -0
- package/src/lib/repos/scaffold/templates.ts +413 -0
- package/src/lib/repos/seed.ts +414 -0
- package/src/lib/repos/store/contract.ts +64 -0
- package/src/lib/repos/store/hash.ts +114 -0
- package/src/lib/repos/store/recipe-store.ts +599 -0
- package/src/lib/repos/store/settings.ts +58 -0
- package/src/lib/repos/subscription-service.ts +2678 -0
- package/src/lib/repos/trust.ts +447 -0
- package/src/lib/settings.ts +546 -189
- package/src/lib/sous-home.ts +104 -0
- package/src/lib/state.ts +52 -20
- package/src/lib/vars/ask.ts +1152 -0
- package/src/lib/vars/definition-source.ts +252 -0
- package/src/lib/vars/display.ts +233 -0
- package/src/lib/vars/index.ts +18 -0
- package/src/lib/vars/ladder.ts +282 -0
- package/src/lib/vars/mappings.ts +265 -0
- package/src/lib/vars/names.ts +94 -0
- package/src/lib/vars/preanswers.ts +395 -0
- package/src/lib/vars/question-plan.ts +218 -0
- package/src/lib/vars/report.ts +228 -0
- package/src/lib/vars/safe-regex.ts +235 -0
- package/src/lib/vars/validate.ts +312 -0
- package/src/lib/watch-loop.ts +148 -0
- package/src/templating/init-liquid-engine.ts +58 -16
- package/src/utils/choice-prompt.ts +143 -0
- package/src/utils/command-errors.ts +186 -0
- package/src/utils/command-help.ts +45 -0
- package/src/utils/confirm-prompt.ts +110 -0
- package/src/utils/flags.ts +153 -0
- package/src/utils/formatting.ts +540 -55
- package/src/utils/prompts.ts +35 -1
- package/src/utils/sous-directory.ts +245 -0
- package/src/utils/table.ts +603 -0
- package/src/utils/value-prompt.ts +119 -0
- package/bin/xcv +0 -5
- package/shared-prompts/_partials/resume-task.md +0 -51
- package/shared-prompts/_partials/sub-agent-delegation.md +0 -32
- package/shared-prompts/_partials/update-task-file.md +0 -52
- package/shared-prompts/memories/automated-browser-tasks/INDEX.tpl.md +0 -52
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/SKILL.tpl.md +0 -102
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/auth-failure-handling.mjs +0 -81
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/chained-workflow.mjs +0 -126
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/simple-fetch.mjs +0 -92
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/architecture.md +0 -61
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/auth-and-sessions.md +0 -65
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/ctx-api.md +0 -96
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/installation.md +0 -104
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/script-conventions.md +0 -243
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/chrome-state.mjs +0 -148
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/debug.mjs +0 -383
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/debug.spec.mjs +0 -267
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/eslint.config.mjs +0 -56
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/harness.mjs +0 -169
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/keyring.mjs +0 -59
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/logger.mjs +0 -25
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/params.mjs +0 -140
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/run.mjs +0 -140
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/settings.tpl.mjs +0 -1
- package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/utils.mjs +0 -185
- package/shared-prompts/skills/automated-browser-tasks/create-automated-browser-task/SKILL.tpl.md +0 -52
- package/shared-prompts/skills/automated-browser-tasks/running-automated-browser-tasks/SKILL.tpl.md +0 -59
- package/shared-prompts/skills/automated-browser-tasks/update-automated-browser-task/SKILL.tpl.md +0 -47
- package/shared-prompts/skills/control-flow/approve/SKILL.tpl.md +0 -26
- package/shared-prompts/skills/control-flow/opine/SKILL.tpl.md +0 -58
- package/shared-prompts/skills/control-flow/repeat/SKILL.tpl.md +0 -27
- package/shared-prompts/skills/control-flow/research/SKILL.tpl.md +0 -34
- package/shared-prompts/skills/sous-skills/about-sous/SKILL.tpl.md +0 -51
- package/shared-prompts/skills/task-files/about-task-files/SKILL.tpl.md +0 -122
- package/shared-prompts/skills/task-files/continue-task-in-new-branch/SKILL.tpl.md +0 -80
- package/shared-prompts/skills/task-files/go/SKILL.tpl.md +0 -14
- package/shared-prompts/skills/task-files/resume-task/SKILL.tpl.md +0 -13
- package/shared-prompts/skills/task-files/start-task/SKILL.tpl.md +0 -93
- package/shared-prompts/skills/task-files/update/SKILL.tpl.md +0 -14
- package/shared-prompts/skills/task-files/update-task-file/SKILL.tpl.md +0 -13
- /package/{shared-prompts/skills/sous-skills → recipes/core/sous-skills/skills}/about-agent-skills/references/substitutions.md +0 -0
- /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.*
|