contentbase 0.0.4 → 0.0.6
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/package.json +3 -2
- package/.mcp.json +0 -9
- package/MCP-DESCRIPTIONS-REVIEW.md +0 -122
- package/bun.lock +0 -512
- package/dist/cnotes +0 -0
- package/examples/sdlc-queries.ts +0 -161
- package/public/web-demo/index.html +0 -813
- package/scripts/examples/01-collection-setup.ts +0 -46
- package/scripts/examples/02-querying.ts +0 -67
- package/scripts/examples/03-sections.ts +0 -36
- package/scripts/examples/04-relationships.ts +0 -54
- package/scripts/examples/05-document-api.ts +0 -54
- package/scripts/examples/06-extract-sections.ts +0 -55
- package/scripts/examples/07-validation.ts +0 -46
- package/scripts/examples/08-serialization.ts +0 -51
- package/scripts/examples/lib/format.ts +0 -87
- package/scripts/examples/lib/setup.ts +0 -21
- package/scripts/examples/run-all.ts +0 -43
- package/showcases/national-parks/models.ts +0 -74
- package/showcases/national-parks/parks/acadia.mdx +0 -40
- package/showcases/national-parks/parks/yosemite.mdx +0 -44
- package/showcases/national-parks/parks/zion.mdx +0 -44
- package/showcases/national-parks/queries.ts +0 -103
- package/showcases/national-parks/trails/angels-landing.mdx +0 -19
- package/showcases/national-parks/trails/cathedral-lakes.mdx +0 -19
- package/showcases/national-parks/trails/half-dome.mdx +0 -19
- package/showcases/national-parks/trails/jordan-pond-path.mdx +0 -19
- package/showcases/national-parks/trails/mist-trail.mdx +0 -19
- package/showcases/national-parks/trails/observation-point.mdx +0 -19
- package/showcases/national-parks/trails/precipice-trail.mdx +0 -19
- package/showcases/national-parks/trails/the-narrows.mdx +0 -19
- package/showcases/node_modules/.cache/.repl_history +0 -3
- package/showcases/recipes/cuisines/chinese.mdx +0 -28
- package/showcases/recipes/cuisines/italian.mdx +0 -32
- package/showcases/recipes/cuisines/mexican.mdx +0 -28
- package/showcases/recipes/models.ts +0 -77
- package/showcases/recipes/queries.ts +0 -89
- package/showcases/recipes/recipes/chinese/egg-fried-rice.mdx +0 -43
- package/showcases/recipes/recipes/chinese/mapo-tofu.mdx +0 -47
- package/showcases/recipes/recipes/italian/bruschetta.mdx +0 -38
- package/showcases/recipes/recipes/italian/cacio-e-pepe.mdx +0 -39
- package/showcases/recipes/recipes/italian/tiramisu.mdx +0 -43
- package/showcases/recipes/recipes/mexican/chicken-tinga.mdx +0 -44
- package/showcases/recipes/recipes/mexican/guacamole.mdx +0 -39
- package/showcases/vinyl-collection/albums/bitches-brew.mdx +0 -36
- package/showcases/vinyl-collection/albums/i-put-a-spell-on-you.mdx +0 -35
- package/showcases/vinyl-collection/albums/in-rainbows.mdx +0 -35
- package/showcases/vinyl-collection/albums/kind-of-blue.mdx +0 -32
- package/showcases/vinyl-collection/albums/ok-computer.mdx +0 -37
- package/showcases/vinyl-collection/albums/wild-is-the-wind.mdx +0 -35
- package/showcases/vinyl-collection/artists/miles-davis.mdx +0 -27
- package/showcases/vinyl-collection/artists/nina-simone.mdx +0 -26
- package/showcases/vinyl-collection/artists/radiohead.mdx +0 -27
- package/showcases/vinyl-collection/models.ts +0 -73
- package/showcases/vinyl-collection/queries.ts +0 -87
- package/src/__tests__/semantic-search.integration.test.ts +0 -284
- package/test/ast-query.test.ts +0 -128
- package/test/collection.test.ts +0 -215
- package/test/define-model.test.ts +0 -78
- package/test/document.test.ts +0 -225
- package/test/extract-sections.test.ts +0 -356
- package/test/fixtures/sdlc/MODELS.md +0 -106
- package/test/fixtures/sdlc/SKILL.md +0 -404
- package/test/fixtures/sdlc/epics/authentication.mdx +0 -42
- package/test/fixtures/sdlc/epics/searching-and-browsing.mdx +0 -21
- package/test/fixtures/sdlc/index.ts +0 -9
- package/test/fixtures/sdlc/models.ts +0 -91
- package/test/fixtures/sdlc/stories/authentication/a-user-should-be-able-to-login.mdx +0 -20
- package/test/fixtures/sdlc/stories/authentication/a-user-should-be-able-to-register.mdx +0 -20
- package/test/fixtures/sdlc/templates/epic.md +0 -23
- package/test/fixtures/sdlc/templates/story.md +0 -19
- package/test/helpers.ts +0 -21
- package/test/model-instance.test.ts +0 -197
- package/test/pattern.test.ts +0 -191
- package/test/query-dsl.test.ts +0 -431
- package/test/query.test.ts +0 -196
- package/test/relationships.test.ts +0 -214
- package/test/section.test.ts +0 -160
- package/test/table-of-contents.test.ts +0 -135
- package/test/validator.test.ts +0 -62
- package/tsconfig.json +0 -18
- package/vitest.config.ts +0 -11
|
@@ -1,404 +0,0 @@
|
|
|
1
|
-
# SDLC Collection Skill
|
|
2
|
-
|
|
3
|
-
You have access to a loaded contentbase collection that models a software development lifecycle (SDLC) with Epics and Stories as markdown files.
|
|
4
|
-
|
|
5
|
-
## Setup
|
|
6
|
-
|
|
7
|
-
The collection is already loaded and available as `collection`. The models `Epic` and `Story` are already imported.
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
import { Collection } from "contentbase";
|
|
11
|
-
import { Epic, Story } from "./models";
|
|
12
|
-
|
|
13
|
-
const collection = new Collection({ rootPath: "./content" });
|
|
14
|
-
collection.register(Epic);
|
|
15
|
-
collection.register(Story);
|
|
16
|
-
await collection.load();
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Models
|
|
20
|
-
|
|
21
|
-
### Epic
|
|
22
|
-
|
|
23
|
-
**Prefix:** `epics` — files live in `epics/` (e.g. `epics/authentication.mdx`)
|
|
24
|
-
|
|
25
|
-
| Field | Type | Required | Default |
|
|
26
|
-
|-------|------|----------|---------|
|
|
27
|
-
| priority | `"low"` \| `"medium"` \| `"high"` | optional | — |
|
|
28
|
-
| status | `"created"` \| `"in-progress"` \| `"complete"` | optional | `"created"` |
|
|
29
|
-
|
|
30
|
-
**Relationships:**
|
|
31
|
-
- `stories` — **hasMany** Story (extracted from the `## Stories` heading in the epic's markdown)
|
|
32
|
-
|
|
33
|
-
**Computed:**
|
|
34
|
-
- `isComplete` — boolean, true when `status === "complete"`
|
|
35
|
-
|
|
36
|
-
### Story
|
|
37
|
-
|
|
38
|
-
**Prefix:** `stories` — files live in `stories/` (e.g. `stories/authentication/a-user-should-be-able-to-register.mdx`)
|
|
39
|
-
|
|
40
|
-
| Field | Type | Required | Default |
|
|
41
|
-
|-------|------|----------|---------|
|
|
42
|
-
| status | `"created"` \| `"in-progress"` \| `"complete"` | optional | `"created"` |
|
|
43
|
-
| epic | string | optional | — |
|
|
44
|
-
|
|
45
|
-
**Sections:**
|
|
46
|
-
- `acceptanceCriteria` — heading: "Acceptance Criteria" — returns `string[]` (list items)
|
|
47
|
-
- `mockups` — heading: "Mockups" — returns `Record<string, string>` (link label → URL)
|
|
48
|
-
|
|
49
|
-
**Relationships:**
|
|
50
|
-
- `epic` — **belongsTo** Epic (via `meta.epic` foreign key)
|
|
51
|
-
|
|
52
|
-
**Computed:**
|
|
53
|
-
- `isComplete` — boolean, true when `status === "complete"`
|
|
54
|
-
|
|
55
|
-
## Example Documents
|
|
56
|
-
|
|
57
|
-
An Epic markdown file (`epics/authentication.mdx`):
|
|
58
|
-
|
|
59
|
-
```mdx
|
|
60
|
-
---
|
|
61
|
-
priority: high
|
|
62
|
-
status: created
|
|
63
|
-
---
|
|
64
|
-
|
|
65
|
-
# Authentication
|
|
66
|
-
|
|
67
|
-
The Authentication stories cover users logging in and out of the application.
|
|
68
|
-
|
|
69
|
-
## Stories
|
|
70
|
-
|
|
71
|
-
### A User should be able to register.
|
|
72
|
-
|
|
73
|
-
As a User I would like to register so that I can use the application.
|
|
74
|
-
|
|
75
|
-
#### Acceptance Criteria
|
|
76
|
-
|
|
77
|
-
- A user can visit the signup form, supply their name, email, and password
|
|
78
|
-
- The signup form should validate the user's information and supply errors
|
|
79
|
-
|
|
80
|
-
#### Mockups
|
|
81
|
-
|
|
82
|
-
- [Invision: Registration Form](https://invisionapp.com)
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
A Story markdown file (`stories/authentication/a-user-should-be-able-to-register.mdx`):
|
|
86
|
-
|
|
87
|
-
```mdx
|
|
88
|
-
---
|
|
89
|
-
status: created
|
|
90
|
-
epic: authentication
|
|
91
|
-
---
|
|
92
|
-
|
|
93
|
-
# A User should be able to register.
|
|
94
|
-
|
|
95
|
-
As a User I would like to register so that I can use the application.
|
|
96
|
-
|
|
97
|
-
## Acceptance Criteria
|
|
98
|
-
|
|
99
|
-
- A user can visit the signup form, supply their name, email, and password
|
|
100
|
-
- The signup form should validate the user's information and supply errors
|
|
101
|
-
- The user should receive a confirmation email
|
|
102
|
-
|
|
103
|
-
## Mockups
|
|
104
|
-
|
|
105
|
-
- [Invision: Registration Form](https://invisionapp.com)
|
|
106
|
-
- [Invision: Registration Form Error State](https://invisionapp.com)
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## Querying
|
|
110
|
-
|
|
111
|
-
### Fetch all instances of a model
|
|
112
|
-
|
|
113
|
-
```ts
|
|
114
|
-
const allEpics = await collection.query(Epic).fetchAll();
|
|
115
|
-
const allStories = await collection.query(Story).fetchAll();
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### Filter with where clauses
|
|
119
|
-
|
|
120
|
-
The `where` method supports three call signatures:
|
|
121
|
-
|
|
122
|
-
```ts
|
|
123
|
-
// Two-arg: implicit equality
|
|
124
|
-
.where("meta.status", "created")
|
|
125
|
-
|
|
126
|
-
// Three-arg: explicit operator
|
|
127
|
-
.where("meta.priority", "neq", "low")
|
|
128
|
-
|
|
129
|
-
// Object shorthand: multiple equality conditions
|
|
130
|
-
.where({ "meta.status": "created", "meta.priority": "high" })
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
Conditions are AND-ed together when chained:
|
|
134
|
-
|
|
135
|
-
```ts
|
|
136
|
-
const results = await collection.query(Epic)
|
|
137
|
-
.where("meta.status", "created")
|
|
138
|
-
.where("meta.priority", "high")
|
|
139
|
-
.fetchAll();
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### Available operators
|
|
143
|
-
|
|
144
|
-
| Operator | Description | Example |
|
|
145
|
-
|----------|-------------|---------|
|
|
146
|
-
| `eq` | Equality (default) | `.where("meta.status", "created")` |
|
|
147
|
-
| `neq` | Not equal | `.where("meta.status", "neq", "complete")` |
|
|
148
|
-
| `gt` | Greater than | `.where("meta.priority", "gt", "low")` |
|
|
149
|
-
| `lt` | Less than | `.where("meta.priority", "lt", "high")` |
|
|
150
|
-
| `gte` | Greater than or equal | `.whereGte("meta.priority", "medium")` |
|
|
151
|
-
| `lte` | Less than or equal | `.whereLte("meta.priority", "medium")` |
|
|
152
|
-
| `in` | Value in array | `.whereIn("meta.status", ["created", "in-progress"])` |
|
|
153
|
-
| `notIn` | Value not in array | `.whereNotIn("meta.status", ["complete"])` |
|
|
154
|
-
| `contains` | String contains | `.whereContains("title", "auth")` |
|
|
155
|
-
| `startsWith` | String starts with | `.whereStartsWith("id", "epics/")` |
|
|
156
|
-
| `endsWith` | String ends with | `.whereEndsWith("title", "register.")` |
|
|
157
|
-
| `regex` | Regex match | `.whereRegex("title", /auth/i)` |
|
|
158
|
-
| `exists` | Field is defined | `.whereExists("meta.priority")` |
|
|
159
|
-
| | Field is not defined | `.whereNotExists("meta.priority")` |
|
|
160
|
-
|
|
161
|
-
### Result methods
|
|
162
|
-
|
|
163
|
-
```ts
|
|
164
|
-
const all = await collection.query(Epic).fetchAll(); // Epic[]
|
|
165
|
-
const first = await collection.query(Epic).first(); // Epic | undefined
|
|
166
|
-
const last = await collection.query(Epic).last(); // Epic | undefined
|
|
167
|
-
const count = await collection.query(Epic).count(); // number
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
### Query paths
|
|
171
|
-
|
|
172
|
-
Where clauses use dot-notation to reach nested properties on the model instance. Valid paths include:
|
|
173
|
-
|
|
174
|
-
- `id` — the document path ID (e.g. `"epics/authentication"`)
|
|
175
|
-
- `title` — the document title (from first heading)
|
|
176
|
-
- `slug` — kebab-cased title
|
|
177
|
-
- `meta.status` — frontmatter fields
|
|
178
|
-
- `meta.priority`
|
|
179
|
-
- `computed.isComplete`
|
|
180
|
-
|
|
181
|
-
## Model Instance Properties
|
|
182
|
-
|
|
183
|
-
When you get a model instance (from a query, `getModel`, or a relationship), it has these properties:
|
|
184
|
-
|
|
185
|
-
```ts
|
|
186
|
-
const epic = await collection.query(Epic).first();
|
|
187
|
-
|
|
188
|
-
epic.id // "epics/authentication"
|
|
189
|
-
epic.title // "Authentication"
|
|
190
|
-
epic.slug // "authentication"
|
|
191
|
-
epic.meta.status // "created"
|
|
192
|
-
epic.meta.priority // "high" | undefined
|
|
193
|
-
|
|
194
|
-
// Computed properties
|
|
195
|
-
epic.computed.isComplete // false
|
|
196
|
-
|
|
197
|
-
// Sections (Story only)
|
|
198
|
-
const story = await collection.query(Story).first();
|
|
199
|
-
story.sections.acceptanceCriteria // ["A user can visit...", "The signup form..."]
|
|
200
|
-
story.sections.mockups // { "Invision: Registration Form": "https://..." }
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
### Get a specific instance by path ID
|
|
204
|
-
|
|
205
|
-
```ts
|
|
206
|
-
const epic = collection.getModel("epics/authentication", Epic);
|
|
207
|
-
const story = collection.getModel("stories/authentication/a-user-should-be-able-to-register", Story);
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
## Relationships
|
|
211
|
-
|
|
212
|
-
### hasMany (Epic → Stories)
|
|
213
|
-
|
|
214
|
-
```ts
|
|
215
|
-
const epic = collection.getModel("epics/authentication", Epic);
|
|
216
|
-
|
|
217
|
-
const stories = epic.relationships.stories.fetchAll(); // Story[]
|
|
218
|
-
const first = epic.relationships.stories.first(); // Story | undefined
|
|
219
|
-
const last = epic.relationships.stories.last(); // Story | undefined
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
Note: `hasMany` extracts child documents from sub-headings within the parent's markdown. Each `### Heading` under `## Stories` becomes a Story instance.
|
|
223
|
-
|
|
224
|
-
### belongsTo (Story → Epic)
|
|
225
|
-
|
|
226
|
-
```ts
|
|
227
|
-
const story = collection.getModel(
|
|
228
|
-
"stories/authentication/a-user-should-be-able-to-register",
|
|
229
|
-
Story
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
const parentEpic = story.relationships.epic.fetch(); // Epic
|
|
233
|
-
parentEpic.title // "Authentication"
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
## Validation
|
|
237
|
-
|
|
238
|
-
```ts
|
|
239
|
-
const result = await story.validate();
|
|
240
|
-
result.valid // boolean
|
|
241
|
-
result.errors // ZodIssue[] — empty if valid
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
## Serialization
|
|
245
|
-
|
|
246
|
-
```ts
|
|
247
|
-
// Basic: id, title, meta
|
|
248
|
-
epic.toJSON()
|
|
249
|
-
|
|
250
|
-
// Include computed properties
|
|
251
|
-
epic.toJSON({ computed: ["isComplete"] })
|
|
252
|
-
|
|
253
|
-
// Include related models (serialized recursively)
|
|
254
|
-
epic.toJSON({ related: ["stories"] })
|
|
255
|
-
|
|
256
|
-
// Include sections
|
|
257
|
-
story.toJSON({ sections: ["acceptanceCriteria", "mockups"] })
|
|
258
|
-
|
|
259
|
-
// Combine all
|
|
260
|
-
story.toJSON({
|
|
261
|
-
sections: ["acceptanceCriteria"],
|
|
262
|
-
computed: ["isComplete"],
|
|
263
|
-
related: ["epic"],
|
|
264
|
-
})
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
## Document Access
|
|
268
|
-
|
|
269
|
-
Every model instance has a `.document` property with the underlying Document:
|
|
270
|
-
|
|
271
|
-
```ts
|
|
272
|
-
const doc = epic.document;
|
|
273
|
-
|
|
274
|
-
doc.id // "epics/authentication"
|
|
275
|
-
doc.content // raw markdown body (without frontmatter)
|
|
276
|
-
doc.meta // { priority: "high", status: "created" }
|
|
277
|
-
doc.title // "Authentication"
|
|
278
|
-
doc.rawContent // full markdown including frontmatter
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
### Document outline and text
|
|
282
|
-
|
|
283
|
-
```ts
|
|
284
|
-
doc.toOutline() // indented heading tree
|
|
285
|
-
doc.toText() // all text content concatenated
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
### AST queries on documents
|
|
289
|
-
|
|
290
|
-
```ts
|
|
291
|
-
doc.nodes.headings // all heading nodes
|
|
292
|
-
doc.nodes.links // all link nodes
|
|
293
|
-
doc.nodes.lists // all list nodes
|
|
294
|
-
|
|
295
|
-
doc.astQuery.select("heading") // first heading node
|
|
296
|
-
doc.astQuery.selectAll("listItem") // all list items
|
|
297
|
-
doc.astQuery.findHeadingByText("Stories") // specific heading
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
### Extracting sections from documents
|
|
301
|
-
|
|
302
|
-
```ts
|
|
303
|
-
const sectionNodes = doc.extractSection("Stories"); // AST nodes under "Stories"
|
|
304
|
-
const sectionQuery = doc.querySection("Stories"); // AstQuery scoped to section
|
|
305
|
-
const items = sectionQuery.selectAll("listItem");
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
### Mutating documents (immutable by default)
|
|
309
|
-
|
|
310
|
-
```ts
|
|
311
|
-
// Returns a NEW document
|
|
312
|
-
const updated = doc.replaceSectionContent("Stories", "New content here");
|
|
313
|
-
const removed = doc.removeSection("Mockups");
|
|
314
|
-
const appended = doc.appendToSection("Acceptance Criteria", "- New criterion");
|
|
315
|
-
|
|
316
|
-
// Mutate in place instead
|
|
317
|
-
doc.replaceSectionContent("Stories", "New content", { mutate: true });
|
|
318
|
-
|
|
319
|
-
// Persist changes to disk
|
|
320
|
-
await doc.save();
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
## Collection Utilities
|
|
324
|
-
|
|
325
|
-
```ts
|
|
326
|
-
// List all document IDs
|
|
327
|
-
collection.available // ["epics/authentication", "epics/searching-and-browsing", ...]
|
|
328
|
-
|
|
329
|
-
// Check if loaded
|
|
330
|
-
collection.loaded // boolean
|
|
331
|
-
|
|
332
|
-
// Generate a markdown table of contents
|
|
333
|
-
collection.tableOfContents({ title: "SDLC Docs" })
|
|
334
|
-
|
|
335
|
-
// Get a summary of all models
|
|
336
|
-
await collection.generateModelSummary() // writes README.md and returns markdown
|
|
337
|
-
|
|
338
|
-
// Export everything as JSON
|
|
339
|
-
await collection.export()
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
## Patterns and Recipes
|
|
343
|
-
|
|
344
|
-
### Find all incomplete items across models
|
|
345
|
-
|
|
346
|
-
```ts
|
|
347
|
-
const incompleteEpics = await collection.query(Epic)
|
|
348
|
-
.where("meta.status", "neq", "complete")
|
|
349
|
-
.fetchAll();
|
|
350
|
-
|
|
351
|
-
const incompleteStories = await collection.query(Story)
|
|
352
|
-
.where("meta.status", "neq", "complete")
|
|
353
|
-
.fetchAll();
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
### Find stories for a specific epic
|
|
357
|
-
|
|
358
|
-
```ts
|
|
359
|
-
const epic = collection.getModel("epics/authentication", Epic);
|
|
360
|
-
const stories = epic.relationships.stories.fetchAll();
|
|
361
|
-
|
|
362
|
-
// Or query standalone story files by their epic foreign key
|
|
363
|
-
const standaloneStories = await collection.query(Story)
|
|
364
|
-
.where("meta.epic", "authentication")
|
|
365
|
-
.fetchAll();
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
### Check acceptance criteria coverage
|
|
369
|
-
|
|
370
|
-
```ts
|
|
371
|
-
const stories = await collection.query(Story).fetchAll();
|
|
372
|
-
|
|
373
|
-
for (const story of stories) {
|
|
374
|
-
const criteria = story.sections.acceptanceCriteria;
|
|
375
|
-
console.log(`${story.title}: ${criteria.length} criteria`);
|
|
376
|
-
}
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
### Get all high-priority epics and their stories
|
|
380
|
-
|
|
381
|
-
```ts
|
|
382
|
-
const highPriority = await collection.query(Epic)
|
|
383
|
-
.where("meta.priority", "high")
|
|
384
|
-
.fetchAll();
|
|
385
|
-
|
|
386
|
-
for (const epic of highPriority) {
|
|
387
|
-
const stories = epic.relationships.stories.fetchAll();
|
|
388
|
-
console.log(`${epic.title}: ${stories.length} stories`);
|
|
389
|
-
}
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
### Summarize project status
|
|
393
|
-
|
|
394
|
-
```ts
|
|
395
|
-
const epics = await collection.query(Epic).fetchAll();
|
|
396
|
-
|
|
397
|
-
const summary = epics.map(epic => ({
|
|
398
|
-
title: epic.title,
|
|
399
|
-
status: epic.meta.status,
|
|
400
|
-
priority: epic.meta.priority,
|
|
401
|
-
storyCount: epic.relationships.stories.fetchAll().length,
|
|
402
|
-
isComplete: epic.computed.isComplete,
|
|
403
|
-
}));
|
|
404
|
-
```
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
priority: high
|
|
3
|
-
status: created
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Authentication
|
|
7
|
-
|
|
8
|
-
The Authentication stories cover users logging in and out of the application, as well as the roles and permissions granted to these users and how they are enforced in the application.
|
|
9
|
-
|
|
10
|
-
## Stories
|
|
11
|
-
|
|
12
|
-
### A User should be able to register.
|
|
13
|
-
|
|
14
|
-
As a User I would like to register so that I can use the application.
|
|
15
|
-
|
|
16
|
-
#### Acceptance Criteria
|
|
17
|
-
|
|
18
|
-
- A user can visit the signup form, supply their name, email, and password
|
|
19
|
-
- The signup form should validate the user's information and supply errors
|
|
20
|
-
- The user should receive a confirmation email
|
|
21
|
-
- The user should show up in our database as confirmed after clicking the confirmation link
|
|
22
|
-
|
|
23
|
-
#### Mockups
|
|
24
|
-
|
|
25
|
-
- [Invision: Registration Form](https://invisionapp.com)
|
|
26
|
-
- [Invision: Registration Form Error State](https://invisionapp.com)
|
|
27
|
-
|
|
28
|
-
### A User should be able to login.
|
|
29
|
-
|
|
30
|
-
As a User I would like to login so that I can use the application.
|
|
31
|
-
|
|
32
|
-
#### Acceptance Criteria
|
|
33
|
-
|
|
34
|
-
- A user can visit the signup form, supply their name, email, and password
|
|
35
|
-
- The signup form should validate the user's information and supply errors
|
|
36
|
-
- The user should receive a confirmation email
|
|
37
|
-
- The user should show up in our database as confirmed after clicking the confirmation link
|
|
38
|
-
|
|
39
|
-
#### Mockups
|
|
40
|
-
|
|
41
|
-
- [Invision: Login Form](https://invisionapp.com)
|
|
42
|
-
- [Invision: Login Form Error State ](https://invisionapp.com)
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
status: created
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Searching And Browsing
|
|
6
|
-
|
|
7
|
-
This epic covers the stories related to searching for a specific item.
|
|
8
|
-
|
|
9
|
-
## Stories
|
|
10
|
-
|
|
11
|
-
### Searching for a product by category
|
|
12
|
-
|
|
13
|
-
As a user I want to be able to search for a product by category so that I can find the right product for me.
|
|
14
|
-
|
|
15
|
-
### Searching for a product by manufacturer
|
|
16
|
-
|
|
17
|
-
As a user I want to be able to search for a product by manufacturer so that I can find the right product for me.
|
|
18
|
-
|
|
19
|
-
### Searching for a product by name
|
|
20
|
-
|
|
21
|
-
As a user I want to be able to search for a product by name so that I can find the right product for me.
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
defineModel,
|
|
3
|
-
section,
|
|
4
|
-
hasMany,
|
|
5
|
-
belongsTo,
|
|
6
|
-
z,
|
|
7
|
-
type ModelDefinition,
|
|
8
|
-
type HasManyDefinition,
|
|
9
|
-
type BelongsToDefinition,
|
|
10
|
-
type SectionDefinition,
|
|
11
|
-
} from "../../../src/index";
|
|
12
|
-
import { toString } from "mdast-util-to-string";
|
|
13
|
-
|
|
14
|
-
const epicMeta = z.object({
|
|
15
|
-
priority: z.enum(["low", "medium", "high"]).optional().describe("Importance level for prioritization"),
|
|
16
|
-
status: z
|
|
17
|
-
.enum(["created", "in-progress", "complete"])
|
|
18
|
-
.default("created")
|
|
19
|
-
.describe("Current workflow state"),
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
const storyMeta = z.object({
|
|
23
|
-
status: z
|
|
24
|
-
.enum(["created", "in-progress", "complete"])
|
|
25
|
-
.default("created")
|
|
26
|
-
.describe("Current workflow state"),
|
|
27
|
-
epic: z.string().optional().describe("Slug of the parent epic"),
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
/** Explicit type for Epic so circular Epic↔Story inference doesn’t collapse to never */
|
|
31
|
-
export type EpicDef = ModelDefinition<
|
|
32
|
-
"Epic",
|
|
33
|
-
typeof epicMeta,
|
|
34
|
-
Record<string, never>,
|
|
35
|
-
{ stories: HasManyDefinition<StoryDef> },
|
|
36
|
-
{ isComplete: (self: any) => boolean }
|
|
37
|
-
>;
|
|
38
|
-
|
|
39
|
-
/** Explicit type for Story so circular Epic↔Story inference doesn’t collapse to never */
|
|
40
|
-
export type StoryDef = ModelDefinition<
|
|
41
|
-
"Story",
|
|
42
|
-
typeof storyMeta,
|
|
43
|
-
Record<string, SectionDefinition<any>>,
|
|
44
|
-
{ epic: BelongsToDefinition<EpicDef> },
|
|
45
|
-
{ isComplete: (self: any) => boolean }
|
|
46
|
-
>;
|
|
47
|
-
|
|
48
|
-
export const Epic: EpicDef = defineModel("Epic", {
|
|
49
|
-
prefix: "epics",
|
|
50
|
-
meta: epicMeta,
|
|
51
|
-
relationships: {
|
|
52
|
-
stories: hasMany(() => Story, {
|
|
53
|
-
heading: "Stories",
|
|
54
|
-
}),
|
|
55
|
-
},
|
|
56
|
-
computed: {
|
|
57
|
-
isComplete: (self: any) => self.meta.status === "complete",
|
|
58
|
-
},
|
|
59
|
-
defaults: {
|
|
60
|
-
status: "created",
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
export const Story: StoryDef = defineModel("Story", {
|
|
65
|
-
prefix: "stories",
|
|
66
|
-
meta: storyMeta,
|
|
67
|
-
sections: {
|
|
68
|
-
acceptanceCriteria: section("Acceptance Criteria", {
|
|
69
|
-
extract: (query) =>
|
|
70
|
-
query.selectAll("listItem").map((n) => toString(n)),
|
|
71
|
-
schema: z.array(z.string()).describe("List of acceptance criteria as plain text strings"),
|
|
72
|
-
}),
|
|
73
|
-
mockups: section("Mockups", {
|
|
74
|
-
extract: (query) =>
|
|
75
|
-
Object.fromEntries(
|
|
76
|
-
query
|
|
77
|
-
.selectAll("link")
|
|
78
|
-
.map((l: any) => [toString(l), l.url])
|
|
79
|
-
),
|
|
80
|
-
schema: z.record(z.string(), z.string()).describe("Map of mockup label to URL"),
|
|
81
|
-
}),
|
|
82
|
-
},
|
|
83
|
-
relationships: {
|
|
84
|
-
epic: belongsTo(() => Epic, {
|
|
85
|
-
foreignKey: (doc) => doc.meta.epic as string,
|
|
86
|
-
}),
|
|
87
|
-
},
|
|
88
|
-
computed: {
|
|
89
|
-
isComplete: (self: any) => self.meta.status === "complete",
|
|
90
|
-
},
|
|
91
|
-
});
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
status: created
|
|
3
|
-
epic: authentication
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# A User should be able to login.
|
|
7
|
-
|
|
8
|
-
As a User I would like to login so that I can use the application.
|
|
9
|
-
|
|
10
|
-
## Acceptance Criteria
|
|
11
|
-
|
|
12
|
-
- A user can visit the signup form, supply their name, email, and password
|
|
13
|
-
- The signup form should validate the user's information and supply errors
|
|
14
|
-
- The user should receive a confirmation email
|
|
15
|
-
- The user should show up in our database as confirmed after clicking the confirmation link
|
|
16
|
-
|
|
17
|
-
## Mockups
|
|
18
|
-
|
|
19
|
-
- [Invision: Login Form](https://invisionapp.com)
|
|
20
|
-
- [Invision: Login Form Error State](https://invisionapp.com)
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
status: created
|
|
3
|
-
epic: authentication
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# A User should be able to register.
|
|
7
|
-
|
|
8
|
-
As a User I would like to register so that I can use the application.
|
|
9
|
-
|
|
10
|
-
## Acceptance Criteria
|
|
11
|
-
|
|
12
|
-
- A user can visit the signup form, supply their name, email, and password
|
|
13
|
-
- The signup form should validate the user's information and supply errors
|
|
14
|
-
- The user should receive a confirmation email
|
|
15
|
-
- The user should show up in our database as confirmed after clicking the confirmation link
|
|
16
|
-
|
|
17
|
-
## Mockups
|
|
18
|
-
|
|
19
|
-
- [Invision: Registration Form](https://invisionapp.com)
|
|
20
|
-
- [Invision: Registration Form Error State](https://invisionapp.com)
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
priority: medium
|
|
3
|
-
status: created
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Epic Title
|
|
7
|
-
|
|
8
|
-
A brief description of this epic and its goals.
|
|
9
|
-
|
|
10
|
-
## Stories
|
|
11
|
-
|
|
12
|
-
### Story Title
|
|
13
|
-
|
|
14
|
-
A brief description of this story.
|
|
15
|
-
|
|
16
|
-
#### Acceptance Criteria
|
|
17
|
-
|
|
18
|
-
- First acceptance criterion
|
|
19
|
-
- Second acceptance criterion
|
|
20
|
-
|
|
21
|
-
#### Mockups
|
|
22
|
-
|
|
23
|
-
[Wireframe](https://example.com/wireframe)
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
status: created
|
|
3
|
-
epic: epic-slug
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Story Title
|
|
7
|
-
|
|
8
|
-
A brief description of what this story accomplishes.
|
|
9
|
-
|
|
10
|
-
## Acceptance Criteria
|
|
11
|
-
|
|
12
|
-
- First acceptance criterion
|
|
13
|
-
- Second acceptance criterion
|
|
14
|
-
- Third acceptance criterion
|
|
15
|
-
|
|
16
|
-
## Mockups
|
|
17
|
-
|
|
18
|
-
[Main View](https://example.com/main-view)
|
|
19
|
-
[Detail View](https://example.com/detail-view)
|
package/test/helpers.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import { Collection } from "../src/collection";
|
|
3
|
-
import { Epic, Story } from "./fixtures/sdlc/models";
|
|
4
|
-
|
|
5
|
-
const dir = import.meta.dirname ?? new URL(".", import.meta.url).pathname;
|
|
6
|
-
|
|
7
|
-
export const FIXTURES_PATH = path.resolve(
|
|
8
|
-
dir,
|
|
9
|
-
"fixtures/sdlc"
|
|
10
|
-
);
|
|
11
|
-
|
|
12
|
-
export async function createTestCollection(): Promise<Collection> {
|
|
13
|
-
const collection = new Collection({
|
|
14
|
-
rootPath: FIXTURES_PATH,
|
|
15
|
-
name: "test-sdlc",
|
|
16
|
-
});
|
|
17
|
-
collection.register(Epic);
|
|
18
|
-
collection.register(Story);
|
|
19
|
-
await collection.load();
|
|
20
|
-
return collection;
|
|
21
|
-
}
|