@dot-skill/cli 0.4.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bharat Dudeja
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * skill — Open .skill Protocol CLI
4
+ *
5
+ * AI agents create; humans review. Continuity drafts for handoff; release for mint.
6
+ *
7
+ * export SKILL_HOST=cursor
8
+ * skill init --title "…"
9
+ * skill propose --json '[…]'
10
+ * skill journey --summary "…"
11
+ * skill checkpoint # continuity draft (partial OK)
12
+ * skill compile -m "…" --mint # release (complete or refuse)
13
+ * skill load ./file.skill # resume handoff in another AI
14
+ */
15
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,489 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * skill — Open .skill Protocol CLI
4
+ *
5
+ * AI agents create; humans review. Continuity drafts for handoff; release for mint.
6
+ *
7
+ * export SKILL_HOST=cursor
8
+ * skill init --title "…"
9
+ * skill propose --json '[…]'
10
+ * skill journey --summary "…"
11
+ * skill checkpoint # continuity draft (partial OK)
12
+ * skill compile -m "…" --mint # release (complete or refuse)
13
+ * skill load ./file.skill # resume handoff in another AI
14
+ */
15
+ import { readFile, writeFile } from "node:fs/promises";
16
+ import { resolve } from "node:path";
17
+ import { inspectSkill, migrateLegacySkill, toSkillMdAdapter, unpackSkill, validatePackageBytes, mintSkillPackage, verifyMintTrust, compileRecipeToSkill, compileSkillSource, approveCompilation, redactSecrets, CompileRefusalError, } from "@dot-skill/core";
18
+ import { runSkillArchive } from "@dot-skill/runtime";
19
+ import { lookup, list, verify as registryVerify, publish as registryPublish } from "@dot-skill/registry";
20
+ import { isValidAgentHost } from "@dot-skill/protocol";
21
+ import { initWorkspace, requireWorkspace, proposeSection, proposeMany, stage, unstage, status, compileWorkspace, checkpoint, discardSection, loadHead, loadSkillHandoff, setJourney, requireAgentHost, } from "@dot-skill/workspace";
22
+ const VERSION = "0.4.1";
23
+ function usage(exitCode = 1) {
24
+ console.log(`skill — Open .skill Protocol CLI v${VERSION}
25
+
26
+ Skills record declared agent provenance. Humans review, stage, and approve releases.
27
+
28
+ Workspace:
29
+ skill init [--title name]
30
+ skill status Completeness checklist + staged sections
31
+ skill propose --title T --body B Agent adds a section (requires SKILL_HOST)
32
+ skill propose --json '[...]'
33
+ skill journey --summary "…" Redacted human+AI journey (no secrets)
34
+ skill add [id...] Stage (default: ALL)
35
+ skill unstage [id...] | skill review | skill discard <id>
36
+ skill checkpoint [-m msg] Continuity draft for AI handoff (partial OK)
37
+ skill compile -m "msg" [--approve] [--mint] [--profile release|continuity]
38
+ Release compile refuses if incomplete
39
+ skill load <file.skill> Resume context in another AI (no private dumps)
40
+ skill mint [--host name] Seal release package (AI host required)
41
+
42
+ Package tools:
43
+ skill inspect|validate|unpack|run <file.skill>
44
+ skill pack <source.json> [-o out.skill] [--approve] [--profile release]
45
+ skill verify-trust <file.skill> [--profile minted]
46
+ skill registry list|lookup <digest> Optional local log (not a public marketplace)
47
+
48
+ Env (agents):
49
+ SKILL_HOST (required) SKILL_PROVIDER SKILL_MODEL SKILL_DEPLOYMENT
50
+ SKILL_ENDPOINT SKILL_ACTOR SKILL_AGENT_RUNTIME
51
+ SKILL_INPUT_TOKENS SKILL_OUTPUT_TOKENS SKILL_SESSION_ID
52
+
53
+ Install: npm i -g @dot-skill/cli or npx @dot-skill/cli …
54
+ Why not markdown: docs/WHY.md
55
+ `);
56
+ process.exit(exitCode);
57
+ }
58
+ function flag(args, name) {
59
+ return args.includes(name);
60
+ }
61
+ function opt(args, name) {
62
+ const i = args.indexOf(name);
63
+ return i >= 0 ? args[i + 1] : undefined;
64
+ }
65
+ async function main() {
66
+ const argv = process.argv.slice(2);
67
+ const cmd = argv[0];
68
+ const rest = argv.slice(1);
69
+ if (!cmd || cmd === "-h" || cmd === "--help")
70
+ usage(0);
71
+ if (cmd === "-V" || cmd === "--version") {
72
+ console.log(VERSION);
73
+ return;
74
+ }
75
+ switch (cmd) {
76
+ case "init": {
77
+ const title = opt(rest, "--title");
78
+ const { root, created } = await initWorkspace(process.cwd(), { title });
79
+ console.log(JSON.stringify({
80
+ ok: true,
81
+ created,
82
+ root,
83
+ hint: created
84
+ ? "Set SKILL_HOST, then: skill propose … → skill checkpoint | skill compile -m \"…\" --mint"
85
+ : "Already a skill workspace",
86
+ }, null, 2));
87
+ break;
88
+ }
89
+ case "status": {
90
+ const root = requireWorkspace();
91
+ const st = await status(root);
92
+ console.log(JSON.stringify({
93
+ root: st.root,
94
+ title: st.title,
95
+ agent_host_ok: st.agent_host_ok,
96
+ journey_summary: st.journey_summary,
97
+ completeness: st.completeness,
98
+ staged: st.staged.map((i) => ({ id: i.id, type: i.type, title: i.title })),
99
+ unstaged: st.unstaged.map((i) => ({ id: i.id, type: i.type, title: i.title })),
100
+ head: st.head,
101
+ }, null, 2));
102
+ break;
103
+ }
104
+ case "propose": {
105
+ requireAgentHost();
106
+ const root = requireWorkspace();
107
+ const json = opt(rest, "--json");
108
+ if (json) {
109
+ const items = JSON.parse(json);
110
+ const made = await proposeMany(root, items);
111
+ console.log(JSON.stringify({ ok: true, count: made.length, ids: made.map((m) => m.id) }, null, 2));
112
+ break;
113
+ }
114
+ const title = opt(rest, "--title");
115
+ const body = opt(rest, "--body");
116
+ const type = opt(rest, "--type");
117
+ if (!title || !body) {
118
+ console.error("Usage: skill propose --title T --body B [--type decision]");
119
+ console.error(' or: skill propose --json \'[{"title":"…","body":"…"}]\'');
120
+ process.exit(2);
121
+ }
122
+ const section = await proposeSection(root, { title, body, type });
123
+ console.log(JSON.stringify({ ok: true, section }, null, 2));
124
+ break;
125
+ }
126
+ case "journey": {
127
+ const root = requireWorkspace();
128
+ const summary = opt(rest, "--summary");
129
+ if (!summary) {
130
+ console.error("Usage: skill journey --summary \"Redacted human+AI journey…\"");
131
+ process.exit(2);
132
+ }
133
+ const open = opt(rest, "--open");
134
+ const config = await setJourney(root, {
135
+ summary,
136
+ open_questions: open ? open.split("|") : undefined,
137
+ });
138
+ console.log(JSON.stringify({ ok: true, journey_summary: config.journey_summary }, null, 2));
139
+ break;
140
+ }
141
+ case "add": {
142
+ const root = requireWorkspace();
143
+ const ids = rest.filter((a) => !a.startsWith("-"));
144
+ const index = await stage(root, ids.length ? ids : "all");
145
+ console.log(JSON.stringify({ ok: true, staged: index.staged }, null, 2));
146
+ break;
147
+ }
148
+ case "unstage": {
149
+ const root = requireWorkspace();
150
+ const ids = rest.filter((a) => !a.startsWith("-"));
151
+ const index = await unstage(root, ids.length ? ids : "all");
152
+ console.log(JSON.stringify({ ok: true, staged: index.staged }, null, 2));
153
+ break;
154
+ }
155
+ case "review": {
156
+ const root = requireWorkspace();
157
+ const st = await status(root);
158
+ console.log(JSON.stringify({
159
+ staged: st.staged.map((i) => ({
160
+ id: i.id,
161
+ type: i.type,
162
+ title: i.title,
163
+ body: i.body,
164
+ source: i.source,
165
+ })),
166
+ }, null, 2));
167
+ break;
168
+ }
169
+ case "discard": {
170
+ const root = requireWorkspace();
171
+ const id = rest[0];
172
+ if (!id)
173
+ usage();
174
+ await discardSection(root, id);
175
+ console.log(JSON.stringify({ ok: true, discarded: id }, null, 2));
176
+ break;
177
+ }
178
+ case "checkpoint": {
179
+ const root = requireWorkspace();
180
+ try {
181
+ const result = await checkpoint(root, {
182
+ message: opt(rest, "-m") ?? opt(rest, "--message"),
183
+ summary: opt(rest, "--summary"),
184
+ input_tokens: opt(rest, "--input-tokens")
185
+ ? Number(opt(rest, "--input-tokens"))
186
+ : undefined,
187
+ output_tokens: opt(rest, "--output-tokens")
188
+ ? Number(opt(rest, "--output-tokens"))
189
+ : undefined,
190
+ });
191
+ console.log(JSON.stringify({
192
+ ok: true,
193
+ profile: "continuity",
194
+ package_path: result.package_path,
195
+ package_digest: result.package_digest,
196
+ skill_id: result.compile.files.manifest.id,
197
+ completeness: result.compile.completeness,
198
+ hint: "Hand this .skill to another AI via: skill load <path>",
199
+ }, null, 2));
200
+ }
201
+ catch (e) {
202
+ if (e instanceof CompileRefusalError) {
203
+ console.log(JSON.stringify({
204
+ ok: false,
205
+ kind: "compile_refused",
206
+ profile: e.profile,
207
+ missing: e.missing,
208
+ hints: e.hints,
209
+ }, null, 2));
210
+ process.exit(2);
211
+ }
212
+ throw e;
213
+ }
214
+ break;
215
+ }
216
+ case "compile":
217
+ case "bake": {
218
+ if (cmd === "bake") {
219
+ console.error("note: `bake` is a Skillerr product term; open protocol command is `skill compile`");
220
+ }
221
+ const root = requireWorkspace();
222
+ const profile = opt(rest, "--profile") ?? "release";
223
+ try {
224
+ const result = await compileWorkspace(root, {
225
+ message: opt(rest, "-m") ?? opt(rest, "--message"),
226
+ title: opt(rest, "--title"),
227
+ summary: opt(rest, "--summary"),
228
+ add_all: !flag(rest, "--no-all"),
229
+ approve: flag(rest, "--approve"),
230
+ mint: flag(rest, "--mint"),
231
+ profile,
232
+ host: opt(rest, "--host"),
233
+ input_tokens: opt(rest, "--input-tokens")
234
+ ? Number(opt(rest, "--input-tokens"))
235
+ : undefined,
236
+ output_tokens: opt(rest, "--output-tokens")
237
+ ? Number(opt(rest, "--output-tokens"))
238
+ : undefined,
239
+ });
240
+ console.log(JSON.stringify({
241
+ ok: true,
242
+ profile: result.profile,
243
+ package_path: result.package_path,
244
+ package_digest: result.package_digest,
245
+ skill_id: result.compile.files.manifest.id,
246
+ minted: result.minted,
247
+ completeness: result.compile.completeness,
248
+ pending_approvals: result.compile.pending_approvals,
249
+ generation_usage: result.compile.files.provenance?.generation_usage,
250
+ }, null, 2));
251
+ }
252
+ catch (e) {
253
+ if (e instanceof CompileRefusalError) {
254
+ console.log(JSON.stringify({
255
+ ok: false,
256
+ kind: "compile_refused",
257
+ profile: e.profile,
258
+ missing: e.missing,
259
+ hints: e.hints,
260
+ message: "Skill generation stopped. Complete missing parts with the AI agent, then compile again.",
261
+ }, null, 2));
262
+ process.exit(2);
263
+ }
264
+ throw e;
265
+ }
266
+ break;
267
+ }
268
+ case "load": {
269
+ const file = rest[0];
270
+ if (!file)
271
+ usage();
272
+ const handoff = await loadSkillHandoff(resolve(file));
273
+ console.log(JSON.stringify({
274
+ ok: true,
275
+ handoff,
276
+ agent_prompt: "Resume from this .skill continuity package. Honor journey, knowledge, open_questions, and typed inputs. Do not invent missing private data.",
277
+ }, null, 2));
278
+ break;
279
+ }
280
+ case "mint": {
281
+ requireAgentHost(opt(rest, "--host"));
282
+ const root = requireWorkspace();
283
+ const head = await loadHead(root);
284
+ const file = rest.find((a) => a.endsWith(".skill")) ?? head.package_path;
285
+ if (!file)
286
+ throw new Error("No package to mint. Run skill compile first.");
287
+ const bytes = new Uint8Array(await readFile(resolve(file)));
288
+ const unpacked = unpackSkill(bytes);
289
+ if (unpacked.raw.manifest.compile_profile === "continuity") {
290
+ throw new Error("Cannot mint continuity draft. Recompile with --profile release first.");
291
+ }
292
+ const { packageBytes, files, attestation } = mintSkillPackage(unpacked.raw, {
293
+ host: requireAgentHost(opt(rest, "--host")),
294
+ provider: process.env.SKILL_PROVIDER,
295
+ model: process.env.SKILL_MODEL,
296
+ deployment: process.env.SKILL_DEPLOYMENT ?? "unknown",
297
+ endpoint: process.env.SKILL_ENDPOINT
298
+ ? redactSecrets(process.env.SKILL_ENDPOINT)
299
+ : undefined,
300
+ agent_runtime: process.env.SKILL_AGENT_RUNTIME ?? "@dot-skill/cli",
301
+ });
302
+ const out = opt(rest, "-o") ?? file;
303
+ await writeFile(resolve(out), packageBytes);
304
+ console.log(JSON.stringify({
305
+ ok: true,
306
+ out,
307
+ mint_status: files.manifest.mint?.mint_status,
308
+ content_id: files.manifest.mint?.content_id,
309
+ package_digest: files.manifest.package_digest,
310
+ generation_usage: attestation.generation_usage,
311
+ }, null, 2));
312
+ break;
313
+ }
314
+ case "publish": {
315
+ console.error("Publish is not part of the open .skill happy path.\n" +
316
+ "Share the .skill file (git, chat, drive). Optional local log: skill registry publish <file>\n" +
317
+ "Hosted registries are product concerns (e.g. Skillerr), not this protocol.");
318
+ process.exit(2);
319
+ break;
320
+ }
321
+ case "inspect": {
322
+ const file = rest[0];
323
+ if (!file)
324
+ usage();
325
+ console.log(JSON.stringify(inspectSkill(new Uint8Array(await readFile(resolve(file)))), null, 2));
326
+ break;
327
+ }
328
+ case "validate": {
329
+ const file = rest[0];
330
+ if (!file)
331
+ usage();
332
+ const result = validatePackageBytes(new Uint8Array(await readFile(resolve(file))));
333
+ console.log(JSON.stringify(result, null, 2));
334
+ process.exit(result.ok ? 0 : 2);
335
+ break;
336
+ }
337
+ case "unpack": {
338
+ const file = rest[0];
339
+ if (!file)
340
+ usage();
341
+ const u = unpackSkill(new Uint8Array(await readFile(resolve(file))));
342
+ console.log(JSON.stringify({
343
+ manifest: u.manifest,
344
+ workflow: u.workflow,
345
+ knowledge: u.knowledge,
346
+ journey: u.raw.provenance?.journey,
347
+ generation_usage: u.raw.provenance?.generation_usage,
348
+ }, null, 2));
349
+ break;
350
+ }
351
+ case "pack": {
352
+ const file = rest[0];
353
+ if (!file)
354
+ usage();
355
+ requireAgentHost(opt(rest, "--host"));
356
+ const approve = flag(rest, "--approve");
357
+ const profile = opt(rest, "--profile") ?? "release";
358
+ const out = opt(rest, "-o") ?? "out.skill";
359
+ const raw = JSON.parse(await readFile(resolve(file), "utf8"));
360
+ let compiled;
361
+ try {
362
+ if (raw.kind === "skill_source") {
363
+ compiled = compileSkillSource(raw, {
364
+ profile,
365
+ approve_inferred_inputs: approve,
366
+ approve_permissions: approve,
367
+ });
368
+ }
369
+ else {
370
+ const recipe = raw;
371
+ if (!recipe.provenance.hosts.length || !isValidAgentHost(recipe.provenance.hosts[0])) {
372
+ recipe.provenance.hosts = [requireAgentHost(opt(rest, "--host"))];
373
+ }
374
+ compiled = compileRecipeToSkill(recipe, {
375
+ profile,
376
+ approve_inferred_inputs: approve,
377
+ approve_permissions: approve,
378
+ host: requireAgentHost(opt(rest, "--host")),
379
+ });
380
+ }
381
+ if (approve)
382
+ compiled = approveCompilation(compiled, { inputs: ["*"], permissions: true });
383
+ }
384
+ catch (e) {
385
+ if (e instanceof CompileRefusalError) {
386
+ console.log(JSON.stringify({ ok: false, kind: "compile_refused", missing: e.missing, hints: e.hints }, null, 2));
387
+ process.exit(2);
388
+ }
389
+ throw e;
390
+ }
391
+ await writeFile(resolve(out), compiled.packageBytes);
392
+ console.log(JSON.stringify({
393
+ out,
394
+ skill_id: compiled.files.manifest.id,
395
+ package_digest: compiled.files.manifest.package_digest,
396
+ completeness: compiled.completeness,
397
+ }, null, 2));
398
+ break;
399
+ }
400
+ case "run": {
401
+ const file = rest[0];
402
+ if (!file)
403
+ usage();
404
+ const mode = (opt(rest, "--mode") ?? "dry_run");
405
+ const inputs = {};
406
+ for (let i = 0; i < rest.length; i++) {
407
+ if (rest[i] === "--input" && rest[i + 1]) {
408
+ const [k, ...v] = rest[i + 1].split("=");
409
+ inputs[k] = v.join("=");
410
+ }
411
+ }
412
+ const run = await runSkillArchive(new Uint8Array(await readFile(resolve(file))), { host: process.env.SKILL_HOST ?? "runtime" }, { mode, inputs });
413
+ console.log(JSON.stringify(run, null, 2));
414
+ process.exit(run.status === "succeeded" || run.status === "paused" ? 0 : 2);
415
+ break;
416
+ }
417
+ case "verify-trust": {
418
+ const file = rest[0];
419
+ if (!file)
420
+ usage();
421
+ const profile = (opt(rest, "--profile") ?? "minted");
422
+ console.log(JSON.stringify(verifyMintTrust(new Uint8Array(await readFile(resolve(file))), profile), null, 2));
423
+ break;
424
+ }
425
+ case "registry": {
426
+ const sub = rest[0];
427
+ if (sub === "list") {
428
+ console.log(JSON.stringify(await list(undefined, Number(opt(rest, "--limit") ?? 50)), null, 2));
429
+ }
430
+ else if (sub === "lookup") {
431
+ const digest = rest[1];
432
+ if (!digest)
433
+ usage();
434
+ console.log(JSON.stringify(await lookup(digest), null, 2));
435
+ }
436
+ else if (sub === "verify") {
437
+ const file = rest[1];
438
+ if (!file)
439
+ usage();
440
+ console.log(JSON.stringify(await registryVerify(new Uint8Array(await readFile(resolve(file)))), null, 2));
441
+ }
442
+ else if (sub === "publish") {
443
+ const file = rest[1];
444
+ if (!file)
445
+ usage();
446
+ const bytes = new Uint8Array(await readFile(resolve(file)));
447
+ const digest = unpackSkill(bytes).manifest.package_digest;
448
+ console.log(JSON.stringify({
449
+ ...(await registryPublish(digest, { path: file })),
450
+ note: "Local transparency log only — not a public marketplace.",
451
+ }, null, 2));
452
+ }
453
+ else
454
+ usage();
455
+ break;
456
+ }
457
+ case "migrate-legacy": {
458
+ const file = rest[0];
459
+ if (!file)
460
+ usage();
461
+ const out = opt(rest, "-o") ?? "migrated.skill";
462
+ const legacy = JSON.parse(await readFile(resolve(file), "utf8"));
463
+ const { packageBytes, files } = migrateLegacySkill(legacy);
464
+ await writeFile(resolve(out), packageBytes);
465
+ console.log(JSON.stringify({ out, skill_id: files.manifest.id }, null, 2));
466
+ break;
467
+ }
468
+ case "to-skill-md": {
469
+ const file = rest[0];
470
+ if (!file)
471
+ usage();
472
+ const out = opt(rest, "-o") ?? "SKILL.md";
473
+ const md = toSkillMdAdapter(unpackSkill(new Uint8Array(await readFile(resolve(file)))).raw);
474
+ await writeFile(resolve(out), md, "utf8");
475
+ console.log(JSON.stringify({ out, warning: "Lossy adapter — markdown is never the source of truth." }, null, 2));
476
+ break;
477
+ }
478
+ case "help":
479
+ usage();
480
+ break;
481
+ default:
482
+ console.error(`Unknown command: ${cmd}\n`);
483
+ usage();
484
+ }
485
+ }
486
+ main().catch((e) => {
487
+ console.error(e instanceof Error ? e.message : e);
488
+ process.exit(1);
489
+ });
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@dot-skill/cli",
3
+ "version": "0.4.1",
4
+ "description": "skill — Open .skill Protocol CLI (continuity and release workflows)",
5
+ "license": "MIT",
6
+ "author": {
7
+ "name": "Bharat Dudeja",
8
+ "url": "https://github.com/bharatdudeja13-cmd"
9
+ },
10
+ "type": "module",
11
+ "bin": {
12
+ "skill": "./dist/cli.js",
13
+ "dot-skill": "./dist/cli.js"
14
+ },
15
+ "main": "./dist/cli.js",
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/dot-skill/dot-skill.git",
22
+ "directory": "packages/cli"
23
+ },
24
+ "files": [
25
+ "dist/cli.js",
26
+ "dist/cli.d.ts",
27
+ "LICENSE"
28
+ ],
29
+ "scripts": {
30
+ "build": "rm -rf dist && tsc -p tsconfig.json",
31
+ "prepack": "npm run build",
32
+ "test": "node --test dist/conformance.test.js"
33
+ },
34
+ "dependencies": {
35
+ "@dot-skill/core": "^0.4.1",
36
+ "@dot-skill/protocol": "^0.4.1",
37
+ "@dot-skill/registry": "^0.4.1",
38
+ "@dot-skill/runtime": "^0.4.1",
39
+ "@dot-skill/workspace": "^0.4.1"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^22.15.21",
43
+ "typescript": "^5.8.3"
44
+ },
45
+ "engines": {
46
+ "node": ">=20"
47
+ }
48
+ }