@sumrco/cli 0.2.0 → 0.3.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.
@@ -17,6 +17,10 @@ operations or services, define it once and reference it with `$ref`.
17
17
  This reduces drift in the source specs immediately. It also gives Kontract a
18
18
  clear path to emit more shared generated code as the generator evolves.
19
19
 
20
+ Before extracting or naming a shared component, choose a name from the domain
21
+ language. Shared names should stay specific enough for generated DTO/model/SDK
22
+ symbols.
23
+
20
24
  ## What to share
21
25
 
22
26
  Prefer shared components for concepts that mean the same thing everywhere:
@@ -80,6 +84,83 @@ content:
80
84
  $ref: ../shared/common.yml#/components/schemas/ImageMimeType
81
85
  ```
82
86
 
87
+ ## Sharing schemas across OpenAPI and AsyncAPI fragments
88
+
89
+ When one domain has both HTTP and messaging contracts, keep protocol entrypoints
90
+ separate and share only neutral business schemas.
91
+
92
+ ```text
93
+ schema/
94
+ bookstore/
95
+ bookstore.openapi.yml
96
+ bookstore.asyncapi.yml
97
+ paths/books.yml
98
+ messages/books.yml
99
+ models/shared.yml
100
+ models/books.yml
101
+ ```
102
+
103
+ Good shared fragment:
104
+
105
+ ```yaml
106
+ # schema/bookstore/models/shared.yml
107
+ BookSlug:
108
+ type: string
109
+ minLength: 1
110
+ maxLength: 120
111
+ pattern: "^[a-z0-9]+(?:-[a-z0-9]+)*$"
112
+
113
+ PaginationMeta:
114
+ $ref: ../../shared/common.yml#/components/schemas/PaginationMeta
115
+ ```
116
+
117
+ OpenAPI can use it through path and response fragments:
118
+
119
+ ```yaml
120
+ # schema/bookstore/paths/books.yml
121
+ paths:
122
+ /books/{bookSlug}:
123
+ parameters:
124
+ - name: bookSlug
125
+ in: path
126
+ required: true
127
+ schema:
128
+ $ref: ../models/shared.yml#/BookSlug
129
+ get:
130
+ operationId: getBook
131
+ responses:
132
+ "200":
133
+ description: Book detail.
134
+ content:
135
+ application/json:
136
+ schema:
137
+ $ref: ../models/books.yml#/Book
138
+ ```
139
+
140
+ AsyncAPI can use the same payload schema from a message fragment:
141
+
142
+ ```yaml
143
+ # schema/bookstore/messages/books.yml
144
+ messages:
145
+ GetBookQuery:
146
+ payload:
147
+ $ref: ../models/books.yml#/GetBookQuery
148
+ BookReply:
149
+ payload:
150
+ $ref: ../models/books.yml#/Book
151
+ ```
152
+
153
+ Keep protocol-specific pieces out of `models/`:
154
+
155
+ - OpenAPI paths, parameters, request bodies, responses → `paths/` or
156
+ `components/`.
157
+ - AsyncAPI channels, operations, messages → `messages/`.
158
+ - Payload objects, enums, scalars, pagination, and reusable business shapes →
159
+ `models/`.
160
+
161
+ This keeps HTTP DTOs, NATS zod messages, and generated models aligned without
162
+ making either protocol depend on the other's metadata.
163
+
83
164
  ## Response envelopes
84
165
 
85
166
  If every endpoint repeats the same envelope fields, do not retype them in every
@@ -211,6 +292,8 @@ components when:
211
292
  needs;
212
293
  - a request DTO and response DTO intentionally differ, even if fields overlap;
213
294
  - names would become vague (`CommonThing`, `BaseData`, `GenericConfig`);
295
+ - the reusable name would hide a real product term, such as using `Collection`
296
+ when collections are a domain concept in the product;
214
297
  - sharing would force unrelated teams/domains to coordinate every future change.
215
298
 
216
299
  A good shared component has a clear product meaning and a stable owner.
@@ -72,6 +72,10 @@ Split or optimize before adding more content when a file shows any of these:
72
72
  Do not split only by line count. First identify the product or technical
73
73
  boundary that gives the new file a stable name and owner.
74
74
 
75
+ Visual section comments can help readers navigate a healthy file, but they are
76
+ not a substitute for splitting an oversized or mixed-domain spec. If comments
77
+ start acting like a table of contents, split by domain/subdomain instead.
78
+
75
79
  ## Domain and subdomain spec structure
76
80
 
77
81
  Use directory nesting to communicate product ownership. The path should answer
@@ -93,14 +97,51 @@ schema/
93
97
  stations.openapi.yml
94
98
  prep-jobs.asyncapi.yml
95
99
  inventory.asyncapi.yml
100
+ bookstore/
101
+ bookstore.openapi.yml # OpenAPI root entrypoint
102
+ bookstore.asyncapi.yml # AsyncAPI root entrypoint
103
+ paths/
104
+ books.yml # HTTP paths by subdomain
105
+ authors.yml
106
+ messages/
107
+ books.yml # NATS channels/messages by subdomain
108
+ authors.yml
109
+ models/
110
+ shared.yml # model fragments shared by both roots
111
+ books.yml
112
+ authors.yml
96
113
  ```
97
114
 
115
+ When a folder already carries the domain name, prefer `*.schema.yml` for
116
+ subdomain surfaces that should generate as one domain package:
117
+
118
+ ```text
119
+ schema/
120
+ recipes/
121
+ catalog.schema.yml # openapi: 3.0.3
122
+ publishing.schema.yml # asyncapi: 3.0.0
123
+ inventory.schema.yml # asyncapi: 3.0.0
124
+ ```
125
+
126
+ Kontract content-detects the protocol marker and groups those files under the
127
+ first folder (`recipes` in this example). Use explicit `*.openapi.yml` or
128
+ `*.asyncapi.yml` when each nested file should remain an independently generated
129
+ service path.
130
+
131
+ When one domain should keep a single generated package but the contract is too
132
+ large for one file, keep the explicit root files and split the internals into
133
+ plain `.yml` fragments. This mirrors Redocly-style authoring: roots are the
134
+ public entrypoints, fragments are implementation detail, and schemas can be
135
+ shared by OpenAPI and AsyncAPI only when the business shape is protocol-neutral.
136
+
98
137
  Guidelines:
99
138
 
100
139
  - Keep one entry-point spec per bounded API surface, not one giant file per
101
140
  repo.
102
141
  - Split by product meaning: `recipes`, `ingredients`, `orders`, `jobs`, or
103
142
  `inventory`, not by generic words like `common`, `misc`, or `helpers`.
143
+ - Use short section markers only as editor landmarks inside a file that is still
144
+ small enough to review.
104
145
  - Use `schema/shared/` only for cross-domain concepts with stable semantics and
105
146
  an obvious owner.
106
147
  - Prefer domain-local shared components when a concept is reused only inside one
@@ -17,19 +17,49 @@ directory (default `schema/`).
17
17
  schema/
18
18
  recipes.openapi.yml ← one OpenAPI doc per service/domain
19
19
  recipes.asyncapi.yml ← one AsyncAPI doc per service/domain
20
+ recipes.schema.yml ← neutral suffix; detected by openapi:/asyncapi:
20
21
  base.yml ← shared components, referenced via $ref
22
+ bookstore/
23
+ bookstore.openapi.yml ← root entrypoint
24
+ bookstore.asyncapi.yml ← root entrypoint
25
+ paths/catalog.yml ← OpenAPI path fragments
26
+ messages/events.yml ← AsyncAPI channel/message fragments
27
+ models/shared.yml ← protocol-neutral model fragments
21
28
  ```
22
29
 
23
30
  ## Rules
24
31
 
25
32
  - OpenAPI specs end in `*.openapi.yml` / `*.openapi.yaml`.
26
33
  - AsyncAPI specs end in `*.asyncapi.yml` / `*.asyncapi.yaml`.
34
+ - Use `*.schema.yml` / `*.schema.yaml` when a domain folder already makes the
35
+ protocol obvious less useful than the business surface name. Kontract reads
36
+ the file marker and treats `openapi:` as OpenAPI and `asyncapi:` as AsyncAPI.
27
37
  - Discovery is recursive; `base.yml` is skipped as an entry point.
28
38
  - Service output paths mirror nested spec paths under the configured source
29
39
  input directory.
40
+ - A root `*.openapi.yml` or `*.asyncapi.yml` may reference plain `.yml`
41
+ fragments for paths, messages, parameters, and schemas. Kontract bundles the
42
+ root before generation when it finds external `$ref` links.
43
+ - OpenAPI and AsyncAPI roots may share neutral model fragments when the shapes
44
+ mean the same thing for HTTP and messaging. Keep protocol-specific path,
45
+ operation, parameter, channel, and message metadata in separate fragments.
46
+ - Nested `*.schema.yml` files are grouped by their first folder. This supports a
47
+ DDD layout where the folder is the domain and each file is a subdomain surface:
48
+
49
+ ```text
50
+ schema/
51
+ recipes/
52
+ catalog.schema.yml ← OpenAPI
53
+ publishing.schema.yml ← AsyncAPI
54
+ ```
55
+
56
+ Both files generate under `contracts/recipes/`.
30
57
  - Use nested domain/subdomain paths when one root file would mix independently
31
58
  owned API surfaces; see the scope and splitting reference for line-count
32
59
  thresholds and naming rules.
60
+ - Use comments only as editor navigation markers. Comments may label groups such
61
+ as `Catalog paths` or `Response schemas`, but they must not explain endpoint
62
+ behavior or replace `summary` / `description`.
33
63
  - Share common schemas through `$ref` into `base.yml`. Do **not** duplicate
34
64
  schemas across specs and do **not** merge by hand-mutating parsed JSON.
35
65
  - Prefer a `schema/shared/` folder for cross-domain components such as common
@@ -41,11 +71,174 @@ schema/
41
71
  with CLI fallback, AsyncAPI via `@asyncapi/cli bundle`); keep `$ref` paths
42
72
  repo-relative.
43
73
 
74
+ ## Split root-entrypoint layout
75
+
76
+ Use this layout when one product domain should generate as one contract package,
77
+ but the root files are getting too large:
78
+
79
+ ```text
80
+ schema/
81
+ bookstore/
82
+ bookstore.openapi.yml
83
+ bookstore.asyncapi.yml
84
+ components/
85
+ parameters.yml
86
+ paths/
87
+ books.yml
88
+ authors.yml
89
+ messages/
90
+ books.yml
91
+ authors.yml
92
+ models/
93
+ shared.yml
94
+ books.yml
95
+ authors.yml
96
+ ```
97
+
98
+ The root files stay small and readable. Fragment files are plain `.yml` because
99
+ they are not standalone OpenAPI or AsyncAPI documents.
100
+
101
+ If a root file needs many visual section markers to stay readable, split the
102
+ internals into domain/subdomain fragments before adding more comments.
103
+
104
+ ```yaml
105
+ # schema/bookstore/bookstore.openapi.yml
106
+ openapi: 3.0.3
107
+ info:
108
+ title: Bookstore Contracts
109
+ version: 1.0.0
110
+ paths:
111
+ /books:
112
+ $ref: ./paths/books.yml#/paths/~1books
113
+ components:
114
+ schemas:
115
+ Book:
116
+ $ref: ./models/books.yml#/Book
117
+ PaginatedBooks:
118
+ $ref: ./models/books.yml#/PaginatedBooks
119
+ ```
120
+
121
+ ```yaml
122
+ # schema/bookstore/paths/books.yml
123
+ paths:
124
+ /books:
125
+ get:
126
+ operationId: listBooks
127
+ responses:
128
+ "200":
129
+ description: Paginated book list.
130
+ content:
131
+ application/json:
132
+ schema:
133
+ $ref: ../models/books.yml#/PaginatedBooks
134
+ ```
135
+
136
+ ```yaml
137
+ # schema/bookstore/bookstore.asyncapi.yml
138
+ asyncapi: 3.0.0
139
+ info:
140
+ title: Bookstore
141
+ version: 1.0.0
142
+ channels:
143
+ listBooks:
144
+ $ref: ./messages/books.yml#/channels/listBooks
145
+ listBooksReply:
146
+ $ref: ./messages/books.yml#/channels/listBooksReply
147
+ operations:
148
+ listBooks:
149
+ $ref: ./messages/books.yml#/operations/listBooks
150
+ components:
151
+ messages:
152
+ ListBooksQuery:
153
+ $ref: ./messages/books.yml#/messages/ListBooksQuery
154
+ PaginatedBooksReply:
155
+ $ref: ./messages/books.yml#/messages/PaginatedBooksReply
156
+ schemas:
157
+ ListBooksQuery:
158
+ $ref: ./models/books.yml#/ListBooksQuery
159
+ PaginatedBooks:
160
+ $ref: ./models/books.yml#/PaginatedBooks
161
+ ```
162
+
163
+ ```yaml
164
+ # schema/bookstore/messages/books.yml
165
+ channels:
166
+ listBooks:
167
+ address: qry.bookstore.books.list
168
+ messages:
169
+ listBooksQuery:
170
+ $ref: ./books.yml#/messages/ListBooksQuery
171
+ listBooksReply:
172
+ address: qry.bookstore.books.list.reply
173
+ messages:
174
+ paginatedBooksReply:
175
+ $ref: ./books.yml#/messages/PaginatedBooksReply
176
+ operations:
177
+ listBooks:
178
+ action: receive
179
+ channel:
180
+ $ref: "#/channels/listBooks"
181
+ messages:
182
+ - $ref: "#/channels/listBooks/messages/listBooksQuery"
183
+ reply:
184
+ channel:
185
+ $ref: "#/channels/listBooksReply"
186
+ messages:
187
+ - $ref: "#/channels/listBooksReply/messages/paginatedBooksReply"
188
+ messages:
189
+ ListBooksQuery:
190
+ payload:
191
+ $ref: ../models/books.yml#/ListBooksQuery
192
+ PaginatedBooksReply:
193
+ payload:
194
+ $ref: ../models/books.yml#/PaginatedBooks
195
+ ```
196
+
197
+ Shared schemas can be referenced by both roots when they carry the same business
198
+ meaning:
199
+
200
+ ```yaml
201
+ # schema/bookstore/models/shared.yml
202
+ BookSlug:
203
+ type: string
204
+ minLength: 1
205
+ maxLength: 120
206
+ pattern: "^[a-z0-9]+(?:-[a-z0-9]+)*$"
207
+ ```
208
+
209
+ ```yaml
210
+ # schema/bookstore/models/books.yml
211
+ Book:
212
+ type: object
213
+ required: [id, slug, title]
214
+ properties:
215
+ id:
216
+ type: string
217
+ format: uuid
218
+ slug:
219
+ $ref: ./shared.yml#/BookSlug
220
+ title:
221
+ type: string
222
+ minLength: 1
223
+ ```
224
+
225
+ Do not put graph, routing, or protocol behavior into shared model fragments.
226
+ Share only business payload shapes.
227
+
44
228
  ## OpenAPI authoring rules
45
229
 
46
230
  - Put reusable HTTP payloads in `components.schemas`.
47
231
  - Operation request/response schemas should reference components with `$ref`.
48
232
  Inline operation schemas are rejected so generated DTO/model names stay stable.
233
+ - Use operation `summary` to name the action. Use `description` to explain
234
+ outcome, scope, caller context, constraints, or next steps; do not repeat the
235
+ summary in sentence form.
236
+ - Avoid tag echo. If the API docs already show a tag such as `Playbook` or
237
+ `Accounts`, omit that word from the summary unless the operation would become
238
+ ambiguous.
239
+ - Route public or developer-facing `summary`, `description`, parameter, schema,
240
+ and response text through the repo's copywriter/microcopy standard before
241
+ validating.
49
242
  - Prefer explicit object schemas over anonymous nested shapes when a payload is
50
243
  shared by multiple operations.
51
244
  - If multiple responses repeat the same envelope fields, extract the envelope
@@ -67,3 +260,16 @@ schema/
67
260
 
68
261
  Run `sumr kontract validate` after any layout change to confirm specs still
69
262
  resolve before generating.
263
+
264
+ ## AsyncAPI copy rules
265
+
266
+ AsyncAPI `title`, `summary`, and `description` fields can appear in generated
267
+ docs, SDK docs, and AI answers. Write them with the same copy gate as OpenAPI:
268
+
269
+ - `title` names the message or operation.
270
+ - `summary` states the job or event in one concise sentence.
271
+ - `description` adds context only when the outcome, constraint, or event
272
+ semantics need more detail.
273
+ - Do not repeat the channel, operation id, schema key, or title in the summary.
274
+ - Keep transport terms such as subject, payload, NATS, command, and query only
275
+ when the reader needs that exact implementation detail.
@@ -2,8 +2,9 @@
2
2
  category: team-member
3
3
  name: contract-author
4
4
  title: Contract Author
5
- description: "Owns API spec authoring and Kontract regeneration discipline: edits OpenAPI/AsyncAPI specs, validates, regenerates, and keeps generated contracts consistent without hand-editing outputs."
5
+ description: "Owns API spec authoring and Kontract regeneration discipline. Use when changing OpenAPI/AsyncAPI specs, validating, regenerating, or reviewing generated contract diffs."
6
6
  modelTier: reasoning
7
+ access: write
7
8
  channels:
8
9
  codex:
9
10
  sandbox_mode: workspace-write
@@ -16,18 +17,25 @@ and let Kontract generate the TypeScript — you never hand-write or hand-patch
16
17
  generated contracts.
17
18
 
18
19
  Apply the generated **sumr-kontract-usage** skill, plus the configuration,
19
- spec-layout, schema-reuse, scope-and-splitting, and generated-output
20
- references, before acting.
20
+ spec-layout, schema-reuse, scope-and-splitting, and generated-output references,
21
+ before acting.
21
22
 
22
23
  ## Responsibilities
23
24
 
24
25
  - Translate API requirements into OpenAPI/AsyncAPI specs under `schema/`.
25
26
  - After writing or updating any `*.openapi.yml` / `*.asyncapi.yml` file, review
26
27
  it against Kontract reuse, layout, and generation rules before validating.
28
+ - Route changed public or developer-facing summaries, descriptions, parameter
29
+ descriptions, response descriptions, message titles, and message summaries
30
+ through the repo's copywriter/microcopy standard before generation.
27
31
  - Keep shared shapes in shared component files and reference them with `$ref`.
28
32
  - Keep specs domain-aligned and reviewable: use domain/subdomain paths, split
29
33
  oversized files at documented thresholds, and avoid generic `common` buckets
30
34
  for unrelated concepts.
35
+ - Use comments only as visual editor landmarks. Do not explain endpoint behavior
36
+ with comments; use OpenAPI/AsyncAPI descriptions for that.
37
+ - Name schemas from domain meaning first, using operation-specific response
38
+ names only when the shape belongs to one operation.
31
39
  - Proactively spot duplicated envelope fields, pagination metadata, error
32
40
  schemas, path/query params, regex scalar constraints, schedules, and config
33
41
  objects before generating.
@@ -2,7 +2,9 @@
2
2
  category: workflow
3
3
  name: contract-change
4
4
  title: Contract Change
5
- description: "Guides an AI agent through safely changing an API spec and regenerating Kontract contracts."
5
+ description: "Guides an AI agent through safely changing an API spec and regenerating Kontract contracts. Use when an API surface changes and generated contracts must follow."
6
+ modelTier: coding
7
+ access: write
6
8
  ---
7
9
 
8
10
  # Contract Change
@@ -3,7 +3,7 @@ kind: CliModule
3
3
  metadata:
4
4
  name: kontract
5
5
  package: "@sumrco/cli"
6
- description: Generate and validate API contract code
6
+ description: Generate and validate API contract artifacts
7
7
  owners:
8
8
  - team: "@sumr-org/kontract-team"
9
9
  tags:
@@ -21,7 +21,7 @@ spec:
21
21
  summary: Activate Kontract AI guidance for this workspace
22
22
  visibility: public
23
23
  - name: generate
24
- summary: Generate TypeScript contracts from API specs
24
+ summary: Generate configured contract artifacts from API specs
25
25
  visibility: public
26
26
  - name: validate
27
27
  summary: Validate API specifications