@kensio/skills 1.18.0 → 1.18.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.
- package/package.json +1 -1
- package/skills/dynamodb-single-table/SKILL.md +1 -1
- package/skills/github-issue-drafting/SKILL.md +1 -1
- package/skills/isolated-testing-style/SKILL.md +1 -1
- package/skills/pangram-check/SKILL.md +1 -1
- package/skills/part-factory-test-data/SKILL.md +1 -1
- package/skills/skill-template/SKILL.md +1 -1
- package/skills/technical-prose-style/SKILL.md +1 -1
- package/skills/yulin-aws-simulation/SKILL.md +113 -34
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@ name: dynamodb-single-table
|
|
|
3
3
|
description: Model data in Amazon DynamoDB as one table by default, reading the current AWS guidance before committing to a schema, writing the access patterns down before any keys exist, holding every entity type in one table, overloading generic partition and sort keys across those types, overloading and sparsifying secondary indexes, and splitting items by write rate. Use when designing or reviewing a DynamoDB schema, when a CDK stack is about to gain a second table, when an entity needs a query it has no key for, when application code fetches from two tables to assemble one response, when a Scan or a filter expression appears, when a partition runs hot or a write throttles, and when asked how to model users, orders, events or tenants in DynamoDB.
|
|
4
4
|
license: Apache-2.0
|
|
5
5
|
metadata:
|
|
6
|
-
version: "1.18.
|
|
6
|
+
version: "1.18.1"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Single-table design in DynamoDB
|
|
@@ -3,7 +3,7 @@ name: github-issue-drafting
|
|
|
3
3
|
description: Draft a GitHub issue from a short note or a rough idea, grounding every claim about the code in the repository the issue will be filed against, splitting work that is really two issues, and filing it with `gh` once the user has approved the draft. Use when turning a to-do item, a Slack message, a code TODO, a failing test or a bug report into an issue, when asked to "write up an issue for" something, to "raise", "file" or "open an issue", when asked whether something should be one issue or several, and when tidying the titles, types or labels of issues that already exist.
|
|
4
4
|
license: Apache-2.0
|
|
5
5
|
metadata:
|
|
6
|
-
version: "1.18.
|
|
6
|
+
version: "1.18.1"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# GitHub issue drafting
|
|
@@ -3,7 +3,7 @@ name: isolated-testing-style
|
|
|
3
3
|
description: Write tests that start from given/when/then, use real collaborators through simulation rather than stubs and mocks, take their isolation from randomised data rather than shared setup and teardown, and assert behaviour rather than call counts. Use when writing or reviewing tests, when a test needs a collaborator faked, when reaching for a mock, spy, `toHaveBeenCalledWith`, `beforeEach`/`afterEach` fixtures or a hardcoded expected hash, when test setup has grown tangled, and when asked "how should I test this?".
|
|
4
4
|
license: Apache-2.0
|
|
5
5
|
metadata:
|
|
6
|
-
version: "1.18.
|
|
6
|
+
version: "1.18.1"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Isolated testing style
|
|
@@ -4,7 +4,7 @@ description: Send the prose of a finished document to Pangram, a commercial AI-t
|
|
|
4
4
|
license: Apache-2.0
|
|
5
5
|
compatibility: Needs a Pangram API key and network access. Every run is billed.
|
|
6
6
|
metadata:
|
|
7
|
-
version: "1.18.
|
|
7
|
+
version: "1.18.1"
|
|
8
8
|
disable-model-invocation: true
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -3,7 +3,7 @@ name: part-factory-test-data
|
|
|
3
3
|
description: Build test data with @kensio/part-factory, keeping in the factory everything a test does not care about, passing dependencies at call time so factories stay independent, and choosing between StaticFactory, DynamicFactory, VariantFactory, MappedFactory and AsyncMappedFactory. Use when writing test fixtures or builders, when a test file is full of object literals, when a shared event, message or payload shape is being hand-written, when a required field is about to be made optional to ease test setup, and when tempted to wrap a factory in a helper function that applies overrides.
|
|
4
4
|
license: Apache-2.0
|
|
5
5
|
metadata:
|
|
6
|
-
version: "1.18.
|
|
6
|
+
version: "1.18.1"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Building test data with Part Factory
|
|
@@ -3,7 +3,7 @@ name: skill-template
|
|
|
3
3
|
description: Scaffold a new agent skill, writing the SKILL.md and its frontmatter to the Agent Skills specification, then wrapping it as a plugin in this repo with package.json, plugin.json and a marketplace entry. Use when the user asks to "add a new skill", "create a skill", "write a SKILL.md" or "start a new plugin", and when checking whether an existing skill is portable between agents.
|
|
4
4
|
license: Apache-2.0
|
|
5
5
|
metadata:
|
|
6
|
-
version: "1.18.
|
|
6
|
+
version: "1.18.1"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Skill template
|
|
@@ -3,7 +3,7 @@ name: technical-prose-style
|
|
|
3
3
|
description: Write documentation, READMEs, code comments, commit messages, release notes and PR text as plain technical prose, by removing the constructions that make unedited LLM prose tiring to read. It targets significance tails, contrastive definition, negation framing, appositive tails, colon explainers and a vocabulary that keeps renaming the same thing. Ships a script that scores prose against Django, Go, Rust and Python documentation. Use when writing or editing docs, a README, a changelog, a blog post or any prose for human readers, when asked to improve, tighten or rewrite writing, when prose "sounds like AI" or "sounds like Claude", and when reviewing documentation in a pull request.
|
|
4
4
|
license: Apache-2.0
|
|
5
5
|
metadata:
|
|
6
|
-
version: "1.18.
|
|
6
|
+
version: "1.18.1"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Technical prose style
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: yulin-aws-simulation
|
|
3
|
-
description: Use the @kensio/yulin in-process AWS simulator well when testing AWS code
|
|
3
|
+
description: Use the @kensio/yulin in-process AWS simulator well when testing AWS code. Share one simulation and SDK interception layer across tests that leave simulated time alone, and isolate the minority that change the clock. Use Yulin directly, deploy CDK-synthesized templates, bind real handlers, intercept SDK clients, drive HTTP with SimAwsHttp, control time, read generated names, match service errors by name, and handle unsupported properties. Use when writing or reviewing tests that touch AWS, replacing SDK stubs, testing CDK stacks or CloudFront resources, simplifying Yulin test setup, or diagnosing unsupported templates and commands.
|
|
4
4
|
license: Apache-2.0
|
|
5
5
|
metadata:
|
|
6
|
-
version: "1.18.
|
|
6
|
+
version: "1.18.1"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Testing with Yulin
|
|
@@ -13,6 +13,13 @@ network and no AWS account. This skill is how to use it well. It serves `isolate
|
|
|
13
13
|
the general argument for simulation over stubs. Each rule says what it buys, and a case that does
|
|
14
14
|
not want that trade can go the other way knowingly.
|
|
15
15
|
|
|
16
|
+
Treat Yulin as suite infrastructure, in the same way tests treat an AWS account or a LocalStack
|
|
17
|
+
container. Create one simulation, deploy the application once and install SDK interception once in
|
|
18
|
+
the Vitest suite setup. Every test talks to that shared environment. Randomised resource names and
|
|
19
|
+
identifiers keep tests independent while the simulated AWS state remains alive for the whole suite.
|
|
20
|
+
This is the default for tests that leave the simulation clock alone. Put the smaller set of tests
|
|
21
|
+
that change simulated time in an isolated group, with their own Yulin setup.
|
|
22
|
+
|
|
16
23
|
For the API read `node_modules/@kensio/yulin/llms.txt`. It indexes the 45 markdown pages beside it
|
|
17
24
|
under `node_modules/@kensio/yulin/docs/`, one per simulated service and per feature guide,
|
|
18
25
|
documenting the version installed. Open the page it names for the service in hand and grep it for
|
|
@@ -29,13 +36,13 @@ service, or a `beforeEach` that reassembles the world.
|
|
|
29
36
|
|
|
30
37
|
Yulin is built to be used directly. `new SimAws()` and `new SimSdk()` are plain constructors with no
|
|
31
38
|
side effects, no network, no cleanup and no awaiting. Service accessors take the same Command
|
|
32
|
-
objects the SDK does.
|
|
33
|
-
environment, and
|
|
39
|
+
objects the SDK does. The suite setup creates these objects once. `deployTemplateFile` is the
|
|
40
|
+
environment, and the tests import the shared simulation objects directly.
|
|
34
41
|
|
|
35
|
-
|
|
36
|
-
simulation back. A wrapper around any of those steps hides what the reader needs to see.
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
The setup constructs, deploys and intercepts. A test exercises the application and reads the shared
|
|
43
|
+
simulation back. A wrapper around any of those steps hides what the reader needs to see. The setup
|
|
44
|
+
can be a small module returning the simulation objects themselves. Once it wants a class, an options
|
|
45
|
+
interface or a directory, it has become a second product.
|
|
39
46
|
|
|
40
47
|
## One CDK app behind the tests, local dev and production
|
|
41
48
|
|
|
@@ -52,6 +59,9 @@ const stack = await simAws.region("eu-west-2").cloudFormation().deployTemplateFi
|
|
|
52
59
|
await stack.waitForDeployComplete();
|
|
53
60
|
```
|
|
54
61
|
|
|
62
|
+
The test suite deploys this template once during its shared Yulin setup. Individual tests use the
|
|
63
|
+
deployed resources and do not deploy their own copies.
|
|
64
|
+
|
|
55
65
|
A hand-written template only tests itself, and a dev environment building its own buckets is a third
|
|
56
66
|
description whose drift shows up as a bug reproducing in one place of the three. Infrastructure
|
|
57
67
|
belongs in the CDK app even when only a test needs it.
|
|
@@ -155,7 +165,7 @@ their order in `stackNames` is what puts the value there in time.
|
|
|
155
165
|
from the simulation, and the code under test uses the SDK exactly as it does in production.
|
|
156
166
|
|
|
157
167
|
```typescript
|
|
158
|
-
|
|
168
|
+
const simSdk = new SimSdk({ simAws });
|
|
159
169
|
simSdk.intercept(SecretsManagerClient); // Every instance, including ones made later.
|
|
160
170
|
```
|
|
161
171
|
|
|
@@ -164,20 +174,22 @@ correctly. A stub has no naming rules and verifies no signatures. A malformed Se
|
|
|
164
174
|
or a wrongly computed Cognito `SECRET_HASH` passes it and fails in production.
|
|
165
175
|
|
|
166
176
|
Intercept the class in most cases, since the code under test usually constructs its own clients.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
177
|
+
Install that class interception once in the suite setup. A class interception is process-wide (it
|
|
178
|
+
shadows `send` on the class prototype) and refuses a second install while the first is live, with
|
|
179
|
+
`SimSdkAlreadyInterceptedError`. Repeated interception in `beforeEach` or in each test file is both
|
|
180
|
+
unnecessary and an error when those files share a worker. Intercept an instance only when one
|
|
181
|
+
specific client should reach Yulin.
|
|
171
182
|
|
|
172
183
|
Whichever it is, it has to be the client the code actually calls. A `DynamoDBDocumentClient` built
|
|
173
184
|
over a `DynamoDBClient` is what the code sends through, and the document client is the one to
|
|
174
185
|
intercept. Every Command routes to the simulation by default, and an allow list of Command classes
|
|
175
186
|
narrows that where something else should handle the rest.
|
|
176
187
|
|
|
177
|
-
`SimSdk`
|
|
178
|
-
|
|
179
|
-
`simSdk
|
|
180
|
-
|
|
188
|
+
Keep the suite's `SimSdk` alive for as long as the suite. Worker exit normally removes its
|
|
189
|
+
process-wide patches. Call `simSdk.restoreAll()` if the process will continue doing other work.
|
|
190
|
+
`using simSdk = new SimSdk()` and `interception.restore()` remain useful for a deliberately
|
|
191
|
+
short-lived simulation. Each `SimSdk` owns a `SimAws`, reachable as `simSdk.simAws`, and
|
|
192
|
+
`new SimSdk({ simAws })` shares an existing one.
|
|
181
193
|
|
|
182
194
|
### A fake accepts any request the simulator would refuse
|
|
183
195
|
|
|
@@ -213,6 +225,19 @@ it as well as the code's own arithmetic.
|
|
|
213
225
|
`simAws.clock().resume()` tracks the underlying clock and `simAws.clock().isFrozen` reports the
|
|
214
226
|
mode. Running mode suits a local dev server, and a test usually wants an advance.
|
|
215
227
|
|
|
228
|
+
### Give clock-changing tests their own simulation
|
|
229
|
+
|
|
230
|
+
The clock is part of a `SimAws` instance's state. Tests sharing that instance also share its current
|
|
231
|
+
time and whether it is frozen or running. One test advancing or resuming the clock therefore changes
|
|
232
|
+
the environment underneath every other test in that group. Randomised resource names cannot isolate
|
|
233
|
+
this change.
|
|
234
|
+
|
|
235
|
+
Most tests should use the suite simulation without changing its clock. Put tests that advance the
|
|
236
|
+
clock or change its mode in a separate Vitest project or file group. Give each of those tests a
|
|
237
|
+
fresh `SimAws`, deployment and interception layer. Each test can then control time without affecting
|
|
238
|
+
another test. A small group may share one isolated simulation when its cases deliberately follow the
|
|
239
|
+
same timeline.
|
|
240
|
+
|
|
216
241
|
## Assert by reading the simulation back
|
|
217
242
|
|
|
218
243
|
The simulation holds real state. After exercising the code, ask the service what happened:
|
|
@@ -224,7 +249,8 @@ const object = await simAws.s3().getObject(new GetObjectCommand({ Bucket: bucket
|
|
|
224
249
|
|
|
225
250
|
A call-count assertion holds only for today's implementation. A state assertion holds however the
|
|
226
251
|
handler is rewritten, and it fails if the call was made in a way the real service would have
|
|
227
|
-
rejected.
|
|
252
|
+
rejected. In the shared suite environment, read the resource named by the test's random identifier.
|
|
253
|
+
Do not list the whole service and assume no other test has left state there.
|
|
228
254
|
|
|
229
255
|
The accessors sit on more than one scope. `simAws.region(name)` carries some of the services and
|
|
230
256
|
`simAws.region(name).account()` carries all of them (`logs()` among the account-only ones), so look
|
|
@@ -303,33 +329,86 @@ costing nothing but convenience as well, once it is forcing structural duplicati
|
|
|
303
329
|
A workaround kept while the issue is open wants a comment naming that issue and a revisit when it
|
|
304
330
|
closes. Re-read those claims on each upgrade. They are the ones nothing tests.
|
|
305
331
|
|
|
306
|
-
##
|
|
332
|
+
## Share one simulation across the whole test suite
|
|
333
|
+
|
|
334
|
+
The recommended lifecycle is one `SimAws`, one deployment and one `SimSdk` interception layer for
|
|
335
|
+
the tests that do not change simulated time. This should be most of the test suite. Do this in
|
|
336
|
+
Vitest setup. Tests should treat the result as a long-running AWS account or LocalStack container
|
|
337
|
+
whose state survives every test and test file.
|
|
338
|
+
|
|
339
|
+
An in-process simulation needs the test files to share a worker and module cache. Set
|
|
340
|
+
`fileParallelism: false` and `isolate: false`, then load one setup module through `setupFiles`:
|
|
307
341
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
342
|
+
```typescript
|
|
343
|
+
// vitest.config.ts
|
|
344
|
+
export default defineConfig({
|
|
345
|
+
test: {
|
|
346
|
+
fileParallelism: false,
|
|
347
|
+
isolate: false,
|
|
348
|
+
setupFiles: ["./test/yulin.setup.ts"],
|
|
349
|
+
},
|
|
350
|
+
});
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Keep the one-time work in an imported module. Vitest executes a setup file before each test file,
|
|
354
|
+
even with isolation disabled, but imported modules stay cached in the shared worker.
|
|
311
355
|
|
|
312
356
|
```typescript
|
|
313
|
-
|
|
357
|
+
// test/yulin-suite.ts
|
|
358
|
+
const ready = (async () => {
|
|
359
|
+
const simAws = new SimAws();
|
|
360
|
+
const simSdk = new SimSdk({ simAws });
|
|
361
|
+
|
|
362
|
+
simSdk.intercept(DynamoDBClient);
|
|
363
|
+
simSdk.intercept(S3Client);
|
|
314
364
|
|
|
315
|
-
beforeAll(async () => {
|
|
316
|
-
simAws = new SimAws();
|
|
317
365
|
const stack = await simAws.cloudFormation().deployTemplateFile({
|
|
318
366
|
templatePath: "cdk.out/SiteStack.template.json",
|
|
319
367
|
});
|
|
320
368
|
await stack.waitForDeployComplete();
|
|
321
|
-
});
|
|
322
369
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
370
|
+
return { simAws, simSdk, stack };
|
|
371
|
+
})();
|
|
372
|
+
|
|
373
|
+
export function yulinSuite() {
|
|
374
|
+
return ready;
|
|
375
|
+
}
|
|
327
376
|
```
|
|
328
377
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
378
|
+
```typescript
|
|
379
|
+
// test/yulin.setup.ts
|
|
380
|
+
import { yulinSuite } from "./yulin-suite";
|
|
381
|
+
|
|
382
|
+
await yulinSuite();
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Tests import `yulinSuite()` and work against that same state. Give every test data a random name or
|
|
386
|
+
identifier, query by that identifier and never assume a service starts empty. A missing-resource
|
|
387
|
+
case uses a new identifier that no test created. A count assertion records the relevant count before
|
|
388
|
+
the action when it cannot query by identifier. Tests remain independent without clearing tables,
|
|
389
|
+
buckets or queues between them.
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
const { simAws } = await yulinSuite();
|
|
393
|
+
const key = `uploads/${faker.string.uuid()}.png`;
|
|
394
|
+
|
|
395
|
+
// Exercise the application, then read this test's object from shared S3 state.
|
|
396
|
+
const object = await simAws.s3().getObject({ input: { Bucket: bucket, Key: key } });
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Do not put the simulation in Vitest `globalSetup`. That hook runs outside the test workers and can
|
|
400
|
+
only pass serializable values into them. `setupFiles` runs in the test worker and can share the live
|
|
401
|
+
`SimAws` object through the cached module above.
|
|
402
|
+
|
|
403
|
+
This shared setup also shares the simulation clock. Keep tests that call `advanceBy`, resume the
|
|
404
|
+
clock or otherwise change simulated time out of this group. Put them in a separate Vitest project or
|
|
405
|
+
file pattern, such as `*.clock.iso.test.ts`, and give each test an isolated Yulin setup. The split
|
|
406
|
+
keeps the common suite setup cheap while allowing the smaller clock-testing group to control time.
|
|
407
|
+
|
|
408
|
+
Creating Yulin once per test or once per file is supported. Reserve it for a case that deliberately
|
|
409
|
+
tests an entire fresh account, an independent clock or incompatible interception. Use the shared
|
|
410
|
+
setup by default. A `beforeEach` or file-level `beforeAll` that deploys the application again
|
|
411
|
+
usually turns random test data into repeated infrastructure work.
|
|
333
412
|
|
|
334
413
|
## Bind a handler and run it as a real simulated Lambda
|
|
335
414
|
|