@cleocode/contracts 2026.5.112 → 2026.5.113
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/cli-meta-suggested-next.test.d.ts +21 -0
- package/dist/__tests__/cli-meta-suggested-next.test.d.ts.map +1 -0
- package/dist/__tests__/cli-meta-suggested-next.test.js +70 -0
- package/dist/__tests__/cli-meta-suggested-next.test.js.map +1 -0
- package/dist/config/__tests__/manifest.test.d.ts +13 -0
- package/dist/config/__tests__/manifest.test.d.ts.map +1 -0
- package/dist/config/__tests__/manifest.test.js +143 -0
- package/dist/config/__tests__/manifest.test.js.map +1 -0
- package/dist/config/manifest.d.ts +158 -0
- package/dist/config/manifest.d.ts.map +1 -0
- package/dist/config/manifest.js +123 -0
- package/dist/config/manifest.js.map +1 -0
- package/dist/docs/__tests__/provenance.test.d.ts +14 -0
- package/dist/docs/__tests__/provenance.test.d.ts.map +1 -0
- package/dist/docs/__tests__/provenance.test.js +579 -0
- package/dist/docs/__tests__/provenance.test.js.map +1 -0
- package/dist/docs/provenance.d.ts +731 -0
- package/dist/docs/provenance.d.ts.map +1 -0
- package/dist/docs/provenance.js +258 -0
- package/dist/docs/provenance.js.map +1 -0
- package/dist/docs-taxonomy.d.ts +24 -0
- package/dist/docs-taxonomy.d.ts.map +1 -1
- package/dist/docs-taxonomy.js +27 -0
- package/dist/docs-taxonomy.js.map +1 -1
- package/dist/doctor.d.ts +101 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/errors.d.ts +4 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +4 -0
- package/dist/errors.js.map +1 -1
- package/dist/exit-codes.d.ts +37 -0
- package/dist/exit-codes.d.ts.map +1 -1
- package/dist/exit-codes.js +37 -0
- package/dist/exit-codes.js.map +1 -1
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/invariants/__tests__/adr-056-release.test.d.ts +16 -0
- package/dist/invariants/__tests__/adr-056-release.test.d.ts.map +1 -0
- package/dist/invariants/__tests__/adr-056-release.test.js +88 -0
- package/dist/invariants/__tests__/adr-056-release.test.js.map +1 -0
- package/dist/invariants/__tests__/adr-070-orchestration.test.d.ts +22 -0
- package/dist/invariants/__tests__/adr-070-orchestration.test.d.ts.map +1 -0
- package/dist/invariants/__tests__/adr-070-orchestration.test.js +151 -0
- package/dist/invariants/__tests__/adr-070-orchestration.test.js.map +1 -0
- package/dist/invariants/adr-056-release.d.ts +42 -0
- package/dist/invariants/adr-056-release.d.ts.map +1 -0
- package/dist/invariants/adr-056-release.js +143 -0
- package/dist/invariants/adr-056-release.js.map +1 -0
- package/dist/invariants/adr-070-orchestration.d.ts +73 -0
- package/dist/invariants/adr-070-orchestration.d.ts.map +1 -0
- package/dist/invariants/adr-070-orchestration.js +290 -0
- package/dist/invariants/adr-070-orchestration.js.map +1 -0
- package/dist/invariants/index.d.ts +4 -1
- package/dist/invariants/index.d.ts.map +1 -1
- package/dist/invariants/index.js +11 -2
- package/dist/invariants/index.js.map +1 -1
- package/dist/operations/__tests__/input-contract.test.d.ts +20 -0
- package/dist/operations/__tests__/input-contract.test.d.ts.map +1 -0
- package/dist/operations/__tests__/input-contract.test.js +158 -0
- package/dist/operations/__tests__/input-contract.test.js.map +1 -0
- package/dist/operations/docs.d.ts +16 -0
- package/dist/operations/docs.d.ts.map +1 -1
- package/dist/operations/input-contract.d.ts +267 -0
- package/dist/operations/input-contract.d.ts.map +1 -0
- package/dist/operations/input-contract.js +29 -0
- package/dist/operations/input-contract.js.map +1 -0
- package/dist/templates/__tests__/manifest.test.d.ts +18 -0
- package/dist/templates/__tests__/manifest.test.d.ts.map +1 -0
- package/dist/templates/__tests__/manifest.test.js +123 -0
- package/dist/templates/__tests__/manifest.test.js.map +1 -0
- package/dist/templates/manifest.d.ts +186 -0
- package/dist/templates/manifest.d.ts.map +1 -0
- package/dist/templates/manifest.js +165 -0
- package/dist/templates/manifest.js.map +1 -0
- package/package.json +2 -2
- package/src/__tests__/cli-meta-suggested-next.test.ts +76 -0
- package/src/config/__tests__/manifest.test.ts +176 -0
- package/src/config/manifest.ts +218 -0
- package/src/docs/__tests__/__snapshots__/provenance.test.ts.snap +252 -0
- package/src/docs/__tests__/provenance.test.ts +648 -0
- package/src/docs/provenance.ts +601 -0
- package/src/docs-taxonomy.ts +55 -0
- package/src/doctor.ts +111 -0
- package/src/errors.ts +4 -0
- package/src/exit-codes.ts +39 -0
- package/src/index.ts +90 -0
- package/src/invariants/__tests__/adr-056-release.test.ts +102 -0
- package/src/invariants/__tests__/adr-070-orchestration.test.ts +186 -0
- package/src/invariants/adr-056-release.ts +154 -0
- package/src/invariants/adr-070-orchestration.ts +317 -0
- package/src/invariants/index.ts +11 -2
- package/src/operations/__tests__/input-contract.test.ts +188 -0
- package/src/operations/docs.ts +16 -0
- package/src/operations/input-contract.ts +301 -0
- package/src/templates/__tests__/manifest.test.ts +140 -0
- package/src/templates/manifest.ts +208 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TemplateManifest contract — SSoT for every template CLEO ships, installs,
|
|
3
|
+
* or rewrites during `cleo init` / `cleo upgrade` / `cleo scaffold-*`.
|
|
4
|
+
*
|
|
5
|
+
* A {@link TemplateManifestEntry} declares one template file: where its
|
|
6
|
+
* source lives in the monorepo, where it installs into a consumer project,
|
|
7
|
+
* how its placeholders are substituted, which placeholders it accepts, and
|
|
8
|
+
* how it MUST be reconciled when the shipped version moves ahead of the
|
|
9
|
+
* deployed copy.
|
|
10
|
+
*
|
|
11
|
+
* This contract is the leaf type. The CORE-side template registry
|
|
12
|
+
* (T9877) consumes it; the CLI verbs (`cleo init --workflows`,
|
|
13
|
+
* `cleo upgrade`, the scaffold sweeper) read the manifest at runtime to
|
|
14
|
+
* decide what to write, what to skip, and what to diff-prompt the user on.
|
|
15
|
+
*
|
|
16
|
+
* @example minimal workflow template entry
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { TemplateManifestEntrySchema, type TemplateManifestEntry } from '@cleocode/contracts';
|
|
19
|
+
*
|
|
20
|
+
* const entry: TemplateManifestEntry = TemplateManifestEntrySchema.parse({
|
|
21
|
+
* id: 'ci.yml',
|
|
22
|
+
* kind: 'workflow',
|
|
23
|
+
* sourcePath: 'packages/core/templates/workflows/ci.yml.tmpl',
|
|
24
|
+
* installPath: '.github/workflows/ci.yml',
|
|
25
|
+
* substitution: 'regex-tmpl',
|
|
26
|
+
* placeholders: [
|
|
27
|
+
* {
|
|
28
|
+
* name: 'NODE_VERSION',
|
|
29
|
+
* source: 'project-context',
|
|
30
|
+
* sourcePath: 'engines.node',
|
|
31
|
+
* defaultValue: 24,
|
|
32
|
+
* },
|
|
33
|
+
* ],
|
|
34
|
+
* updateStrategy: 'overwrite-on-bump',
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @task T9875
|
|
39
|
+
* @epic T9874
|
|
40
|
+
* @saga T9855
|
|
41
|
+
* @see ADR-076 (canonical doc routing — same boundary discipline applies here)
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
import { z } from 'zod';
|
|
45
|
+
|
|
46
|
+
// ─── TemplateKind ─────────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Allowed categories of template the manifest can describe.
|
|
50
|
+
*
|
|
51
|
+
* - `workflow` — GitHub Actions / CI workflow files.
|
|
52
|
+
* - `config` — Configuration files (e.g. `.editorconfig`, `tsconfig.json`).
|
|
53
|
+
* - `agent` — Agent definition files shipped to `.claude/agents/` etc.
|
|
54
|
+
* - `skill` — Skill markdown shipped to `.claude/skills/` etc.
|
|
55
|
+
* - `provider` — Provider-specific bootstrap files (Anthropic, OpenAI, …).
|
|
56
|
+
* - `doc` — Documentation scaffolds (e.g. README.md, AGENTS.md).
|
|
57
|
+
*/
|
|
58
|
+
export const TEMPLATE_KINDS = ['workflow', 'config', 'agent', 'skill', 'provider', 'doc'] as const;
|
|
59
|
+
|
|
60
|
+
/** Discriminator for the category of file a template represents. */
|
|
61
|
+
export type TemplateKind = (typeof TEMPLATE_KINDS)[number];
|
|
62
|
+
|
|
63
|
+
// ─── TemplateSubstitution ─────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Allowed substitution strategies a template can request.
|
|
67
|
+
*
|
|
68
|
+
* - `regex-tmpl` — `{{KEY}}` placeholder substitution via regex.
|
|
69
|
+
* - `static` — No substitution; the file is copied byte-for-byte.
|
|
70
|
+
* - `json-merge` — The template is parsed as JSON and merged into any
|
|
71
|
+
* existing JSON file at `installPath`; placeholders inside
|
|
72
|
+
* string values still go through `regex-tmpl`.
|
|
73
|
+
*/
|
|
74
|
+
export const TEMPLATE_SUBSTITUTIONS = ['regex-tmpl', 'static', 'json-merge'] as const;
|
|
75
|
+
|
|
76
|
+
/** Strategy the installer uses to materialize a template's `sourcePath`. */
|
|
77
|
+
export type TemplateSubstitution = (typeof TEMPLATE_SUBSTITUTIONS)[number];
|
|
78
|
+
|
|
79
|
+
// ─── UpdateStrategy ───────────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* How an installed template is reconciled with the shipped copy on
|
|
83
|
+
* `cleo upgrade` / `cleo init --force`.
|
|
84
|
+
*
|
|
85
|
+
* - `overwrite-on-bump` — Always overwrite when the manifest version is
|
|
86
|
+
* newer than the deployed copy.
|
|
87
|
+
* - `diff-prompt` — Show a diff and ask the user before overwriting.
|
|
88
|
+
* - `immutable` — Never overwrite once installed.
|
|
89
|
+
* - `manifest-merge` — Apply structural merge (e.g. JSON deep-merge,
|
|
90
|
+
* respecting existing user keys).
|
|
91
|
+
*/
|
|
92
|
+
export const TEMPLATE_UPDATE_STRATEGIES = [
|
|
93
|
+
'overwrite-on-bump',
|
|
94
|
+
'diff-prompt',
|
|
95
|
+
'immutable',
|
|
96
|
+
'manifest-merge',
|
|
97
|
+
] as const;
|
|
98
|
+
|
|
99
|
+
/** Reconciliation policy for an installed template on upgrade. */
|
|
100
|
+
export type UpdateStrategy = (typeof TEMPLATE_UPDATE_STRATEGIES)[number];
|
|
101
|
+
|
|
102
|
+
// ─── PlaceholderSpec ──────────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Allowed resolver sources a {@link PlaceholderSpec} can reference.
|
|
106
|
+
*
|
|
107
|
+
* - `project-context` — `.cleo/project-context.json` (detected ecosystem hints).
|
|
108
|
+
* - `project-info` — `.cleo/project-info.json` (per-project metadata).
|
|
109
|
+
* - `.cleo/config` — Per-project config (`<projectRoot>/.cleo/config.json`).
|
|
110
|
+
* - `~/.cleo/config` — User-global config (`$XDG_CONFIG_HOME/cleo/config.json`).
|
|
111
|
+
* - `tool-resolver` — Resolved via the per-language tool resolver
|
|
112
|
+
* (e.g. `pnpm` vs `npm` chosen by `primaryType`).
|
|
113
|
+
* - `literal` — Hard-coded value baked into the manifest entry.
|
|
114
|
+
*/
|
|
115
|
+
export const PLACEHOLDER_SOURCES = [
|
|
116
|
+
'project-context',
|
|
117
|
+
'project-info',
|
|
118
|
+
'.cleo/config',
|
|
119
|
+
'~/.cleo/config',
|
|
120
|
+
'tool-resolver',
|
|
121
|
+
'literal',
|
|
122
|
+
] as const;
|
|
123
|
+
|
|
124
|
+
/** Resolver source enum for {@link PlaceholderSpec.source}. */
|
|
125
|
+
export type PlaceholderSource = (typeof PLACEHOLDER_SOURCES)[number];
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Zod schema for a single placeholder declaration on a template entry.
|
|
129
|
+
*
|
|
130
|
+
* Each placeholder is resolved at install time by reading `sourcePath` from
|
|
131
|
+
* the named `source`. If resolution returns `undefined`, the installer
|
|
132
|
+
* falls back to `defaultValue`; if neither yields a value, installation
|
|
133
|
+
* fails with `E_PLACEHOLDER_UNRESOLVED`.
|
|
134
|
+
*/
|
|
135
|
+
export const PlaceholderSpecSchema = z.object({
|
|
136
|
+
/**
|
|
137
|
+
* Placeholder identifier as it appears in the template body
|
|
138
|
+
* (e.g. `NODE_VERSION` matches `{{NODE_VERSION}}`).
|
|
139
|
+
*/
|
|
140
|
+
name: z.string().min(1, 'placeholder name must be non-empty'),
|
|
141
|
+
/** Resolver source the installer consults for this placeholder. */
|
|
142
|
+
source: z.enum(PLACEHOLDER_SOURCES),
|
|
143
|
+
/**
|
|
144
|
+
* Path expression evaluated against `source` (e.g. `engines.node` against
|
|
145
|
+
* `project-context`, `defaults.branchModel` against `.cleo/config`).
|
|
146
|
+
* For `literal` source, this MAY be the literal value's identifier.
|
|
147
|
+
*/
|
|
148
|
+
sourcePath: z.string().min(1, 'placeholder sourcePath must be non-empty'),
|
|
149
|
+
/**
|
|
150
|
+
* Fallback value used when `source[sourcePath]` resolves to `undefined`.
|
|
151
|
+
* `null` is permitted to explicitly mark "no default — failure required".
|
|
152
|
+
*/
|
|
153
|
+
defaultValue: z.union([z.string(), z.number(), z.boolean(), z.null()]).optional(),
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Zod-inferred input shape for a placeholder declaration.
|
|
158
|
+
*
|
|
159
|
+
* Identical to {@link PlaceholderSpec} — there are no schema transforms.
|
|
160
|
+
*/
|
|
161
|
+
export type PlaceholderSpecInput = z.infer<typeof PlaceholderSpecSchema>;
|
|
162
|
+
|
|
163
|
+
/** Declared placeholder on a {@link TemplateManifestEntry}. */
|
|
164
|
+
export type PlaceholderSpec = PlaceholderSpecInput;
|
|
165
|
+
|
|
166
|
+
// ─── TemplateManifestEntry ────────────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Zod schema for a single template manifest entry.
|
|
170
|
+
*
|
|
171
|
+
* Validates the full descriptor a template registry consumer needs to
|
|
172
|
+
* install a template file:
|
|
173
|
+
*
|
|
174
|
+
* - `id` — Stable identifier (kebab-case, matches install basename
|
|
175
|
+
* or a domain-unique slug).
|
|
176
|
+
* - `kind` — One of {@link TEMPLATE_KINDS}.
|
|
177
|
+
* - `sourcePath` — Repo-relative path of the template source.
|
|
178
|
+
* - `installPath` — Project-relative path where the rendered file lands.
|
|
179
|
+
* - `substitution` — One of {@link TEMPLATE_SUBSTITUTIONS}.
|
|
180
|
+
* - `placeholders` — Declared placeholders the installer must resolve.
|
|
181
|
+
* - `updateStrategy` — Reconciliation policy on upgrade.
|
|
182
|
+
*/
|
|
183
|
+
export const TemplateManifestEntrySchema = z.object({
|
|
184
|
+
/** Stable identifier for this template entry. */
|
|
185
|
+
id: z.string().min(1, 'id must be non-empty'),
|
|
186
|
+
/** Category of file this template represents. */
|
|
187
|
+
kind: z.enum(TEMPLATE_KINDS),
|
|
188
|
+
/** Repo-relative path of the template source file. */
|
|
189
|
+
sourcePath: z.string().min(1, 'sourcePath must be non-empty'),
|
|
190
|
+
/** Project-relative path where the rendered template installs. */
|
|
191
|
+
installPath: z.string().min(1, 'installPath must be non-empty'),
|
|
192
|
+
/** Substitution strategy the installer applies to `sourcePath`. */
|
|
193
|
+
substitution: z.enum(TEMPLATE_SUBSTITUTIONS),
|
|
194
|
+
/** Declared placeholders this template requires. May be empty. */
|
|
195
|
+
placeholders: z.array(PlaceholderSpecSchema),
|
|
196
|
+
/** Reconciliation policy on upgrade. */
|
|
197
|
+
updateStrategy: z.enum(TEMPLATE_UPDATE_STRATEGIES),
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Zod-inferred input shape for a template manifest entry.
|
|
202
|
+
*
|
|
203
|
+
* Identical to {@link TemplateManifestEntry} — there are no schema transforms.
|
|
204
|
+
*/
|
|
205
|
+
export type TemplateManifestEntryInput = z.infer<typeof TemplateManifestEntrySchema>;
|
|
206
|
+
|
|
207
|
+
/** Public type for a fully-validated template manifest entry. */
|
|
208
|
+
export type TemplateManifestEntry = TemplateManifestEntryInput;
|