@cat-factory/agents 0.59.2 → 0.61.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.
Files changed (38) hide show
  1. package/dist/agents/kinds/ralph.d.ts +18 -0
  2. package/dist/agents/kinds/ralph.d.ts.map +1 -0
  3. package/dist/agents/kinds/ralph.js +114 -0
  4. package/dist/agents/kinds/ralph.js.map +1 -0
  5. package/dist/agents/kinds/registry.d.ts.map +1 -1
  6. package/dist/agents/kinds/registry.js +2 -0
  7. package/dist/agents/kinds/registry.js.map +1 -1
  8. package/dist/fragmentLibrary/FragmentSourceService.d.ts.map +1 -1
  9. package/dist/fragmentLibrary/FragmentSourceService.js +49 -66
  10. package/dist/fragmentLibrary/FragmentSourceService.js.map +1 -1
  11. package/dist/fragmentLibrary/fragment-source.logic.d.ts.map +1 -1
  12. package/dist/fragmentLibrary/fragment-source.logic.js +4 -69
  13. package/dist/fragmentLibrary/fragment-source.logic.js.map +1 -1
  14. package/dist/index.d.ts +5 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +6 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/repoSourceSync/frontmatter.d.ts +12 -0
  19. package/dist/repoSourceSync/frontmatter.d.ts.map +1 -0
  20. package/dist/repoSourceSync/frontmatter.js +70 -0
  21. package/dist/repoSourceSync/frontmatter.js.map +1 -0
  22. package/dist/repoSourceSync/repo-source-sync.d.ts +88 -0
  23. package/dist/repoSourceSync/repo-source-sync.d.ts.map +1 -0
  24. package/dist/repoSourceSync/repo-source-sync.js +50 -0
  25. package/dist/repoSourceSync/repo-source-sync.js.map +1 -0
  26. package/dist/skillLibrary/SkillCatalogService.d.ts +35 -0
  27. package/dist/skillLibrary/SkillCatalogService.d.ts.map +1 -0
  28. package/dist/skillLibrary/SkillCatalogService.js +52 -0
  29. package/dist/skillLibrary/SkillCatalogService.js.map +1 -0
  30. package/dist/skillLibrary/SkillSourceService.d.ts +67 -0
  31. package/dist/skillLibrary/SkillSourceService.d.ts.map +1 -0
  32. package/dist/skillLibrary/SkillSourceService.js +237 -0
  33. package/dist/skillLibrary/SkillSourceService.js.map +1 -0
  34. package/dist/skillLibrary/skill-source.logic.d.ts +21 -0
  35. package/dist/skillLibrary/skill-source.logic.d.ts.map +1 -0
  36. package/dist/skillLibrary/skill-source.logic.js +43 -0
  37. package/dist/skillLibrary/skill-source.logic.js.map +1 -0
  38. package/package.json +5 -5
@@ -0,0 +1,237 @@
1
+ import { NotFoundError, ValidationError, assertFound } from '@cat-factory/kernel';
2
+ import { normalizeDirPath, probeRepoSourceStatus, syncRepoSource, } from '../repoSourceSync/repo-source-sync.js';
3
+ import { isSkillManifest, parseSkillManifest, slugFromDirName } from './skill-source.logic.js';
4
+ /**
5
+ * Repo-sourced Claude skills (docs/initiatives/repo-skills.md): link a repo
6
+ * directory of skill folders to an account, resync it (read each `<skill>/SKILL.md`
7
+ * directory, upsert changed skills, tombstone removed ones), and answer the cheap
8
+ * "check for changes" without writing. Reads go through the account's existing
9
+ * GitHub installation — no new credential store. The shared repo-source engine
10
+ * (repoSourceSync) owns the sync mechanics; this service supplies the skill
11
+ * differentiator: the sync unit is a DIRECTORY (`<skill>/SKILL.md` + its sibling
12
+ * resources), and a resource-only edit (which advances the dir head commit without
13
+ * touching `SKILL.md`'s blob sha) is caught by re-reading whenever the pinned commit
14
+ * moved.
15
+ */
16
+ export class SkillSourceService {
17
+ deps;
18
+ constructor(deps) {
19
+ this.deps = deps;
20
+ }
21
+ /** Linked sources for an account + their last-synced state. */
22
+ async list(accountId) {
23
+ const rows = await this.deps.skillSourceRepository.listByAccount(accountId);
24
+ return rows.map(toWire);
25
+ }
26
+ /** Link a repo directory as a skill source. Does not sync (call {@link sync}). */
27
+ async link(accountId, input) {
28
+ const now = this.deps.clock.now();
29
+ const record = {
30
+ id: this.deps.idGenerator.next('sklsrc'),
31
+ accountId,
32
+ repoOwner: input.repoOwner.trim(),
33
+ repoName: input.repoName.trim(),
34
+ gitRef: input.gitRef?.trim() || 'HEAD',
35
+ dirPath: normalizeDirPath(input.dirPath),
36
+ lastSyncedCommit: null,
37
+ lastSyncedAt: null,
38
+ createdAt: now,
39
+ deletedAt: null,
40
+ };
41
+ await this.deps.skillSourceRepository.upsert(record);
42
+ return toWire(record);
43
+ }
44
+ /** Unlink a source and tombstone every skill it produced. */
45
+ async unlink(accountId, sourceId) {
46
+ const source = await this.require(accountId, sourceId);
47
+ const now = this.deps.clock.now();
48
+ const skills = await this.deps.accountSkillRepository.listBySource(sourceId);
49
+ if (skills.length > 0) {
50
+ await this.deps.accountSkillRepository.softDeleteBySource(sourceId, now);
51
+ }
52
+ await this.deps.skillSourceRepository.softDelete(source.id, now);
53
+ if (skills.length > 0)
54
+ await this.deps.invalidateCatalog?.(accountId);
55
+ }
56
+ /**
57
+ * Resync a source: read each `<skill>/SKILL.md` directory, upsert every skill whose
58
+ * manifest or resource manifest changed, tombstone skills no longer produced, and
59
+ * stamp the source dir's head commit. Idempotent — re-running with no upstream change
60
+ * touches nothing.
61
+ */
62
+ async sync(accountId, sourceId) {
63
+ const source = await this.require(accountId, sourceId);
64
+ const installationId = await this.requireInstallation(source);
65
+ return syncRepoSource({
66
+ source,
67
+ installationId,
68
+ githubClient: this.deps.githubClient,
69
+ now: this.deps.clock.now(),
70
+ listExisting: () => this.deps.accountSkillRepository.listBySource(sourceId),
71
+ existingId: (s) => s.skillId,
72
+ reconcile: async ({ readRef, commitMoved, now }, existing) => {
73
+ const existingById = new Map(existing.map((s) => [s.skillId, s]));
74
+ // The head commit for the whole source dir is the exact staleness signal: if it
75
+ // has not advanced since the last sync, NOTHING under it changed (manifest OR
76
+ // resource), so every skill is unchanged and we skip all per-directory reads.
77
+ if (!commitMoved) {
78
+ return {
79
+ liveIds: new Set(existing.map((s) => s.skillId)),
80
+ upserted: 0,
81
+ unchanged: existing.length,
82
+ };
83
+ }
84
+ // Sort dirs by name (code-unit order, locale-independent) so slug-collision
85
+ // resolution is deterministic — first wins — regardless of ICU collation.
86
+ const dirs = (await this.listDir(source, installationId, source.dirPath, readRef))
87
+ .filter((e) => e.type === 'dir')
88
+ .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
89
+ const liveIds = new Set();
90
+ const seenSlugs = new Set();
91
+ let upserted = 0;
92
+ let unchanged = 0;
93
+ let incomplete = false;
94
+ for (const dir of dirs) {
95
+ const slug = slugFromDirName(dir.name);
96
+ // Two sibling dirs can slug to the same id (e.g. `release_notes` + `release-notes`,
97
+ // both → `release-notes`). Keep the first (sorted order) deterministically and skip
98
+ // the rest rather than silently overwriting one skill's row with another's.
99
+ if (seenSlugs.has(slug))
100
+ continue;
101
+ seenSlugs.add(slug);
102
+ const skillId = `src:${source.id}:${slug}`;
103
+ const synced = await this.syncSkillDir(source, dir.name, dir.path, skillId, existingById.get(skillId), installationId, readRef, now);
104
+ if (synced === 'skip')
105
+ continue;
106
+ // 'kept'/'stale' (both keep a prior row) and 'upserted' all survive the tombstone
107
+ // sweep — a transient failure must never retire an existing skill.
108
+ liveIds.add(skillId);
109
+ if (synced === 'upserted')
110
+ upserted++;
111
+ else
112
+ unchanged++;
113
+ // A detected-but-unapplied change leaves us not caught up to the head commit.
114
+ if (synced === 'stale')
115
+ incomplete = true;
116
+ }
117
+ return { liveIds, upserted, unchanged, incomplete };
118
+ },
119
+ tombstone: (s, now) => this.deps.accountSkillRepository.softDelete(s.accountId, s.skillId, now),
120
+ updateSyncState: (commit, now) => this.deps.skillSourceRepository.updateSyncState(source.id, commit, now),
121
+ invalidate: () => this.deps.invalidateCatalog?.(accountId) ?? Promise.resolve(),
122
+ });
123
+ }
124
+ /** Lightweight "check for changes": one head-commit read compared to the last sync. */
125
+ async status(accountId, sourceId) {
126
+ const source = await this.require(accountId, sourceId);
127
+ const installationId = await this.requireInstallation(source);
128
+ return probeRepoSourceStatus({ source, installationId, githubClient: this.deps.githubClient });
129
+ }
130
+ // --- internals ----------------------------------------------------------
131
+ async require(accountId, sourceId) {
132
+ const source = assertFound(await this.deps.skillSourceRepository.get(sourceId), 'SkillSource', sourceId);
133
+ // The route gate only authorizes the addressed account, so the record must belong
134
+ // to it; 404 hides other accounts' sources entirely.
135
+ if (source.accountId !== accountId)
136
+ throw new NotFoundError('SkillSource', sourceId);
137
+ if (source.deletedAt !== null)
138
+ throw new NotFoundError('SkillSource', sourceId);
139
+ return source;
140
+ }
141
+ async requireInstallation(source) {
142
+ const installationId = await this.deps.resolveInstallationId(source.accountId);
143
+ if (installationId === null) {
144
+ throw new ValidationError('No GitHub installation is available for this account; connect GitHub before syncing a skill source');
145
+ }
146
+ return installationId;
147
+ }
148
+ listDir(source, installationId, path, readRef) {
149
+ return this.deps.githubClient.listDirectory(installationId, { owner: source.repoOwner, repo: source.repoName }, path, readRef);
150
+ }
151
+ /**
152
+ * Reconcile one `<skill>/` directory:
153
+ * - 'skip' — not a skill (no `SKILL.md`), or unreadable/unparseable with NO prior row.
154
+ * - 'kept' — nothing changed; the prior row is current and untouched.
155
+ * - 'stale' — a change WAS detected but couldn't be applied this round (manifest read
156
+ * back as a 404/non-file, or parsed empty) so the prior row is kept alive rather than
157
+ * retired. Signals the caller to leave the source pin behind and retry next sync.
158
+ * - 'upserted' — the manifest or its resource manifest moved and was written.
159
+ */
160
+ async syncSkillDir(source, dirName, dirPath, skillId, prior, installationId, readRef, now) {
161
+ const entries = await this.listDir(source, installationId, dirPath, readRef);
162
+ const manifest = entries.find((e) => e.type === 'file' && isSkillManifest(e.name));
163
+ if (!manifest)
164
+ return 'skip'; // not a skill directory (SKILL.md removed → prior tombstoned)
165
+ const resources = entries
166
+ .filter((e) => e.type === 'file' && !isSkillManifest(e.name))
167
+ .map((e) => ({ path: e.path, sha: e.sha, size: e.size ?? 0 }))
168
+ .sort((a, b) => a.path.localeCompare(b.path));
169
+ // Fast path: the SKILL.md blob sha is unchanged AND the resource manifest matches,
170
+ // so nothing in this skill changed — keep the prior row untouched.
171
+ if (prior && prior.sourceSha === manifest.sha && sameResources(prior.resources, resources)) {
172
+ return 'kept';
173
+ }
174
+ // Resource-only change: SKILL.md is unchanged but a sibling resource moved. The
175
+ // manifest content (name/description/instructions) is identical, so refresh only the
176
+ // resource manifest + pinned commit without re-fetching the body.
177
+ if (prior && prior.sourceSha === manifest.sha) {
178
+ await this.deps.accountSkillRepository.upsert({
179
+ ...prior,
180
+ resources,
181
+ pinnedCommit: readRef,
182
+ updatedAt: now,
183
+ deletedAt: null,
184
+ });
185
+ return 'upserted';
186
+ }
187
+ const file = await this.deps.githubClient.getFileContent(installationId, { owner: source.repoOwner, repo: source.repoName }, manifest.path, readRef);
188
+ // Unreadable / unparseable this round: keep a prior skill alive rather than retiring
189
+ // it over a transient read or an in-progress edit; with no prior there's nothing to keep.
190
+ // Either way a change was detected (the manifest sha moved) but not applied, so report
191
+ // 'stale' so the source pin is NOT advanced and the next sync re-reads.
192
+ if (!file)
193
+ return prior ? 'stale' : 'skip';
194
+ const parsed = parseSkillManifest(dirName, file.content);
195
+ if (!parsed)
196
+ return prior ? 'stale' : 'skip';
197
+ await this.deps.accountSkillRepository.upsert({
198
+ skillId,
199
+ accountId: source.accountId,
200
+ name: parsed.name,
201
+ description: parsed.description,
202
+ instructions: parsed.instructions,
203
+ resources,
204
+ sourceId: source.id,
205
+ sourcePath: manifest.path,
206
+ sourceSha: file.sha,
207
+ pinnedCommit: readRef,
208
+ createdAt: prior?.createdAt ?? now,
209
+ updatedAt: now,
210
+ deletedAt: null,
211
+ });
212
+ return 'upserted';
213
+ }
214
+ }
215
+ function sameResources(a, b) {
216
+ if (a.length !== b.length)
217
+ return false;
218
+ for (let i = 0; i < a.length; i++) {
219
+ if (a[i].path !== b[i].path || a[i].sha !== b[i].sha)
220
+ return false;
221
+ }
222
+ return true;
223
+ }
224
+ function toWire(record) {
225
+ return {
226
+ id: record.id,
227
+ accountId: record.accountId,
228
+ repoOwner: record.repoOwner,
229
+ repoName: record.repoName,
230
+ gitRef: record.gitRef,
231
+ dirPath: record.dirPath,
232
+ lastSyncedCommit: record.lastSyncedCommit,
233
+ lastSyncedAt: record.lastSyncedAt,
234
+ createdAt: record.createdAt,
235
+ };
236
+ }
237
+ //# sourceMappingURL=SkillSourceService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SkillSourceService.js","sourceRoot":"","sources":["../../src/skillLibrary/SkillSourceService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /** A skill parsed from a `SKILL.md` manifest's frontmatter + body. */
2
+ export interface ParsedSkillManifest {
3
+ name: string;
4
+ description: string;
5
+ /** The procedural instructions (the markdown body). */
6
+ instructions: string;
7
+ }
8
+ /** Slug a skill DIRECTORY name into a stable, id-safe token (`Bug Triage` → `bug-triage`). */
9
+ export declare function slugFromDirName(dirName: string): string;
10
+ /** Whether a directory listing entry is a `SKILL.md` manifest (case-insensitive). */
11
+ export declare function isSkillManifest(name: string): boolean;
12
+ /**
13
+ * Parse a `SKILL.md` file. Frontmatter carries `name` + `description`; the markdown
14
+ * body is the instructions. Tolerant, mirroring the fragment parser: a missing name
15
+ * defaults to a humanised directory name and a missing description to the first body
16
+ * line, so a sparse manifest still imports. Returns null only when there is no usable
17
+ * body at all — an empty `SKILL.md` is not a skill, and returning null keeps the prior
18
+ * synced row alive rather than retiring a skill over an in-progress edit.
19
+ */
20
+ export declare function parseSkillManifest(dirName: string, content: string): ParsedSkillManifest | null;
21
+ //# sourceMappingURL=skill-source.logic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-source.logic.d.ts","sourceRoot":"","sources":["../../src/skillLibrary/skill-source.logic.ts"],"names":[],"mappings":"AAQA,sEAAsE;AACtE,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,uDAAuD;IACvD,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,8FAA8F;AAC9F,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAOvD;AAED,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAQ/F"}
@@ -0,0 +1,43 @@
1
+ import { parseSimpleYaml, splitFrontmatter, str } from '../repoSourceSync/frontmatter.js';
2
+ /** Slug a skill DIRECTORY name into a stable, id-safe token (`Bug Triage` → `bug-triage`). */
3
+ export function slugFromDirName(dirName) {
4
+ return (dirName
5
+ .toLowerCase()
6
+ .replace(/[^a-z0-9]+/g, '-')
7
+ .replace(/^-+|-+$/g, '') || 'skill');
8
+ }
9
+ /** Whether a directory listing entry is a `SKILL.md` manifest (case-insensitive). */
10
+ export function isSkillManifest(name) {
11
+ return /^skill\.md$/i.test(name);
12
+ }
13
+ /**
14
+ * Parse a `SKILL.md` file. Frontmatter carries `name` + `description`; the markdown
15
+ * body is the instructions. Tolerant, mirroring the fragment parser: a missing name
16
+ * defaults to a humanised directory name and a missing description to the first body
17
+ * line, so a sparse manifest still imports. Returns null only when there is no usable
18
+ * body at all — an empty `SKILL.md` is not a skill, and returning null keeps the prior
19
+ * synced row alive rather than retiring a skill over an in-progress edit.
20
+ */
21
+ export function parseSkillManifest(dirName, content) {
22
+ const { frontmatter, body } = splitFrontmatter(content);
23
+ const fm = parseSimpleYaml(frontmatter);
24
+ const instructions = body.trim();
25
+ if (!instructions)
26
+ return null;
27
+ const name = str(fm.name) ?? humanise(dirName);
28
+ const description = str(fm.description) ?? firstLine(instructions) ?? name;
29
+ return { name, description, instructions };
30
+ }
31
+ // --- internals ------------------------------------------------------------
32
+ function firstLine(body) {
33
+ const line = body
34
+ .split(/\r?\n/)
35
+ .map((l) => l.replace(/^[#>\-*\s]+/, '').trim())
36
+ .find((l) => l.length > 0);
37
+ return line ? line.slice(0, 200) : undefined;
38
+ }
39
+ function humanise(dirName) {
40
+ const stem = dirName.replace(/[-_]+/g, ' ').trim();
41
+ return stem ? stem.charAt(0).toUpperCase() + stem.slice(1) : 'Skill';
42
+ }
43
+ //# sourceMappingURL=skill-source.logic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-source.logic.js","sourceRoot":"","sources":["../../src/skillLibrary/skill-source.logic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAA;AAgBzF,8FAA8F;AAC9F,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,OAAO,CACL,OAAO;SACJ,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CACtC,CAAA;AACH,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAClC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe,EAAE,OAAe;IACjE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;IACvD,MAAM,EAAE,GAAG,eAAe,CAAC,WAAW,CAAC,CAAA;IACvC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;IAChC,IAAI,CAAC,YAAY;QAAE,OAAO,IAAI,CAAA;IAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAA;IAC9C,MAAM,WAAW,GAAG,GAAG,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,SAAS,CAAC,YAAY,CAAC,IAAI,IAAI,CAAA;IAC1E,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,YAAY,EAAE,CAAA;AAC5C,CAAC;AAED,6EAA6E;AAE7E,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,IAAI,GAAG,IAAI;SACd,KAAK,CAAC,OAAO,CAAC;SACd,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;SAC/C,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IAC5B,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;AAC9C,CAAC;AAED,SAAS,QAAQ,CAAC,OAAe;IAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;IAClD,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;AACtE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/agents",
3
- "version": "0.59.2",
3
+ "version": "0.61.0",
4
4
  "description": "Agent catalog, routing, prompts and fragment library for the Agent Architecture Board.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -31,14 +31,14 @@
31
31
  "ai": "^6.0.224",
32
32
  "handlebars": "^4.7.9",
33
33
  "valibot": "^1.4.2",
34
- "@cat-factory/contracts": "0.135.0",
35
- "@cat-factory/kernel": "0.129.2",
36
- "@cat-factory/prompt-fragments": "0.13.24"
34
+ "@cat-factory/contracts": "0.137.0",
35
+ "@cat-factory/kernel": "0.131.0",
36
+ "@cat-factory/prompt-fragments": "0.13.26"
37
37
  },
38
38
  "devDependencies": {
39
39
  "typescript": "7.0.2",
40
40
  "vitest": "^4.1.10",
41
- "@cat-factory/caching": "0.8.7"
41
+ "@cat-factory/caching": "0.9.0"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "tsc -b tsconfig.build.json",