@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
@@ -1,8 +1,42 @@
1
- import { confirm as inquire } from "@inquirer/prompts";
1
+ import { confirm as inquire, select } from "@inquirer/prompts";
2
2
  import { color } from "@oclif/color";
3
3
 
4
4
  import { blankLine, blankLines, log } from "./formatting.js";
5
5
 
6
+ /**
7
+ * Ask a yes or no question and hand back the answer, leaving the decision about
8
+ * what to do with a "no" to the caller. `areYouSure` is the variant that ends
9
+ * the process; use this one where a refusal has to be reported rather than
10
+ * simply obeyed.
11
+ *
12
+ * @param prompt - The question to ask.
13
+ * @param defaultAnswer - What Enter alone means. Defaults to no.
14
+ */
15
+ export async function askYesNo(prompt: string, defaultAnswer = false): Promise<boolean> {
16
+ blankLine();
17
+ return inquire({ message: prompt, default: defaultAnswer });
18
+ }
19
+
20
+ /**
21
+ * Ask the user to pick one of several choices, handing back the value behind
22
+ * the one they picked. The caller decides the order the choices are shown in;
23
+ * this only draws them.
24
+ *
25
+ * Whether a question may be asked at all is not decided here: that is
26
+ * `isInteractive` in `src/lib/interactive.ts`, the one rule every prompt in
27
+ * sous is gated by.
28
+ *
29
+ * @param prompt - The question to ask.
30
+ * @param choices - The options, in the order they should be listed.
31
+ */
32
+ export async function askChoice<T>(
33
+ prompt: string,
34
+ choices: Array<{ name: string; value: T }>
35
+ ): Promise<T> {
36
+ blankLine();
37
+ return select({ message: prompt, choices });
38
+ }
39
+
6
40
  /**
7
41
  * Ask the user if they're sure they want to proceed.
8
42
  * @param prompt - An optional, custom, prompt to display to the user.
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Self-describing directories.
3
+ *
4
+ * Sous creates a handful of directories for its own bookkeeping: the managed
5
+ * config layers, linked checkouts, the machine-wide recipe cache, the user-level
6
+ * sous directory itself. Somebody who finds one of them months later, or an
7
+ * agent reading the repository for the first time, should be able to learn what
8
+ * it is without leaving the directory.
9
+ *
10
+ * So every directory sous creates for itself gets three small files the first
11
+ * time it is created:
12
+ *
13
+ * - `README.md`, a short plain-language explanation of what the directory is,
14
+ * who writes to it, whether it may be edited or deleted, and whether it is
15
+ * committed.
16
+ * - `AGENTS.md` and `CLAUDE.md`, each a single line pointing at the README.
17
+ *
18
+ * None of the three is ever overwritten. A user who rewrites the README, or who
19
+ * puts real instructions in `AGENTS.md`, keeps what they wrote forever.
20
+ *
21
+ * Rendered OUTPUT directories are deliberately NOT covered by this: what lands
22
+ * in `.claude/skills/`, in a recipe's output destination, or anywhere else a
23
+ * compilation target writes belongs to the user, and sous does not add files of
24
+ * its own to it.
25
+ */
26
+
27
+ import fs from "node:fs";
28
+ import path from "node:path";
29
+ import { resolveSousHome } from "../lib/sous-home.js";
30
+
31
+ /** The three files written into every sous-created directory. */
32
+ export const README_FILENAME = "README.md";
33
+
34
+ /** Agent instruction file names; both hold the same single pointer line. */
35
+ export const AGENT_POINTER_FILENAMES = ["AGENTS.md", "CLAUDE.md"] as const;
36
+
37
+ /** The one line both agent instruction files contain. */
38
+ export const AGENT_POINTER_LINE = "Read `./README.md` for information about this directory.";
39
+
40
+ /** The README text for a sous-created directory. */
41
+ export type SousDirectoryReadme = {
42
+ /** The heading, naming the directory in plain language. */
43
+ title: string;
44
+ /** One or more paragraphs of body text; blank lines are added between them. */
45
+ body: string | string[];
46
+ };
47
+
48
+ /** Renders the README body into its final markdown text. */
49
+ function renderReadme(readme: SousDirectoryReadme): string {
50
+ const paragraphs = Array.isArray(readme.body) ? readme.body : [readme.body];
51
+ return `# ${readme.title}\n\n${paragraphs.join("\n\n")}\n`;
52
+ }
53
+
54
+ /**
55
+ * Writes a file only when it does not exist yet. The exclusive write flag makes
56
+ * the check and the write one operation, so two sous processes racing to create
57
+ * the same directory cannot overwrite each other. Any failure is swallowed: a
58
+ * missing explanatory file is never a reason for a command to fail.
59
+ *
60
+ * @param filePath - Absolute path of the file to create.
61
+ * @param contents - What to write when the file is absent.
62
+ */
63
+ function writeIfAbsent(filePath: string, contents: string): void {
64
+ try {
65
+ fs.writeFileSync(filePath, contents, { encoding: "utf8", flag: "wx" });
66
+ } catch {
67
+ // Already there, or not writable; either way there is nothing to do.
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Creates one of sous's own bookkeeping directories and makes it
73
+ * self-describing: `README.md` holding the given explanation, plus `AGENTS.md`
74
+ * and `CLAUDE.md` pointing at it. Files that already exist are left exactly as
75
+ * they are.
76
+ *
77
+ * Safe to call on every command; it is idempotent and cheap.
78
+ *
79
+ * @param directory - Absolute path of the directory to create.
80
+ * @param readme - The explanation written into `README.md` on first creation.
81
+ * @returns The directory path, so calls can be inlined.
82
+ */
83
+ export function ensureSousDirectory(
84
+ directory: string,
85
+ readme: SousDirectoryReadme
86
+ ): string {
87
+ fs.mkdirSync(directory, { recursive: true });
88
+
89
+ writeIfAbsent(path.join(directory, README_FILENAME), renderReadme(readme));
90
+ for (const name of AGENT_POINTER_FILENAMES) {
91
+ writeIfAbsent(path.join(directory, name), `${AGENT_POINTER_LINE}\n`);
92
+ }
93
+
94
+ return directory;
95
+ }
96
+
97
+ // --- The directories sous creates for itself ----------------------------------------------------
98
+ //
99
+ // Each function below owns the wording for one directory, so every call site is a
100
+ // single line and the same explanation is written no matter which command got
101
+ // there first.
102
+
103
+ /**
104
+ * Ensures a project's `conf.d/` drop-in layer directory.
105
+ *
106
+ * @param directory - Absolute path to the project's `conf.d/` directory.
107
+ */
108
+ export function ensureConfdDirectory(directory: string): string {
109
+ return ensureSousDirectory(directory, {
110
+ title: "conf.d: drop-in configuration layers",
111
+ body: [
112
+ "Every `.js`, `.mjs`, `.json`, `.jsonc` or `.yaml` file directly inside this " +
113
+ "directory is a configuration layer. Sous loads them after the project's main " +
114
+ "`sous.config.*` file, in filename order, and merges each one over what came " +
115
+ "before it.",
116
+ "Layers numbered 500 through 599 are written by sous itself (for example " +
117
+ "`500-repos.jsonc`, recording the repositories this project trusts). Sous edits " +
118
+ "those files by key, so your comments, key order and formatting survive; it never " +
119
+ "touches the main config or any layer outside that band.",
120
+ "You may add, edit and delete layers here yourself, including the ones sous writes. " +
121
+ "This directory is normally committed to version control along with the rest of " +
122
+ "`.sous/`; keep machine-specific paths and secrets out of it and put them in " +
123
+ "`.sous/.env.local` instead.",
124
+ ],
125
+ });
126
+ }
127
+
128
+ /**
129
+ * Ensures a project's `.sous/repos/` directory of linked checkouts.
130
+ *
131
+ * @param directory - Absolute path to the project's `repos/` directory.
132
+ */
133
+ export function ensureProjectReposDirectory(directory: string): string {
134
+ return ensureSousDirectory(directory, {
135
+ title: "repos: linked repository checkouts",
136
+ body: [
137
+ "`sous repo link` clones a recipe repository in here so you can work on it and on " +
138
+ "this project at the same time. Each checkout is an ordinary git working copy; " +
139
+ "sous reads from it and never writes to it.",
140
+ "Everything here is machine-local. The `.gitignore` beside this file holds a single " +
141
+ "`*`, so nothing in this directory (these explanatory files included) is visible " +
142
+ "to the project's own repository.",
143
+ "It is safe to delete a checkout once you have run `sous repo unlink` for it; sous " +
144
+ "goes back to the published version of that repository.",
145
+ ],
146
+ });
147
+ }
148
+
149
+ /**
150
+ * Ensures the user-level sous directory, `$SOUS_HOME` (`~/.sous` by default).
151
+ *
152
+ * @param directory - Absolute path to the user-level sous directory.
153
+ */
154
+ export function ensureSousHomeDirectory(directory: string): string {
155
+ return ensureSousDirectory(directory, {
156
+ title: "Your user-level sous directory",
157
+ body: [
158
+ "This is `$SOUS_HOME` (`~/.sous` unless you set that variable). It holds the sous " +
159
+ "state that belongs to this machine rather than to any one project: the recipe " +
160
+ "cache in `cache/`, globally linked checkouts in `repos/`, and the machine-wide " +
161
+ "links map `sous.links.json`.",
162
+ "No project reads its configuration from here, and nothing in here is committed " +
163
+ "anywhere. Sous recreates whatever it needs, so you can delete any of it; you " +
164
+ "will lose only cached downloads and the record of which repositories you linked " +
165
+ "globally.",
166
+ ],
167
+ });
168
+ }
169
+
170
+ /**
171
+ * Explains the parent directory too, but ONLY when it really is the user-level
172
+ * sous directory. A store rooted somewhere else (a test fixture, a root somebody
173
+ * pointed at by hand) must never leave explanatory files in a directory sous
174
+ * does not own, such as the system temporary directory.
175
+ *
176
+ * @param directory - The child directory whose parent is being considered.
177
+ */
178
+ function ensureParentWhenSousHome(directory: string): void {
179
+ const parent = path.resolve(path.dirname(directory));
180
+ if (parent !== path.resolve(resolveSousHome())) return;
181
+ ensureSousHomeDirectory(parent);
182
+ }
183
+
184
+ /**
185
+ * Ensures the machine-wide recipe store root, `$SOUS_HOME/cache/`.
186
+ *
187
+ * @param directory - Absolute path to the store root.
188
+ */
189
+ export function ensureStoreRootDirectory(directory: string): string {
190
+ ensureParentWhenSousHome(directory);
191
+ return ensureSousDirectory(directory, {
192
+ title: "cache: the machine-wide recipe store",
193
+ body: [
194
+ "Sous downloads every recipe version it needs into this directory, one immutable " +
195
+ "folder per version, and verifies each one against its content hash before using " +
196
+ "it. Builds read from here, so a version is downloaded once and shared by every " +
197
+ "project on this machine.",
198
+ "The store is disposable. Everything in it can be fetched again from the pins in " +
199
+ "each project's lockfile, so you may delete any part of it at any time; " +
200
+ "`sous repo gc` does the same thing tidily. Nothing here is committed, and " +
201
+ "editing a stored file only makes sous discard the entry as corrupted.",
202
+ ],
203
+ });
204
+ }
205
+
206
+ /**
207
+ * Ensures the machine-wide directory of globally linked checkouts,
208
+ * `$SOUS_HOME/repos/`.
209
+ *
210
+ * @param directory - Absolute path to the global repos directory.
211
+ */
212
+ export function ensureGlobalReposDirectory(directory: string): string {
213
+ ensureParentWhenSousHome(directory);
214
+ return ensureSousDirectory(directory, {
215
+ title: "repos: globally linked repository checkouts",
216
+ body: [
217
+ "`sous repo link --global` clones a recipe repository in here, where every project " +
218
+ "on this machine can use the same checkout. Each one is an ordinary git working " +
219
+ "copy; sous reads from it and never writes to it.",
220
+ "Everything here is machine-local and is not committed anywhere. It is safe to " +
221
+ "delete a checkout once no project links to it any more; sous goes back to the " +
222
+ "published version of that repository.",
223
+ ],
224
+ });
225
+ }
226
+
227
+ /**
228
+ * Ensures the cached repository index directory, `$SOUS_HOME/cache/_indexes/`.
229
+ *
230
+ * @param directory - Absolute path to the index cache directory.
231
+ */
232
+ export function ensureIndexCacheDirectory(directory: string): string {
233
+ ensureStoreRootDirectory(path.dirname(directory));
234
+ return ensureSousDirectory(directory, {
235
+ title: "_indexes: cached repository indexes",
236
+ body: [
237
+ "Each repository sous knows about publishes one small index file listing the " +
238
+ "recipes and versions it offers. Sous keeps a copy of each here, beside a " +
239
+ "`.meta.json` sidecar recording when that copy was fetched.",
240
+ "Sous writes these files; there is nothing here to edit. Deleting any of them is " +
241
+ "safe and costs only a refetch the next time the repository is consulted. Nothing " +
242
+ "here is committed.",
243
+ ],
244
+ });
245
+ }