@sous-io/sous 0.2.37 → 1.0.1

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 (244) hide show
  1. package/README.md +14 -256
  2. package/dist/bin.js +11 -0
  3. package/dist/sous.js +1093 -0
  4. package/docs/markdown/README.md +5 -4
  5. package/docs/markdown/_sidebar.md +21 -1
  6. package/docs/markdown/commands.md +294 -431
  7. package/docs/markdown/config-discovery.md +25 -14
  8. package/docs/markdown/config-inspection.md +53 -31
  9. package/docs/markdown/config-layers.md +168 -66
  10. package/docs/markdown/config-variables.md +44 -11
  11. package/docs/markdown/configuration.md +104 -17
  12. package/docs/markdown/design-principles.md +4 -1
  13. package/docs/markdown/repositories-authoring.md +30 -0
  14. package/docs/markdown/repositories-consuming.md +72 -14
  15. package/docs/markdown/repositories-file-formats.md +31 -13
  16. package/docs/markdown/repositories-providers.md +48 -57
  17. package/docs/markdown/repositories-quickstart.md +3 -2
  18. package/docs/markdown/repositories-troubleshooting.md +27 -1
  19. package/docs/markdown/repositories-variables.md +6 -1
  20. package/docs/markdown/repositories.md +18 -8
  21. package/package.json +48 -107
  22. package/recipes/core/sous-skills/skills/about-liquid-templates/SKILL.tpl.md +4 -6
  23. package/recipes/core/sous-skills/skills/about-liquid-templates/references/liquid-filters.tpl.md +2 -0
  24. package/recipes/core/sous-skills/skills/about-sous/SKILL.tpl.md +10 -9
  25. package/recipes/core/sous-skills/skills/about-sous-configuration/SKILL.tpl.md +35 -14
  26. package/recipes/core/sous-skills/skills/about-sous-recipes/SKILL.tpl.md +51 -20
  27. package/recipes/core/sous-skills/sous.recipe.yaml +1 -1
  28. package/sous.config.schema.json +351 -132
  29. package/bin/run.js +0 -51
  30. package/src/base-command.ts +0 -457
  31. package/src/commands/build.ts +0 -181
  32. package/src/commands/clear.ts +0 -79
  33. package/src/commands/compile.ts +0 -155
  34. package/src/commands/config/get.ts +0 -110
  35. package/src/commands/config/show.ts +0 -32
  36. package/src/commands/config/validate.ts +0 -53
  37. package/src/commands/help.ts +0 -46
  38. package/src/commands/init.ts +0 -289
  39. package/src/commands/launch.ts +0 -139
  40. package/src/commands/lock/rebuild.ts +0 -239
  41. package/src/commands/lock/show.ts +0 -115
  42. package/src/commands/namespace/list.ts +0 -138
  43. package/src/commands/namespace/show.ts +0 -130
  44. package/src/commands/prune.ts +0 -43
  45. package/src/commands/recipe/list.ts +0 -112
  46. package/src/commands/recipe/show.ts +0 -439
  47. package/src/commands/repo/add.ts +0 -145
  48. package/src/commands/repo/contribute.ts +0 -604
  49. package/src/commands/repo/gc.ts +0 -173
  50. package/src/commands/repo/init.ts +0 -136
  51. package/src/commands/repo/link.ts +0 -899
  52. package/src/commands/repo/list.ts +0 -236
  53. package/src/commands/repo/release.ts +0 -685
  54. package/src/commands/repo/remove.ts +0 -194
  55. package/src/commands/repo/search.ts +0 -243
  56. package/src/commands/repo/submit.ts +0 -341
  57. package/src/commands/repo/unlink.ts +0 -461
  58. package/src/commands/subscription/add.ts +0 -286
  59. package/src/commands/subscription/list.ts +0 -208
  60. package/src/commands/subscription/remove.ts +0 -182
  61. package/src/commands/subscription/update.ts +0 -216
  62. package/src/commands/update.ts +0 -178
  63. package/src/commands/vars/ask.ts +0 -417
  64. package/src/commands/vars/index.ts +0 -79
  65. package/src/commands/vars/list.ts +0 -67
  66. package/src/commands/vars/show.ts +0 -77
  67. package/src/config-command.ts +0 -30
  68. package/src/lib/build-preparation.ts +0 -128
  69. package/src/lib/build-service.ts +0 -580
  70. package/src/lib/config-discovery.ts +0 -378
  71. package/src/lib/config-inspect.ts +0 -145
  72. package/src/lib/config-kernel.mjs +0 -379
  73. package/src/lib/config-schema.ts +0 -401
  74. package/src/lib/env-file.ts +0 -328
  75. package/src/lib/env-local.ts +0 -212
  76. package/src/lib/errors.ts +0 -32
  77. package/src/lib/glob-pattern.ts +0 -24
  78. package/src/lib/include-resolver.ts +0 -523
  79. package/src/lib/interactive.ts +0 -165
  80. package/src/lib/markdown-compiler.ts +0 -947
  81. package/src/lib/package-info.ts +0 -25
  82. package/src/lib/pid-service.ts +0 -99
  83. package/src/lib/project-install.d.mts +0 -44
  84. package/src/lib/project-install.mjs +0 -234
  85. package/src/lib/project-scaffold/index.ts +0 -332
  86. package/src/lib/project-scaffold/templates.ts +0 -240
  87. package/src/lib/repos/catalog-display.ts +0 -215
  88. package/src/lib/repos/catalog-inputs.ts +0 -288
  89. package/src/lib/repos/catalog.ts +0 -842
  90. package/src/lib/repos/contribute.ts +0 -452
  91. package/src/lib/repos/core-recipe.ts +0 -105
  92. package/src/lib/repos/declarations.ts +0 -187
  93. package/src/lib/repos/defaults.ts +0 -175
  94. package/src/lib/repos/formats/common.ts +0 -440
  95. package/src/lib/repos/formats/index-file.ts +0 -253
  96. package/src/lib/repos/formats/links-map.ts +0 -98
  97. package/src/lib/repos/formats/lockfile.ts +0 -169
  98. package/src/lib/repos/formats/patterns.ts +0 -57
  99. package/src/lib/repos/formats/recipe-manifest.ts +0 -205
  100. package/src/lib/repos/formats/repo-manifest.ts +0 -97
  101. package/src/lib/repos/formats/store-entry.ts +0 -84
  102. package/src/lib/repos/formats/variable-definition.ts +0 -268
  103. package/src/lib/repos/freshness.ts +0 -264
  104. package/src/lib/repos/git-clone.ts +0 -850
  105. package/src/lib/repos/identity.ts +0 -89
  106. package/src/lib/repos/index.ts +0 -57
  107. package/src/lib/repos/links.ts +0 -356
  108. package/src/lib/repos/load-manifest.ts +0 -237
  109. package/src/lib/repos/lock-service.ts +0 -454
  110. package/src/lib/repos/locked-namespace-resolver.ts +0 -232
  111. package/src/lib/repos/locked-recipe-lookup.ts +0 -123
  112. package/src/lib/repos/locked-recipes.ts +0 -254
  113. package/src/lib/repos/managed-layer.ts +0 -422
  114. package/src/lib/repos/memories-hash-name.ts +0 -33
  115. package/src/lib/repos/namespace-resolver.ts +0 -527
  116. package/src/lib/repos/providers/base.ts +0 -268
  117. package/src/lib/repos/providers/git.ts +0 -245
  118. package/src/lib/repos/providers/github.ts +0 -589
  119. package/src/lib/repos/providers/gitlab.ts +0 -332
  120. package/src/lib/repos/providers/http.ts +0 -128
  121. package/src/lib/repos/providers/index-cache.ts +0 -421
  122. package/src/lib/repos/providers/index.ts +0 -106
  123. package/src/lib/repos/providers/local.ts +0 -410
  124. package/src/lib/repos/providers/provider.ts +0 -615
  125. package/src/lib/repos/recipe-config-layers.ts +0 -316
  126. package/src/lib/repos/recipe-key-matcher.ts +0 -62
  127. package/src/lib/repos/recipe-memories.ts +0 -178
  128. package/src/lib/repos/recipe-targets.ts +0 -236
  129. package/src/lib/repos/ref-search.ts +0 -46
  130. package/src/lib/repos/release/bump.ts +0 -161
  131. package/src/lib/repos/release/changelog.ts +0 -461
  132. package/src/lib/repos/release/git-state.ts +0 -460
  133. package/src/lib/repos/release/index-builder.ts +0 -1108
  134. package/src/lib/repos/release/index.ts +0 -19
  135. package/src/lib/repos/release/plan.ts +0 -521
  136. package/src/lib/repos/release/settle.ts +0 -154
  137. package/src/lib/repos/release/submissions.ts +0 -214
  138. package/src/lib/repos/release/submit-checkout.ts +0 -290
  139. package/src/lib/repos/release/submit-questions.ts +0 -153
  140. package/src/lib/repos/release/submit-service.ts +0 -898
  141. package/src/lib/repos/release/tags.ts +0 -239
  142. package/src/lib/repos/release/validate.ts +0 -463
  143. package/src/lib/repos/resolver.ts +0 -1028
  144. package/src/lib/repos/scaffold/index.ts +0 -245
  145. package/src/lib/repos/scaffold/templates.ts +0 -517
  146. package/src/lib/repos/seed.ts +0 -473
  147. package/src/lib/repos/store/contract.ts +0 -64
  148. package/src/lib/repos/store/hash.ts +0 -191
  149. package/src/lib/repos/store/recipe-store.ts +0 -599
  150. package/src/lib/repos/store/settings.ts +0 -58
  151. package/src/lib/repos/subscription-service.ts +0 -3859
  152. package/src/lib/repos/trust.ts +0 -447
  153. package/src/lib/repos/update-plan.ts +0 -235
  154. package/src/lib/self-update/index.ts +0 -14
  155. package/src/lib/self-update/installs.ts +0 -413
  156. package/src/lib/self-update/managers.ts +0 -126
  157. package/src/lib/self-update/plan.ts +0 -315
  158. package/src/lib/self-update/registry.ts +0 -99
  159. package/src/lib/self-update/run.ts +0 -506
  160. package/src/lib/self-update/versions.ts +0 -243
  161. package/src/lib/settings.ts +0 -1107
  162. package/src/lib/sous-home.ts +0 -104
  163. package/src/lib/state.ts +0 -167
  164. package/src/lib/vars/answers.ts +0 -192
  165. package/src/lib/vars/ask.ts +0 -1152
  166. package/src/lib/vars/definition-source.ts +0 -261
  167. package/src/lib/vars/display.ts +0 -231
  168. package/src/lib/vars/index.ts +0 -20
  169. package/src/lib/vars/ladder.ts +0 -282
  170. package/src/lib/vars/mappings.ts +0 -260
  171. package/src/lib/vars/names.ts +0 -94
  172. package/src/lib/vars/preanswers.ts +0 -395
  173. package/src/lib/vars/question-plan.ts +0 -241
  174. package/src/lib/vars/report.ts +0 -228
  175. package/src/lib/vars/safe-regex.ts +0 -235
  176. package/src/lib/vars/secrets.ts +0 -131
  177. package/src/lib/vars/validate.ts +0 -312
  178. package/src/lib/version-report.ts +0 -90
  179. package/src/lib/watch-loop.ts +0 -148
  180. package/src/lib/watch-service.ts +0 -116
  181. package/src/services/ref-resolver/container.ts +0 -107
  182. package/src/services/ref-resolver/dependency.ts +0 -60
  183. package/src/services/ref-resolver/format.ts +0 -197
  184. package/src/services/ref-resolver/glob.ts +0 -115
  185. package/src/services/ref-resolver/hash-names.ts +0 -155
  186. package/src/services/ref-resolver/index.ts +0 -55
  187. package/src/services/ref-resolver/injectable.ts +0 -36
  188. package/src/services/ref-resolver/location.ts +0 -62
  189. package/src/services/ref-resolver/lookups/cached-index-lookup.ts +0 -53
  190. package/src/services/ref-resolver/lookups/catalog-lookup.ts +0 -23
  191. package/src/services/ref-resolver/lookups/catalog-matcher.ts +0 -261
  192. package/src/services/ref-resolver/lookups/chained-lookup.ts +0 -27
  193. package/src/services/ref-resolver/lookups/env-var-lookup.ts +0 -65
  194. package/src/services/ref-resolver/lookups/fetched-index-lookup.ts +0 -150
  195. package/src/services/ref-resolver/lookups/recorded-dependency-lookup.ts +0 -62
  196. package/src/services/ref-resolver/lookups/ref-lookup.ts +0 -54
  197. package/src/services/ref-resolver/lookups/variable-lookup.ts +0 -123
  198. package/src/services/ref-resolver/named-refs.ts +0 -144
  199. package/src/services/ref-resolver/parser/env-var-splitter.ts +0 -27
  200. package/src/services/ref-resolver/parser/location-splitter.ts +0 -239
  201. package/src/services/ref-resolver/parser/name-path-splitter.ts +0 -200
  202. package/src/services/ref-resolver/parser/partial-ref.ts +0 -84
  203. package/src/services/ref-resolver/parser/query-splitter.ts +0 -61
  204. package/src/services/ref-resolver/parser/range-splitter.ts +0 -52
  205. package/src/services/ref-resolver/parser/ref-parser.ts +0 -115
  206. package/src/services/ref-resolver/parser/repo-qualifier-splitter.ts +0 -51
  207. package/src/services/ref-resolver/parts.ts +0 -115
  208. package/src/services/ref-resolver/pruners/command-line-pruner.ts +0 -47
  209. package/src/services/ref-resolver/pruners/include-pruner.ts +0 -66
  210. package/src/services/ref-resolver/pruners/manifest-pruner.ts +0 -130
  211. package/src/services/ref-resolver/pruners/ref-pruner.ts +0 -135
  212. package/src/services/ref-resolver/pruners/rule-helpers.ts +0 -44
  213. package/src/services/ref-resolver/pruners/stored-key-pruner.ts +0 -136
  214. package/src/services/ref-resolver/ref-helpers.ts +0 -62
  215. package/src/services/ref-resolver/ref-picker-service.ts +0 -204
  216. package/src/services/ref-resolver/ref-report.ts +0 -84
  217. package/src/services/ref-resolver/ref-resolve-arguments.ts +0 -47
  218. package/src/services/ref-resolver/ref-resolve-result.ts +0 -36
  219. package/src/services/ref-resolver/ref-resolver-service.ts +0 -323
  220. package/src/services/ref-resolver/source.ts +0 -48
  221. package/src/services/ref-resolver/tokens.ts +0 -25
  222. package/src/services/ref-resolver/types.ts +0 -155
  223. package/src/templating/filters/bullet-list.ts +0 -9
  224. package/src/templating/filters/index.ts +0 -8
  225. package/src/templating/init-liquid-engine.ts +0 -139
  226. package/src/templating/lib/glob-files.ts +0 -74
  227. package/src/templating/lib/import-export.ts +0 -32
  228. package/src/templating/lib/secret-scope.ts +0 -41
  229. package/src/templating/lib/tag-args.ts +0 -19
  230. package/src/templating/tags/exportScalarVarsJs.ts +0 -46
  231. package/src/templating/tags/getFiles.ts +0 -89
  232. package/src/templating/tags/index.ts +0 -14
  233. package/src/templating/tags/listFiles.ts +0 -54
  234. package/src/templating/tags/showVars.ts +0 -28
  235. package/src/utils/choice-prompt.ts +0 -143
  236. package/src/utils/command-errors.ts +0 -186
  237. package/src/utils/command-help.ts +0 -45
  238. package/src/utils/confirm-prompt.ts +0 -110
  239. package/src/utils/flags.ts +0 -177
  240. package/src/utils/formatting.ts +0 -849
  241. package/src/utils/prompts.ts +0 -53
  242. package/src/utils/sous-directory.ts +0 -245
  243. package/src/utils/table.ts +0 -603
  244. package/src/utils/value-prompt.ts +0 -119
@@ -1,98 +0,0 @@
1
- /**
2
- * The links map: `sous.links.json`.
3
- *
4
- * A link redirects a repo's resolution away from the store and at a real
5
- * working copy on disk, which is how a maintainer edits recipes: edits happen
6
- * in a checkout, never in the store. `sous repo link` writes an entry;
7
- * `sous repo unlink` removes it and leaves the checkout in place unless asked
8
- * to delete one sous cloned.
9
- *
10
- * Two maps are read: the project's `.sous/sous.links.json` and the machine-wide
11
- * `$SOUS_HOME/sous.links.json`, with the project map winning on conflict. The
12
- * file is MACHINE-WRITTEN and machine-local; it is never committed, because a
13
- * link bypasses versions, the lockfile and freshness checks, and those bypasses
14
- * belong to one person's machine rather than to the team.
15
- */
16
-
17
- import { z } from "zod";
18
- import {
19
- absolutePathSchema,
20
- formatVersionSchema,
21
- isoTimestampSchema,
22
- parseFormat,
23
- repoNameSchema,
24
- stableJsonStringify,
25
- } from "./common.js";
26
-
27
- /** How the linked working copy came to exist. */
28
- export const LINK_ORIGINS = ["clone", "path"] as const;
29
-
30
- /** One linked repo. */
31
- export const repoLinkSchema = z.strictObject({
32
- /** Absolute path to the working copy sous reads instead of the store. */
33
- path: absolutePathSchema,
34
- /** When the link was created. */
35
- linkedAt: isoTimestampSchema,
36
- /**
37
- * Whether sous cloned the working copy itself ('clone') or was pointed at an
38
- * existing checkout ('path'). Unlinking leaves either in place unless
39
- * `--remove` is passed, and even then only a 'clone' is ever deleted: a
40
- * 'path' checkout belongs to whoever linked it.
41
- */
42
- origin: z.enum(LINK_ORIGINS),
43
- });
44
-
45
- /** The links map schema. */
46
- export const linksMapSchema = z.strictObject({
47
- formatVersion: formatVersionSchema,
48
- /** Every linked repo, keyed by the repo's configured short name. */
49
- links: z.record(repoNameSchema, repoLinkSchema),
50
- });
51
-
52
- /** A validated links map. */
53
- export type LinksMap = z.infer<typeof linksMapSchema>;
54
-
55
- /** One validated link entry. */
56
- export type RepoLink = z.infer<typeof repoLinkSchema>;
57
-
58
- /** How the linked working copy came to exist. */
59
- export type LinkOrigin = (typeof LINK_ORIGINS)[number];
60
-
61
- /**
62
- * Validates a parsed links map, throwing a ConfigError that names the file and
63
- * the path of every bad field.
64
- *
65
- * @param value - The parsed contents of the links file.
66
- * @param sourceLabel - The links file's path, named in error messages.
67
- */
68
- export function parseLinksMap(value: unknown, sourceLabel: string): LinksMap {
69
- return parseFormat(linksMapSchema, value, sourceLabel, "links map");
70
- }
71
-
72
- /** An empty links map, for a project or machine with nothing linked. */
73
- export function createEmptyLinksMap(): LinksMap {
74
- return { formatVersion: 1, links: {} };
75
- }
76
-
77
- /**
78
- * Serializes a links map for writing, with keys sorted.
79
- *
80
- * @param map - The links map to write.
81
- */
82
- export function stringifyLinksMap(map: LinksMap): string {
83
- return stableJsonStringify(map);
84
- }
85
-
86
- /**
87
- * Merges a machine-wide links map with a project's, with the project's entries
88
- * winning, which is the precedence `sous repo link` documents.
89
- *
90
- * @param global - The machine-wide map, or undefined when there is none.
91
- * @param project - The project's map, or undefined when there is none.
92
- */
93
- export function mergeLinksMaps(
94
- global: LinksMap | undefined,
95
- project: LinksMap | undefined
96
- ): Record<string, RepoLink> {
97
- return { ...(global?.links ?? {}), ...(project?.links ?? {}) };
98
- }
@@ -1,169 +0,0 @@
1
- /**
2
- * The project lockfile: `.sous/sous.lock.json`, committed to the project.
3
- *
4
- * The lockfile is MACHINE-WRITTEN and records the exact version and content
5
- * hash of everything a project currently uses, so a fresh clone restores
6
- * deterministically with no prompts and no version drift. Together with repo
7
- * trust it is the supply-chain defense: nothing new enters a project except
8
- * through an explicit, visible change to these files.
9
- *
10
- * A repository appears twice over: the project's own short name is the key of
11
- * the `repos` map and is what every recipe entry and every message names, while
12
- * the entry's `identity` is what the machine-wide store is keyed by.
13
- *
14
- * `requestedBy` is what makes removal safe. Every entry lists who holds it, the
15
- * literal string `project` for something the project subscribed to directly and
16
- * a recipe key for something pulled in as a dependency. Unsubscribing removes
17
- * one holder; the entry itself goes only when the last holder does.
18
- */
19
-
20
- import { z } from "zod";
21
- import { repoIdentity } from "../identity.js";
22
- import { requireProvider } from "../providers/index.js";
23
- import {
24
- contentHashSchema,
25
- formatVersionSchema,
26
- parseFormat,
27
- recipeKeySchema,
28
- repoIdentitySchema,
29
- repoNameSchema,
30
- repoUrlSchema,
31
- semverVersionSchema,
32
- stableJsonStringify,
33
- DEPENDENCY_KINDS,
34
- type DependencyKind,
35
- } from "./common.js";
36
-
37
- /** The literal `requestedBy` holder meaning "the project subscribed to this directly". */
38
- export const PROJECT_HOLDER = "project";
39
-
40
- /** How a locked recipe entered the project: the dependency kinds every format shares. */
41
- export const LOCK_KINDS = DEPENDENCY_KINDS;
42
-
43
- /** One repo the project resolves against. */
44
- export const lockedRepoSchema = z
45
- .strictObject({
46
- /**
47
- * Where the repo lives, as recorded when it was added: a URL, or an absolute
48
- * path for a repository on this machine read through the `local` provider.
49
- */
50
- url: repoUrlSchema,
51
- /**
52
- * The repository's canonical identity, derived from that URL. The keys of
53
- * `repos` are the project's own short names, which no other project has to
54
- * agree with; this is what the machine-wide store and the index cache file
55
- * the repository under, so a restore finds the same cached copy every other
56
- * project uses.
57
- *
58
- * Optional ON READ only, for lockfiles written before the store was keyed by
59
- * identity: an entry without one has its identity derived from `url` below,
60
- * exactly as the writer would have derived it. Every lockfile sous writes
61
- * carries it, so the field fills itself in on the next write.
62
- */
63
- identity: repoIdentitySchema.optional(),
64
- /** Content hash of the index this lock was resolved against, when known. */
65
- indexHash: contentHashSchema.optional(),
66
- })
67
- .transform((entry, ctx) => {
68
- if (entry.identity !== undefined) return { ...entry, identity: entry.identity };
69
-
70
- // An older lockfile recorded only the URL. Deriving the identity through the
71
- // provider that handles that URL is what the writer itself does, so the
72
- // answer is the one the entry would have carried had it been written today.
73
- try {
74
- const identity = repoIdentity(requireProvider(entry.url).canonicalize(entry.url));
75
- return { ...entry, identity };
76
- } catch {
77
- ctx.addIssue({
78
- code: "custom",
79
- path: ["identity"],
80
- message:
81
- `is missing, and sous could not work one out from the url '${entry.url}' ` +
82
- `because no provider recognizes it. Add an 'identity' to this entry, or ` +
83
- `remove the lockfile and subscribe again to have sous rebuild it.`,
84
- });
85
- return z.NEVER;
86
- }
87
- });
88
-
89
- /** One locked recipe. */
90
- export const lockedRecipeSchema = z.strictObject({
91
- /** The short name of the repo it came from; must appear under `repos`. */
92
- repo: repoNameSchema,
93
- /** The exact version resolved. */
94
- version: semverVersionSchema,
95
- /** Content hash of that version, verified against the store after every fetch. */
96
- hash: contentHashSchema,
97
- /**
98
- * Who holds this entry: `project` for a direct subscription, or the ref key of
99
- * a recipe that requires it. Used to refcount removal.
100
- */
101
- requestedBy: z
102
- .array(z.string().min(1, "must not be empty"))
103
- .min(1, "must name at least one holder"),
104
- /** Whether the holder relationship is a co-subscription or a build dependency. */
105
- kind: z.enum(LOCK_KINDS),
106
- });
107
-
108
- /** The lockfile schema. */
109
- export const lockfileSchema = z
110
- .strictObject({
111
- formatVersion: formatVersionSchema,
112
- /** Every repo the locked recipes came from, keyed by short name. */
113
- repos: z.record(repoNameSchema, lockedRepoSchema),
114
- /** Every locked recipe, keyed `namespace/recipe`. */
115
- recipes: z.record(recipeKeySchema, lockedRecipeSchema),
116
- })
117
- .superRefine((lock, ctx) => {
118
- // A recipe pointing at a repo the lockfile does not describe cannot be
119
- // restored, so name the pair rather than failing later at fetch time.
120
- for (const [key, entry] of Object.entries(lock.recipes)) {
121
- if (!Object.hasOwn(lock.repos, entry.repo)) {
122
- ctx.addIssue({
123
- code: "custom",
124
- path: ["recipes", key, "repo"],
125
- message:
126
- `names the repo '${entry.repo}', which this lockfile does not describe ` +
127
- `under 'repos'`,
128
- });
129
- }
130
- }
131
- });
132
-
133
- /** A validated lockfile. */
134
- export type Lockfile = z.infer<typeof lockfileSchema>;
135
-
136
- /** One locked repo entry. */
137
- export type LockedRepo = z.infer<typeof lockedRepoSchema>;
138
-
139
- /** One locked recipe entry. */
140
- export type LockedRecipe = z.infer<typeof lockedRecipeSchema>;
141
-
142
- /** How a locked recipe entered the project. */
143
- export type LockKind = DependencyKind;
144
-
145
- /**
146
- * Validates a parsed lockfile, throwing a ConfigError that names the file and
147
- * the path of every bad field.
148
- *
149
- * @param value - The parsed contents of the lockfile.
150
- * @param sourceLabel - The lockfile's path, named in error messages.
151
- */
152
- export function parseLockfile(value: unknown, sourceLabel: string): Lockfile {
153
- return parseFormat(lockfileSchema, value, sourceLabel, "lockfile");
154
- }
155
-
156
- /** An empty lockfile, for a project that has locked nothing yet. */
157
- export function createEmptyLockfile(): Lockfile {
158
- return { formatVersion: 1, repos: {}, recipes: {} };
159
- }
160
-
161
- /**
162
- * Serializes a lockfile for writing, with every object key sorted so the
163
- * committed file changes only when its content genuinely does.
164
- *
165
- * @param lockfile - The lockfile to write.
166
- */
167
- export function stringifyLockfile(lockfile: Lockfile): string {
168
- return stableJsonStringify(lockfile);
169
- }
@@ -1,57 +0,0 @@
1
- /**
2
- * Dependency-free regular expressions shared by the Repositories on-disk
3
- * formats, the ref parser and the sous config schema.
4
- *
5
- * This module deliberately imports nothing, so `config-schema.ts` can reuse the
6
- * patterns without pulling in zod schemas, semver, or the rest of the repos
7
- * layer. `formats/common.ts` re-exports everything here.
8
- */
9
-
10
- /**
11
- * Lowercase kebab-case identifier: starts with a letter, then letters, digits
12
- * or hyphens. Used for repo short names, namespace names and recipe names
13
- * (`sous-recipes`, `tool-usage`, `automated-browser-tasks`).
14
- */
15
- export const KEBAB_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
16
-
17
- /** A repo's configured short name, as used by the `repo:` ref qualifier. */
18
- export const REPO_NAME_PATTERN = KEBAB_NAME_PATTERN;
19
-
20
- /** A namespace name. */
21
- export const NAMESPACE_NAME_PATTERN = KEBAB_NAME_PATTERN;
22
-
23
- /** A recipe name, unique within its namespace. */
24
- export const RECIPE_NAME_PATTERN = KEBAB_NAME_PATTERN;
25
-
26
- /**
27
- * A ref key: either a bare namespace (`workflow`) or a fully qualified recipe
28
- * (`workflow/task-files`). Never carries a repo qualifier or a version range.
29
- */
30
- export const REF_KEY_PATTERN = /^[a-z][a-z0-9-]*(\/[a-z][a-z0-9-]*)?$/;
31
-
32
- /** A recipe key, which always has both segments (`workflow/task-files`). */
33
- export const RECIPE_KEY_PATTERN = /^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/;
34
-
35
- /**
36
- * A repository's canonical identity: the host, then the path it lives at, all
37
- * lowercase (`github.com/sous-io/sous-recipes`). It is what every machine-wide
38
- * key uses, because a project's short name for a repository is its own label
39
- * and no other project has to agree with it.
40
- */
41
- export const REPO_IDENTITY_PATTERN = /^[^\s/]+(\/[^\s/]+)+$/;
42
-
43
- /** A content hash, written as the algorithm name followed by lowercase hex. */
44
- export const CONTENT_HASH_PATTERN = /^sha256-[0-9a-f]{64}$/;
45
-
46
- /** An environment variable name, as declared by a recipe variable definition. */
47
- export const ENV_VAR_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/;
48
-
49
- /** A camelCase variable name, as declared by a recipe variable definition. */
50
- export const VARIABLE_NAME_PATTERN = /^[a-z][a-zA-Z0-9]*$/;
51
-
52
- /**
53
- * An ISO 8601 timestamp carrying an explicit offset (`Z` or `+hh:mm`). Machine
54
- * written timestamps come from `new Date().toISOString()`, which matches.
55
- */
56
- export const ISO_TIMESTAMP_PATTERN =
57
- /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?(Z|[+-]\d{2}:\d{2})$/;
@@ -1,205 +0,0 @@
1
- /**
2
- * The recipe manifest: `sous.recipe.yaml` (or `.yml`, or `.json`) in each
3
- * recipe folder. It is HAND-WRITTEN and is the complete, executable-free
4
- * description of one publishable unit: what it is called, what version it is,
5
- * what it depends on, what files it contributes, and what variables it needs
6
- * answered.
7
- *
8
- * Two dependency lists exist, and they mean different things:
9
- *
10
- * - `depends` is a build dependency. The target is fetched, pinned and trust
11
- * gated, and is addressable from this recipe's own files, but its files do
12
- * NOT enter the subscribing project's output.
13
- * - `subscribes` is a co-subscription. Subscribing to this recipe subscribes
14
- * the project to the target too, with full semantics: its questions run and
15
- * its files DO enter the output. A curated bundle is simply a recipe made
16
- * mostly of `subscribes` entries.
17
- *
18
- * Both lists name their targets BY LOCATION:
19
- *
20
- * workflow/sat a sibling in this repository
21
- * github://sous-io/sous-recipes/workflow/sat a recipe in another one
22
- *
23
- * Every other location form works too (an HTTPS or SSH URL, a browser URL); a
24
- * short name such as `sous-recipes:` is a consuming project's own label, so it
25
- * never appears in a published manifest. See `RefResolverService.parse` with
26
- * `RefSource.Manifest`.
27
- */
28
-
29
- import { z } from "zod";
30
- import {
31
- extensibleObject,
32
- formatVersionSchema,
33
- namespaceNameSchema,
34
- parseFormat,
35
- recipeNameSchema,
36
- relativePathSchema,
37
- semverVersionSchema,
38
- submissionsSchema,
39
- } from "./common.js";
40
- import { RefSource, sharedRefResolver } from "../../../services/ref-resolver/index.js";
41
- import { variableDefinitionSchema } from "./variable-definition.js";
42
-
43
- // Variable definitions live in their own module, so the index can read them
44
- // without importing the ref parser this manifest needs; they are re-exported
45
- // here because a definition is part of the manifest.
46
- export * from "./variable-definition.js";
47
-
48
- // --- Dependency refs ----------------------------------------------------------------------------
49
-
50
- /**
51
- * A dependency in `depends` or `subscribes`: a bare ref naming a recipe in this
52
- * same repository, or a location naming one in another repository. Parsed with the real parser so
53
- * a manifest and the rest of sous never disagree about what a dependency means;
54
- * the parser's message is carried through as the zod issue message.
55
- */
56
- const dependencyRefSchema = z.string().superRefine((value, ctx) => {
57
- try {
58
- sharedRefResolver().parse(value, RefSource.Manifest);
59
- } catch (error) {
60
- ctx.addIssue({ code: "custom", message: (error as Error).message });
61
- }
62
- });
63
-
64
- /** Builds a list-of-refs schema that also rejects the same target listed twice. */
65
- function refListSchema(label: string) {
66
- return z.array(dependencyRefSchema).superRefine((refs, ctx) => {
67
- const seen = new Set<string>();
68
- refs.forEach((entry, index) => {
69
- const trimmed = entry.trim();
70
- if (seen.has(trimmed)) {
71
- ctx.addIssue({
72
- code: "custom",
73
- path: [index],
74
- message: `'${trimmed}' is listed more than once in ${label}`,
75
- });
76
- }
77
- seen.add(trimmed);
78
- });
79
- });
80
- }
81
-
82
- // --- Contents -----------------------------------------------------------------------------------
83
-
84
- /**
85
- * What a content entry contributes. `skills`, `memories` and `prompts` are
86
- * compiled or copied into the subscribing project's agent directories; `config`
87
- * entries name config layer files that are merged into the subscriber's config.
88
- */
89
- export const CONTENT_KINDS = ["skills", "memories", "prompts", "config"] as const;
90
-
91
- /** One group of files this recipe contributes, all of the same kind. */
92
- export const recipeContentSchema = extensibleObject({
93
- /** What the files are, which decides where they land in a subscribing project. */
94
- kind: z.enum(CONTENT_KINDS),
95
- /** Glob patterns, relative to the recipe folder, naming the files to contribute. */
96
- include: z
97
- .array(relativePathSchema("an include pattern", true))
98
- .min(1, "must list at least one include pattern"),
99
- /** Glob patterns, relative to the recipe folder, removed from the include set. */
100
- exclude: z.array(relativePathSchema("an exclude pattern", true)).optional(),
101
- });
102
-
103
- // --- The manifest -------------------------------------------------------------------------------
104
-
105
- /** The recipe manifest schema. */
106
- export const recipeManifestSchema = extensibleObject({
107
- formatVersion: formatVersionSchema,
108
- /** The namespace this recipe belongs to. Must be declared by the repo manifest. */
109
- namespace: namespaceNameSchema,
110
- /** The recipe's name, unique within its namespace. */
111
- name: recipeNameSchema,
112
- /**
113
- * The published version. Recipe metadata is the source of truth for versions;
114
- * a git tag of the shape `namespace/recipe@1.2.3` is a convenience ref that
115
- * `sous repo release` keeps consistent with this field.
116
- */
117
- version: semverVersionSchema,
118
- /** One-paragraph summary, shown by `sous repo search` and `sous repo list`. */
119
- description: z.string().optional(),
120
- /**
121
- * Whether this recipe takes proposed changes, winning over the repository's
122
- * own `submissions` block. A recipe whose files are copied in from somewhere
123
- * else sets `allowed: false` and says where to go instead.
124
- */
125
- submissions: submissionsSchema.optional(),
126
- /**
127
- * Build dependencies: fetched and addressable here, but not added to the
128
- * project. Each entry is a bare ref naming a sibling recipe in this same
129
- * repository, or a locator URL naming a recipe in another one.
130
- */
131
- depends: refListSchema("'depends'").optional(),
132
- /** Co-subscriptions: subscribing here subscribes the project to these too. */
133
- subscribes: refListSchema("'subscribes'").optional(),
134
- /**
135
- * The files this recipe contributes. A curated bundle contributes no files of
136
- * its own, so this may be omitted; it then defaults to an empty list.
137
- */
138
- contents: z.array(recipeContentSchema).default([]),
139
- /** Variable definitions this recipe publishes. */
140
- variables: z
141
- .array(variableDefinitionSchema)
142
- .superRefine((definitions, ctx) => {
143
- const seenNames = new Set<string>();
144
- const seenEnv = new Map<string, number>();
145
- definitions.forEach((definition, index) => {
146
- if (seenNames.has(definition.name)) {
147
- ctx.addIssue({
148
- code: "custom",
149
- path: [index, "name"],
150
- message: `the variable '${definition.name}' is defined more than once`,
151
- });
152
- }
153
- seenNames.add(definition.name);
154
-
155
- if (definition.env !== undefined) {
156
- const first = seenEnv.get(definition.env);
157
- if (first !== undefined) {
158
- ctx.addIssue({
159
- code: "custom",
160
- path: [index, "env"],
161
- message:
162
- `the environment variable '${definition.env}' is already claimed by ` +
163
- `variables[${first}]; two definitions cannot share one environment variable`,
164
- });
165
- } else {
166
- seenEnv.set(definition.env, index);
167
- }
168
- }
169
- });
170
- })
171
- .optional(),
172
- });
173
-
174
- /** A validated recipe manifest. */
175
- export type RecipeManifest = z.infer<typeof recipeManifestSchema>;
176
-
177
- /** One validated content group from a recipe manifest. */
178
- export type RecipeContent = z.infer<typeof recipeContentSchema>;
179
-
180
- /** What a content group contributes. */
181
- export type ContentKind = (typeof CONTENT_KINDS)[number];
182
-
183
- /**
184
- * Validates a parsed recipe manifest, throwing a ConfigError that names the
185
- * file and the path of every bad field.
186
- *
187
- * @param value - The parsed contents of the manifest file.
188
- * @param sourceLabel - The manifest's file path, named in error messages.
189
- */
190
- export function parseRecipeManifest(
191
- value: unknown,
192
- sourceLabel: string
193
- ): RecipeManifest {
194
- return parseFormat(recipeManifestSchema, value, sourceLabel, "recipe manifest");
195
- }
196
-
197
- /**
198
- * The recipe's key (`namespace/name`), which is how it is stored in an index,
199
- * a lockfile and a project's subscriptions.
200
- *
201
- * @param manifest - A validated recipe manifest.
202
- */
203
- export function recipeManifestKey(manifest: RecipeManifest): string {
204
- return `${manifest.namespace}/${manifest.name}`;
205
- }
@@ -1,97 +0,0 @@
1
- /**
2
- * The repo manifest: `sous.repo.yaml` (or `.yml`, or `.json`) at the root of a
3
- * repository. It is HAND-WRITTEN by the repo's maintainers and is the entry
4
- * point sous reads to learn what a repo publishes: its namespaces, and where
5
- * each recipe folder lives.
6
- *
7
- * The manifest is deliberately declarative and executable-free: it is data, so
8
- * sous can read, validate and browse it before the repository is trusted, in a
9
- * dry run and in `repo release --check` without executing anything. So
10
- * manifests are YAML or JSON only.
11
- */
12
-
13
- import { z } from "zod";
14
- import {
15
- extensibleObject,
16
- formatVersionSchema,
17
- namespaceNameSchema,
18
- parseFormat,
19
- relativePathSchema,
20
- repoNameSchema,
21
- submissionsSchema,
22
- } from "./common.js";
23
-
24
- /** A namespace declaration. Namespaces group recipes and are not versioned. */
25
- export const repoNamespaceSchema = extensibleObject({
26
- /** One-line, plain-language summary of what the namespace holds. */
27
- description: z.string().optional(),
28
- });
29
-
30
- /** Path to a recipe folder, relative to the repo root. */
31
- const recipePathSchema = relativePathSchema("a recipe path");
32
-
33
- /**
34
- * The repo manifest schema.
35
- *
36
- * `name` is only a SUGGESTED short name. The short name a project actually uses
37
- * is recorded in its own config by `sous repo add`, which defaults to the last
38
- * URL segment and can be overridden, so two repos suggesting the same name
39
- * never collide in a project.
40
- */
41
- export const repoManifestSchema = extensibleObject({
42
- formatVersion: formatVersionSchema,
43
- /** Suggested short name for the repo. */
44
- name: repoNameSchema,
45
- /** One-paragraph summary of the repo, shown by `sous repo list` and `sous repo search`. */
46
- description: z.string().optional(),
47
- /**
48
- * Where to send a contribution: a URL, or free text describing the process.
49
- * Surfaced when a provider does not support `sous repo submit`, so a
50
- * contributor is never left without a route.
51
- */
52
- contribute: z.string().min(1, "must not be empty").optional(),
53
- /**
54
- * Whether the repository's recipes take proposed changes. A recipe's own
55
- * `submissions` block wins over this one. `sous repo submit` warns before
56
- * proposing a change to a recipe that does not, and `sous repo release
57
- * --check` fails a pull request that changes one.
58
- */
59
- submissions: submissionsSchema.optional(),
60
- /** Every namespace the repo publishes, keyed by namespace name. */
61
- namespaces: z.record(namespaceNameSchema, repoNamespaceSchema),
62
- /**
63
- * Every recipe folder in the repo, as a path relative to the repo root. Each
64
- * folder must contain a recipe manifest (`sous.recipe.yaml` or a supported
65
- * variant); `sous repo release` verifies that and builds the index from it.
66
- */
67
- recipes: z.array(recipePathSchema).superRefine((paths, ctx) => {
68
- const seen = new Set<string>();
69
- paths.forEach((entry, index) => {
70
- if (seen.has(entry)) {
71
- ctx.addIssue({
72
- code: "custom",
73
- path: [index],
74
- message: `the recipe path '${entry}' is listed more than once`,
75
- });
76
- }
77
- seen.add(entry);
78
- });
79
- }),
80
- });
81
-
82
- /** A validated repo manifest. */
83
- export type RepoManifest = z.infer<typeof repoManifestSchema>;
84
-
85
- /** A validated namespace declaration from a repo manifest. */
86
- export type RepoNamespace = z.infer<typeof repoNamespaceSchema>;
87
-
88
- /**
89
- * Validates a parsed repo manifest, throwing a ConfigError that names the file
90
- * and the path of every bad field.
91
- *
92
- * @param value - The parsed contents of the manifest file.
93
- * @param sourceLabel - The manifest's file path, named in error messages.
94
- */
95
- export function parseRepoManifest(value: unknown, sourceLabel: string): RepoManifest {
96
- return parseFormat(repoManifestSchema, value, sourceLabel, "repo manifest");
97
- }