@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
@@ -0,0 +1,408 @@
1
+ # Authoring a Repository
2
+
3
+ This is the guide to publishing recipes of your own: creating a repository, writing a recipe,
4
+ editing one that is already published, cutting a release, and proposing a change to someone
5
+ else's repository. [Repository file formats](repositories-file-formats.md) holds every schema
6
+ these commands read and write; this page is about the workflow.
7
+
8
+ ?> A recipe repository is not a sous project. It has no `.sous/` directory, and `sous repo init`,
9
+ `sous repo release` and `sous repo submit` do not look for one. Run them from inside the
10
+ repository itself.
11
+
12
+ ## Create a repository
13
+
14
+ ```term
15
+ $ sous repo init ./my-recipes --name my-recipes --namespace workflow
16
+ wrote sous.repo.yaml
17
+ wrote sous.index.json
18
+ wrote recipes/workflow/example/sous.recipe.yaml
19
+ wrote recipes/workflow/example/skills/example-skill/SKILL.md
20
+ wrote README.md
21
+ wrote .github/workflows/sous-release.yml
22
+ wrote .gitignore
23
+ ```
24
+
25
+ | Flag | What it does |
26
+ |------|--------------|
27
+ | `--name <name>` | Short name for the repository. Defaults to the directory's own name |
28
+ | `--namespace <name>` | The one namespace to declare. Defaults to the repository's name |
29
+ | `--force` | Write the scaffold over a repository that already exists |
30
+ | `--dry-run` | Print the files that would be written without writing them |
31
+
32
+ Names are lowercase kebab-case, and a name given in any other case is lowercased for you, so a
33
+ directory called `My-Recipes` yields `my-recipes`. `repo init` refuses to write over a directory
34
+ that already holds a repo manifest unless you pass `--force`, and it reads every manifest back
35
+ after writing it, so the scaffold it leaves behind is one that validates.
36
+
37
+ ?> `repo init --force` means overwrite, not "answer yes". It is unrelated to the shared
38
+ confirmation flag other commands spell `--yes`, `-y`, `--force` or `--trust`, and `repo init`
39
+ does not accept those other spellings.
40
+
41
+ ## The layout
42
+
43
+ ```text
44
+ my-recipes/
45
+ sous.repo.yaml what this repository publishes
46
+ sous.index.json the catalog, written by 'sous repo release'
47
+ recipes/
48
+ workflow/
49
+ example/
50
+ sous.recipe.yaml one recipe, with its own version
51
+ skills/
52
+ example-skill/
53
+ SKILL.md
54
+ .github/workflows/sous-release.yml
55
+ .gitignore
56
+ README.md
57
+ ```
58
+
59
+ Nothing in that tree is fixed except the two manifest filenames and the index at the root.
60
+ Recipe folders may live anywhere; what makes a folder a recipe is that `sous.repo.yaml` lists its
61
+ path under `recipes`, and that the folder holds exactly one recipe manifest.
62
+
63
+ Both hand-written manifests are YAML or JSON and never JavaScript. A repository's whole trust
64
+ story rests on being readable without running any of its code, and a manifest that could execute
65
+ would break that guarantee.
66
+
67
+ ## Write a recipe
68
+
69
+ Copy the example folder, edit its manifest, and add the new path to the `recipes` list in
70
+ `sous.repo.yaml`. A minimal recipe:
71
+
72
+ ```yaml
73
+ formatVersion: 1
74
+ namespace: workflow
75
+ name: task-files
76
+ version: 0.1.0
77
+ description: >-
78
+ Per-branch task files, with skills for starting and resuming work.
79
+
80
+ contents:
81
+ - kind: skills
82
+ include:
83
+ - skills/**/*.md
84
+ ```
85
+
86
+ `contents` groups are what a subscriber's project actually receives, one group per kind
87
+ (`skills`, `memories`, `prompts` or `config`), each with `include` glob patterns relative to the
88
+ recipe folder. Two more optional lists say what else the recipe needs: `depends` for build
89
+ dependencies whose files stay out of a subscriber's output, and `subscribes` for co-subscriptions
90
+ whose files go in. Full field tables are in
91
+ [`sous.recipe.yaml`](repositories-file-formats.md#sousrecipeyaml-the-recipe-manifest).
92
+
93
+ Recipe metadata is the source of truth for the version. Never edit `sous.index.json` by hand;
94
+ `sous repo release` regenerates it.
95
+
96
+ ## Declare the variables a recipe needs
97
+
98
+ A recipe that needs a value from the project asks for it through a **definition**: a published
99
+ specification, never a value. Sous asks the question only when a subscribed recipe needs the
100
+ variable and no valid answer is already in scope.
101
+
102
+ ```yaml
103
+ variables:
104
+ - name: taskFileRoot
105
+ type: path
106
+ prompt: Where should task files be stored?
107
+ description: >-
108
+ This recipe mandates the creation of task files that are stored locally
109
+ and, in general, should not be committed. This setting dictates the path in
110
+ which agents will store and search for your task files. The default value
111
+ stores task files in the project's .sous directory, but you can specify any
112
+ local path, either relative to the project root or absolute.
113
+ example: ~/my-task-files
114
+ default: .sous/tasks
115
+ required: true
116
+ scope: shared
117
+
118
+ - name: serviceToken
119
+ type: string
120
+ env: SERVICE_TOKEN
121
+ prompt: What is this project's service token?
122
+ description: >-
123
+ This recipe authenticates every call it makes with a service token, which
124
+ is issued per project and is not shared between them. Create one under
125
+ Settings, then Tokens, and give it read access to the project you are
126
+ configuring. There is no default; a token is always specific to you, and it
127
+ is stored in the gitignored env file so it never reaches git.
128
+ example: svc_0123456789abcdef0123
129
+ secret: true
130
+ scope: local
131
+ validate:
132
+ minLength: 20
133
+ ```
134
+
135
+ The rules worth knowing while you write one:
136
+
137
+ - **`name` is camelCase**, and it is how templates refer to the variable.
138
+ - **`description` and `example` are both required.** The one-line `prompt` is rarely enough on its
139
+ own, and the person answering it cannot read your mind. The description is the paragraph shown
140
+ above the question and by `sous vars show <name>`; the example is a realistic sample answer,
141
+ shown with the question.
142
+ - **A description explains, a prompt asks.** Write the description in full sentences, and cover
143
+ three things: what the setting is for, what the default does, and what else is acceptable. Write
144
+ the prompt as one plain question and nothing else. The pair above is the model:
145
+ "This recipe mandates the creation of task files that are stored locally and, in general, should
146
+ not be committed. This setting dictates the path in which agents will store and search for your
147
+ task files. The default value stores task files in the project's .sous directory, but you can
148
+ specify any local path, either relative to the project root or absolute." asked as
149
+ "Where should task files be stored?". A description that only restates the prompt, or a prompt
150
+ that tries to carry the explanation, both make the question harder to answer.
151
+ - **An example is documentation, a default is a value.** Sous never stores an example and never
152
+ offers it as the answer; it only ever shows it. Use `default` for a value a project should
153
+ actually start with. The same text may appear in both when the sample answer really is the right
154
+ starting value.
155
+ - **`env` names the environment variable** an answer binds to. Omit it and `sous repo release`
156
+ derives one from the name: `apiUrl` becomes `SOUS_VAR_API_URL`. Naming it explicitly is how a
157
+ recipe reuses a value the environment already carries, such as `GITHUB_TOKEN`.
158
+ - **`secret: true`** stores the answer in the gitignored `.sous/.env.local` and hides the value
159
+ everywhere sous prints it.
160
+ - **`scope`** picks the file the answer is written to: `shared` for the committed `.sous/.env`,
161
+ `local` for the gitignored `.sous/.env.local`. A secret declared as `shared` is rejected,
162
+ because that combination would commit the secret.
163
+ - **`validate.pattern` runs under a time budget.** Sous runs a published pattern on a worker and
164
+ stops waiting after a fixed budget, so a pattern that backtracks forever cannot hang the person
165
+ answering; it fails validation instead, and the message names your pattern. Keep patterns simple.
166
+ - **`x-intentional: true`** silences the release warning about claiming a well-known environment
167
+ variable name. `PATH`, `HOME`, `USER`, `SHELL`, `GITHUB_TOKEN`, `GITLAB_TOKEN`, `NPM_TOKEN` and
168
+ anything starting `AWS_` or `SOUS_` draw that warning; binding an existing token is legitimate,
169
+ it just deserves saying out loud.
170
+
171
+ Two definitions of the same name may share one environment variable, because that is exactly
172
+ what the shared rung of the resolution ladder is for. Two definitions of **different** names
173
+ claiming the same environment variable is an error. See
174
+ [Recipe variables](repositories-variables.md) for how an answer is found at build time.
175
+
176
+ !> Within a major version a schema may only LOOSEN. Tightening a constraint is a major bump, and
177
+ an upgrade re-validates stored answers, re-prompting only where an old answer no longer fits.
178
+
179
+ ## Edit a repository in place
180
+
181
+ Edits happen in a real working copy, never in the machine-wide store. `sous repo link`, run
182
+ inside a project, points that project's resolution of one repository at a checkout:
183
+
184
+ ```bash
185
+ sous repo link ~/Projects/my-recipes # link the checkout that is already there
186
+ sous repo link my-recipes # clone it into .sous/repos/<owner>/<name>
187
+ sous repo link my-recipes ~/Projects/my-recipes # link a checkout that already exists
188
+ sous repo link my-recipes --global # one checkout shared by every project
189
+ sous repo unlink my-recipes
190
+ ```
191
+
192
+ The command is written three ways.
193
+
194
+ A **path on its own** links the checkout that is already at that path, where it is. Relative
195
+ paths and `~` work, because that is what people type. The repository is added to the project
196
+ first if it has not been added yet, which is the same trust ceremony `sous repo add` runs; its
197
+ short name is the one the checkout's own repo manifest suggests, falling back to the directory's
198
+ name. Nothing is cloned.
199
+
200
+ A **repository on its own** is cloned for you, into `.sous/repos/<owner>/<name>` or into
201
+ `$SOUS_HOME/repos/<owner>/<name>` with `--global`. A directory that is already a checkout of the
202
+ same remote is reused rather than cloned again, so running the command twice is harmless; one
203
+ holding a different remote is an error, because reading the wrong recipes silently would be
204
+ worse than stopping.
205
+
206
+ A **repository followed by a path** links the checkout at that path to that repository, and
207
+ clones nothing.
208
+
209
+ In every form the path must hold a repo manifest at its root, and a path in both slots is an
210
+ error: the first one already says which checkout to link.
211
+
212
+ Linking also maintains the two ignore files that keep machine-local sous files out of your
213
+ project's history: a `.sous/repos/.gitignore` holding a single `*`, and a delimited managed block
214
+ inside `.sous/.gitignore`. Only the lines between the markers are ever rewritten, and both files
215
+ are written only when their contents would change. `sous prune` and `sous clear` never reach
216
+ into `.sous/repos/`.
217
+
218
+ Because a link bypasses versions, the lockfile and freshness checks, and because those bypasses
219
+ belong to one person's machine rather than to the team, both the link command and every
220
+ subsequent build say so loudly:
221
+
222
+ ```text
223
+ The repository 'my-recipes' is now LINKED.
224
+ Its recipes are read from the checkout above, so versions, the lockfile
225
+ and freshness checks no longer apply to it. Builds say so every time.
226
+ ```
227
+
228
+ `sous repo unlink` removes the map entry and nothing else. The checkout stays exactly where it
229
+ is, and its path is printed so you can delete it yourself if you want to. Getting the scope wrong
230
+ is the easy mistake here, so unlinking a name that is linked in the other scope tells you which
231
+ scope holds it and which flag removes it.
232
+
233
+ ## Release
234
+
235
+ `sous repo release` publishes new versions of this repository's recipes. One run does the whole
236
+ job: it raises versions, regenerates `sous.index.json`, commits both, and cuts the tags that
237
+ publish them. Run it from inside the repository, with your recipe changes already committed.
238
+
239
+ ```term
240
+ $ sous repo release
241
+ The release this would make:
242
+ workflow/task-files : 1.1.0 becomes 1.1.1 (a patch step; the tag would be workflow/task-files@1.1.1)
243
+
244
+ Left alone:
245
+ workflow/sat: its files have not changed since workflow/sat@1.4.0.
246
+
247
+ This run would:
248
+ Raise the versions listed above, in the manifests that declare them.
249
+ Regenerate sous.index.json, with each version's dependencies resolved.
250
+ Commit the manifests and the index together.
251
+ Cut one annotated tag per version, dependency-first.
252
+ Push nothing; pass '--push' to push what it makes.
253
+
254
+ Publish these versions? yes
255
+ workflow/task-files: 1.1.0 becomes 1.1.1.
256
+ Wrote sous.index.json.
257
+ Committed: Release workflow/task-files@1.1.1
258
+ Created the tag workflow/task-files@1.1.1.
259
+ ```
260
+
261
+ The plan is always printed first, and a run asks once before it changes anything. `--yes` answers
262
+ that question ahead of time, and `--dry-run` prints the plan and stops.
263
+
264
+ ### What a run decides
265
+
266
+ Three facts about each recipe decide everything, and nothing else does:
267
+
268
+ 1. **Is it in scope?** Every recipe is, unless `--namespace` or `--recipe` narrows the run.
269
+ 2. **Have its files changed since the tag that last published it?** A recipe nobody touched is
270
+ not re-released; a published version that says the same thing as the one before it is noise.
271
+ `--include-unchanged` releases everything in scope anyway.
272
+ 3. **Has its version already been raised past that tag?** If so, the bump has been done and this
273
+ run only publishes it. That is what a merge looks like to the continuous integration run.
274
+
275
+ ### The flags
276
+
277
+ | Invocation | What it does |
278
+ |------------|--------------|
279
+ | `sous repo release` | Plan, ask once, then bump, regenerate, commit and tag |
280
+ | `sous repo release --dry-run` | Print the plan and stop |
281
+ | `sous repo release --yes` | Skip the question; everything else is the same |
282
+ | `sous repo release --namespace <ns>` | Release only that namespace. Repeatable |
283
+ | `sous repo release --recipe <ns/name>` | Release only that recipe. Repeatable |
284
+ | `sous repo release --bump <level>` | `patch` (the default), `minor`, `major` or `prerelease` |
285
+ | `sous repo release --no-bump` | Raise nothing; a changed recipe nobody raised is an error |
286
+ | `sous repo release --include-unchanged` | Release everything in scope, changed or not |
287
+ | `sous repo release --tag` | Cut the tags even on a branch other than the default one |
288
+ | `sous repo release --push` | Push the commit, and the tags this run created, to `origin` |
289
+ | `sous repo release --check` | Read only: validate, and fail when the committed index is out of date |
290
+ | `sous repo release --ci` | The merge preset: never bump, never ask, fail on anything unbumped |
291
+
292
+ ### The branch rule
293
+
294
+ On a branch other than the default one, a release bumps and commits but cuts no tags, and says
295
+ why: tags are cut on the default branch, by continuous integration after the merge. Pass `--tag`
296
+ to cut them anyway, which is what a repository with no automation wants.
297
+
298
+ ### Dependencies and the sibling rule
299
+
300
+ Tags are cut **dependency-first**, so a recipe is never published before something it depends on.
301
+ Everything a released recipe depends on inside this repository has to be a version that exists
302
+ once the run's own tags are counted, and there are exactly two ways that fails:
303
+
304
+ - The sibling has **never been published**. Nothing can depend on it, so the run stops and names
305
+ the tag that has to be cut.
306
+ - The sibling has been published, has changed since, and sits **outside this release's scope**.
307
+ That is fine: the release goes ahead depending on the last published version, and warns with
308
+ facts you can check.
309
+
310
+ ```term
311
+ recipes/workflow/task-files/sous.recipe.yaml:
312
+ 'workflow/sat' has changes since 'workflow/sat@1.4.0' that are outside this release's scope;
313
+ 'workflow/task-files@1.1.1' will depend on 'workflow/sat@1.4.0'.
314
+ ```
315
+
316
+ Each version's resolved dependencies are written into the index, so a consumer installing that
317
+ version installs what it was published with rather than re-resolving its ranges months later.
318
+
319
+ ### Three rules that keep metadata, tags and the index in step
320
+
321
+ - **A published version never changes.** Its content hash is carried forward exactly as
322
+ published, and a disagreement is an error telling you to bump the version rather than
323
+ republish it.
324
+ - **A version is published when its tag exists.** The one moment an index records a version
325
+ without a tag is the release commit itself: the index is committed and the tag is cut on that
326
+ commit. Any older version missing its tag is an error.
327
+ - **The tags are the backstop.** A tagged version missing from the index is rebuilt from its tag,
328
+ so deleting `sous.index.json` and regenerating it restores the same catalog.
329
+
330
+ !> A release commits the version bumps and the index, and nothing else. It refuses to run while
331
+ anything else is uncommitted, because a tag names one commit and the index it writes records
332
+ what each recipe folder holds right now. It also refuses, before writing anything, when git has
333
+ no author identity to commit under; set `user.name` and `user.email` in the repository, which the
334
+ scaffolded workflow does for you.
335
+
336
+ A bump edits the manifest in place, so its comments, its field order and its layout all survive.
337
+ Two small normalizations happen in a YAML manifest: a folded block of prose may be re-wrapped,
338
+ and the spacing before a trailing comment is collapsed to one space.
339
+
340
+ ### The scaffolded workflow
341
+
342
+ `sous repo init` writes `.github/workflows/sous-release.yml`, which runs the same command in its
343
+ two presets. It calls the sous CLI straight from npm, so nothing has to be installed into the
344
+ repository:
345
+
346
+ - On a **pull request**, `sous repo release --check`. It only reads, so it is safe on an
347
+ untrusted branch, and it fails the pull request when a manifest is wrong or the committed index
348
+ (including the dependencies it records) is stale.
349
+ - On a **push to the default branch**, `sous repo release --ci --push`. `--ci` raises no versions
350
+ and asks no questions: the version bump belongs in the change being merged, so a recipe that
351
+ changed without one fails here rather than being given a version nobody reviewed. Both
352
+ checkouts use `fetch-depth: 0`, so existing tags are visible and a published version is never
353
+ cut a second time.
354
+
355
+ ## Contribute to someone else's repository
356
+
357
+ `sous repo submit` proposes your committed changes to a repository's maintainers. It never
358
+ publishes and never writes to a repository directly.
359
+
360
+ ```bash
361
+ sous repo submit
362
+ sous repo submit --title "Add a linting recipe"
363
+ sous repo submit --draft
364
+ sous repo submit --dry-run
365
+ ```
366
+
367
+ It runs in three stages, printing each step before it runs:
368
+
369
+ 1. **Preflight.** An `origin` remote exists, sous recognizes its provider, that provider's
370
+ command line tool (`gh` or `glab`) is installed and signed in, and everything is committed.
371
+ 2. **Validation.** The repository validates and the committed index is current, so a proposal
372
+ never fails the maintainer's own checks and wastes their review.
373
+ 3. **Delegation.** Sous asks the provider whether you can push to the repository itself, forks
374
+ it onto your account when you cannot, pushes the branch, and asks the provider to open the
375
+ proposal. Every one of those is the provider's own business; sous only sequences them and
376
+ reports what came back. A change sitting on the default branch is moved to a branch named
377
+ `sous/submit-<date>-<time>` first.
378
+
379
+ `--title` defaults to your last commit's subject and `--body` to a summary sous writes. A failure
380
+ partway through says exactly which steps completed: a pushed branch with no proposal behind it is
381
+ a normal outcome of a network failure, and you are told about it rather than left guessing.
382
+
383
+ ### What each provider supports
384
+
385
+ Providers differ, and sous says so rather than pretending otherwise:
386
+
387
+ | Provider | Command line tool | Push permission | Forking | Proposal |
388
+ |---|---|---|---|---|
389
+ | GitHub | `gh` | Read from GitHub, so a contributor without it is forked automatically | `gh repo fork`, with a `fork` remote added for you | Pull request |
390
+ | GitLab | `glab` | Sous cannot tell, so it pushes to `origin` and says so | Not done for you; fork the project yourself and push your branch there | Merge request |
391
+ | Local | none | Not applicable | Not applicable | Not applicable; a repository on your own disk is edited directly |
392
+
393
+ When a provider cannot carry out a step, it says what to do by hand instead of stopping halfway
394
+ through. A local repository never submits at all: it declares no submit support, so sous points
395
+ you at the repository's `contribute` field instead.
396
+
397
+ ?> When a repository's provider cannot open a proposal for you, sous prints the `contribute`
398
+ pointer from its `sous.repo.yaml` instead, so you are never left without a route. Set that field
399
+ in your own repository for the same reason.
400
+
401
+ ## Where to go next
402
+
403
+ - [Repository file formats](repositories-file-formats.md): every manifest and index schema
404
+ - [Recipe variables](repositories-variables.md): what a definition turns into on a subscriber's
405
+ machine
406
+ - [Skill categories](skill-categories.md): the canonical categories, and how the official
407
+ repository uses them as namespaces
408
+ - [Command reference](commands.md): every command and flag