pf2e-sage-stats 0.2.4 → 0.2.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/dist/index.js CHANGED
@@ -24,6 +24,7 @@ const commander_1 = require("commander");
24
24
  const promises_1 = __importDefault(require("fs/promises"));
25
25
  const path_1 = __importDefault(require("path"));
26
26
  const readline_1 = __importDefault(require("readline"));
27
+ const clipboardy_1 = __importDefault(require("clipboardy"));
27
28
  const package_json_1 = __importDefault(require("../package.json"));
28
29
  const app_1 = require("./app");
29
30
  const program = new commander_1.Command(package_json_1.default.name);
@@ -71,6 +72,19 @@ program.command('sort')
71
72
  .action(() => __awaiter(void 0, void 0, void 0, function* () {
72
73
  console.log(yield (0, app_1.sortFolder)());
73
74
  }));
75
+ program.command('convert')
76
+ .description('Convert a discord export file into a txt log')
77
+ .argument('<file>', 'input file')
78
+ .option('-o, --output <file>', 'output file')
79
+ .action((argument, option) => __awaiter(void 0, void 0, void 0, function* () {
80
+ const file = yield promises_1.default.readFile(argument, { encoding: 'utf-8' });
81
+ if (option.output) {
82
+ yield promises_1.default.writeFile(option.output, (0, app_1.screenPlay)(file), { encoding: 'utf-8' });
83
+ }
84
+ else {
85
+ console.log((0, app_1.screenPlay)(file));
86
+ }
87
+ }));
74
88
  program.command('flat')
75
89
  .description('Generte a special flat npc tsv')
76
90
  .option('-n, --name <string>', 'npc name')
@@ -78,23 +92,23 @@ program.command('flat')
78
92
  .action((option) => __awaiter(void 0, void 0, void 0, function* () {
79
93
  var _a, _b;
80
94
  if (option.output) {
81
- yield promises_1.default.writeFile(option.output, (0, app_1.fromatMap)((_a = option.name) !== null && _a !== void 0 ? _a : 'flat', app_1.prediceateMap), { encoding: 'utf-8' });
95
+ yield promises_1.default.writeFile(option.output, (0, app_1.fromatMap)((_a = option.name) !== null && _a !== void 0 ? _a : 'flat', app_1.flatMap), { encoding: 'utf-8' });
82
96
  }
83
97
  else {
84
- console.log((0, app_1.fromatMap)((_b = option.name) !== null && _b !== void 0 ? _b : 'flat', app_1.prediceateMap));
98
+ console.log((0, app_1.fromatMap)((_b = option.name) !== null && _b !== void 0 ? _b : 'flat', app_1.flatMap));
85
99
  }
86
100
  }));
87
- program.command('adjustment')
88
- .description('Generte a special adjustment npc tsv')
101
+ program.command('dice')
102
+ .description('Generte a special dice npc tsv')
89
103
  .option('-n, --name <string>', 'npc name')
90
104
  .option('-o, --output <file>', 'output json')
91
105
  .action((option) => __awaiter(void 0, void 0, void 0, function* () {
92
106
  var _a, _b;
93
107
  if (option.output) {
94
- yield promises_1.default.writeFile(option.output, (0, app_1.fromatMap)((_a = option.name) !== null && _a !== void 0 ? _a : 'adjustment', (0, app_1.adjustmentMap)()), { encoding: 'utf-8' });
108
+ yield promises_1.default.writeFile(option.output, (0, app_1.fromatMap)((_a = option.name) !== null && _a !== void 0 ? _a : 'dice', app_1.diceMap), { encoding: 'utf-8' });
95
109
  }
96
110
  else {
97
- console.log((0, app_1.fromatMap)((_b = option.name) !== null && _b !== void 0 ? _b : 'adjustment', (0, app_1.adjustmentMap)()));
111
+ console.log((0, app_1.fromatMap)((_b = option.name) !== null && _b !== void 0 ? _b : 'dice', app_1.diceMap));
98
112
  }
99
113
  }));
100
114
  program.command('statblock')
@@ -131,26 +145,24 @@ program.command('tsv')
131
145
  .argument('<file>', 'input json')
132
146
  .option('-o, --output <file>', 'output tsv')
133
147
  .option('-s, --secretDC', 'produce secret DCs')
134
- .option('-r, --recallDC', 'produce recall DCs')
135
148
  .option('-d, --defaultSkills', 'produce values for untrained skills')
136
149
  .action((argument, option) => __awaiter(void 0, void 0, void 0, function* () {
137
150
  const file = yield promises_1.default.readFile(argument, { encoding: 'utf-8' });
138
151
  const stats = (0, app_1.parseJSON)(file);
139
152
  const output = option.output ? option.output : path_1.default.join(path_1.default.parse(argument).dir, path_1.default.parse(argument).name + '.tsv');
140
- yield promises_1.default.writeFile(output, (0, app_1.formatTSV)(stats, option.secretDC, option.defaultSkills, option.recallDC), { encoding: 'utf-8' });
153
+ yield promises_1.default.writeFile(output, (0, app_1.formatTSV)(stats, option.secretDC, option.defaultSkills), { encoding: 'utf-8' });
141
154
  }));
142
155
  program.command('command')
143
156
  .description('Generte a creation command file from JSON')
144
157
  .argument('<file>', 'input json')
145
158
  .option('-o, --output <file>', 'output txt')
146
159
  .option('-s, --secretDC', 'produce secret DCs')
147
- .option('-r, --recallDC', 'produce recall DCs')
148
160
  .option('-d, --defaultSkills', 'produce values for untrained skills')
149
161
  .action((argument, option) => __awaiter(void 0, void 0, void 0, function* () {
150
162
  const file = yield promises_1.default.readFile(argument, { encoding: 'utf-8' });
151
163
  const stats = (0, app_1.parseJSON)(file);
152
164
  const output = option.output ? option.output : path_1.default.join(path_1.default.parse(argument).dir, path_1.default.parse(argument).name + '-command.txt');
153
- yield promises_1.default.writeFile(output, (0, app_1.formatCommand)(stats, option.secretDC, option.defaultSkills, option.recallDC), { encoding: 'utf-8' });
165
+ yield promises_1.default.writeFile(output, (0, app_1.formatCommand)(stats, option.secretDC, option.defaultSkills), { encoding: 'utf-8' });
154
166
  }));
155
167
  program.command('newtracker')
156
168
  .argument('<file>', 'input text file')
@@ -191,7 +203,9 @@ program.command('track')
191
203
  }
192
204
  else {
193
205
  console.clear();
194
- console.log((0, app_1.formatTracker)(tracker));
206
+ const track = (0, app_1.formatTracker)(tracker);
207
+ clipboardy_1.default.writeSync(track);
208
+ console.log(track);
195
209
  }
196
210
  });
197
211
  yield invoke(argument);
@@ -216,56 +230,304 @@ program.command('track')
216
230
  }
217
231
  }))(),
218
232
  (() => __awaiter(void 0, void 0, void 0, function* () {
233
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
219
234
  const rl = readline_1.default.createInterface({
220
235
  input: process.stdin,
221
236
  output: process.stdout
222
237
  });
223
238
  while (true) {
224
239
  yield new Promise((resolve) => setTimeout(resolve, 500));
225
- const input = yield new Promise((resolve) => (rl.question('> ', resolve)));
226
- const hpmod = input.match(/([^\d\s+-]+)\s*([+-]?\d+)/);
227
- if (hpmod) {
228
- const file = yield promises_1.default.readFile(argument, { encoding: 'utf-8' });
229
- const tracker = (0, app_1.parseTracker)(file);
230
- const index = tracker.findIndex((c) => c.alias === hpmod[1]);
231
- if (index >= 0) {
232
- let hp = tracker[index].hp;
233
- let thp = tracker[index].temphp;
234
- if (hpmod[2].match(/^[+-]/)) {
235
- const delta = parseInt(hpmod[2]);
236
- if (delta < 0) {
237
- thp += delta;
238
- hp += Math.min(thp, 0);
240
+ let input = yield new Promise((resolve) => (rl.question('> ', resolve)));
241
+ const commands = [{
242
+ regex: /^\s*[bB]\s*(\d+)/,
243
+ map: (match) => ({ command: 'block', index: parseInt(match[1], 10) })
244
+ }, {
245
+ regex: /^\s*reset/i,
246
+ map: () => ({ command: 'reset' })
247
+ }, {
248
+ regex: /^\s*heal/i,
249
+ map: () => ({ command: 'heal' })
250
+ }, {
251
+ regex: /^\s*exit/i,
252
+ map: () => ({ command: 'exit' })
253
+ }, {
254
+ regex: /^\s*([^\d\s+-]+)\s*([+-]?\d+)/,
255
+ map: (match) => {
256
+ if (match[2].match(/^[+-]/)) {
257
+ return { command: 'mod-hp', tag: match[1], value: parseInt(match[2]) };
239
258
  }
240
259
  else {
241
- hp += delta;
260
+ return { command: 'set-hp', tag: match[1], value: parseInt(match[2]) };
242
261
  }
243
262
  }
244
- else {
245
- hp = parseInt(hpmod[2]);
263
+ }, {
264
+ regex: /^\s*(\S+)\s+[tT]\s*([+-]?\d+)/,
265
+ map: (match) => {
266
+ if (match[2].match(/^[+-]/)) {
267
+ return { command: 'mod-thp', tag: match[1], value: parseInt(match[2]) };
268
+ }
269
+ else {
270
+ return { command: 'set-thp', tag: match[1], value: parseInt(match[2]) };
271
+ }
246
272
  }
247
- tracker[index].temphp = Math.max(0, thp);
248
- tracker[index].hp = Math.min(tracker[index].maxhp, Math.max(0, hp));
249
- yield promises_1.default.writeFile(argument, JSON.stringify(tracker, undefined, 2), { encoding: 'utf-8' });
273
+ }, {
274
+ regex: /^\s*(\S+)\s+[iI]\s*([+-]?\d+)/,
275
+ map: (match) => {
276
+ if (match[2].match(/^[+-]/)) {
277
+ return { command: 'mod-init', tag: match[1], value: parseInt(match[2]) };
278
+ }
279
+ else {
280
+ return { command: 'set-init', tag: match[1], value: parseInt(match[2]) };
281
+ }
282
+ },
283
+ }, {
284
+ regex: /^\s*(\S+)\s+[sS]?\s*=?"([^"]*)"/,
285
+ map: (match) => {
286
+ return { command: 'set-conditions', tag: match[1], value: match[2] };
287
+ },
288
+ }, {
289
+ regex: /^\s*(\S+)\s+[sS]?\s*\+"([^"]*)"/,
290
+ map: (match) => {
291
+ return { command: 'add-conditions', tag: match[1], value: match[2] };
292
+ },
293
+ }, {
294
+ regex: /^\s*(\S+)\s+[sS]?\s*-"([^"]*)"/,
295
+ map: (match) => {
296
+ return { command: 'rem-conditions', tag: match[1], value: match[2] };
297
+ },
298
+ }, {
299
+ regex: /^\s*(\S+)\s+[sS]?\s*\+\+"([^"]*)"/,
300
+ map: (match) => {
301
+ return { command: 'inc-conditions', tag: match[1], value: match[2] };
302
+ },
303
+ }, {
304
+ regex: /^\s*(\S+)\s+[sS]?\s*--"([^"]*)"/,
305
+ map: (match) => {
306
+ return { command: 'dec-conditions', tag: match[1], value: match[2] };
307
+ },
308
+ }, {
309
+ regex: /^\s*(\S+)\s+[cC]/,
310
+ map: (match) => ({ command: 'check', tag: match[1] }),
311
+ }, {
312
+ regex: /^\s*(\S+)\s+[aA]/,
313
+ map: (match) => ({ command: 'arrow', tag: match[1] }),
314
+ }, {
315
+ regex: /^\s*(\S+)\s+[eE]/,
316
+ map: (match) => ({ command: 'empty', tag: match[1] }),
317
+ }, {
318
+ regex: /^\s*(\S+)\s+[xX]/,
319
+ map: (match) => ({ command: 'cross', tag: match[1] }),
320
+ }, {
321
+ regex: /^\s*(\S+)\s+[dD]/,
322
+ map: (match) => ({ command: 'delete', tag: match[1] }),
323
+ }, {
324
+ regex: /^\s*(\S+)\s+[wW]/,
325
+ map: (match) => ({ command: 'wait', tag: match[1] }),
326
+ }];
327
+ const actions = [];
328
+ while (true) {
329
+ const command = commands.find((c) => input.match(c.regex));
330
+ if (!command) {
331
+ break;
250
332
  }
333
+ const match = input.match(command.regex);
334
+ if (!match) {
335
+ break;
336
+ }
337
+ const action = command.map(match);
338
+ input = input.slice(match[0].length);
339
+ actions.push(action);
251
340
  }
252
- const thpmod = input.match(/(\S+)\s+[tT]\s*([+-]?\d+)/);
253
- if (thpmod) {
254
- const file = yield promises_1.default.readFile(argument, { encoding: 'utf-8' });
255
- const tracker = (0, app_1.parseTracker)(file);
256
- const index = tracker.findIndex((c) => c.alias === thpmod[1]);
257
- if (index >= 0) {
258
- let thp = tracker[index].temphp;
259
- if (thpmod[2].match(/^[+-]/)) {
260
- thp += parseInt(thpmod[2]);
341
+ const file = yield promises_1.default.readFile(argument, { encoding: 'utf-8' });
342
+ let tracker = (0, app_1.parseTracker)(file);
343
+ for (const action of actions) {
344
+ if (action.command === 'block') {
345
+ const sorted = [...tracker].sort((a, b) => (a.init === b.init) ? (b.foe ? 1 : 0) - (a.foe ? 1 : 0) : b.init - a.init);
346
+ const blocks = [[sorted[0]]];
347
+ for (let i = 1; i < sorted.length; ++i) {
348
+ if (sorted[i].foe === blocks[blocks.length - 1][0].foe) {
349
+ blocks[blocks.length - 1].push(sorted[i]);
350
+ }
351
+ else {
352
+ blocks.push([sorted[i]]);
353
+ }
354
+ }
355
+ for (let i = 0; i < blocks.length; ++i) {
356
+ if (i < action.index) {
357
+ blocks[i].forEach((b) => b.state = 'check');
358
+ }
359
+ else if (i === action.index) {
360
+ blocks[i].forEach((b) => b.state = 'arrow');
361
+ }
362
+ else {
363
+ blocks[i].forEach((b) => b.state = 'empty');
364
+ }
365
+ }
366
+ }
367
+ else if (action.command === 'wait') {
368
+ const sorted = [...tracker].sort((a, b) => (a.init === b.init) ? (b.foe ? 1 : 0) - (a.foe ? 1 : 0) : b.init - a.init);
369
+ const blocks = [[sorted[0]]];
370
+ for (let i = 1; i < sorted.length; ++i) {
371
+ if (sorted[i].foe === blocks[blocks.length - 1][0].foe) {
372
+ blocks[blocks.length - 1].push(sorted[i]);
373
+ }
374
+ else {
375
+ blocks.push([sorted[i]]);
376
+ }
377
+ }
378
+ const blockIndex = blocks.findIndex((b) => b.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag));
379
+ const block = blocks[blockIndex + 1];
380
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag);
381
+ if (block && item) {
382
+ item.init = ((_b = (_a = block[block.length - 1]) === null || _a === void 0 ? void 0 : _a.init) !== null && _b !== void 0 ? _b : 0) - 1;
383
+ }
384
+ }
385
+ else if (action.command === 'check') {
386
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag);
387
+ if (item) {
388
+ item.state = "check";
389
+ }
390
+ }
391
+ else if (action.command === 'arrow') {
392
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag);
393
+ if (item) {
394
+ item.state = "arrow";
261
395
  }
262
- else {
263
- thp = parseInt(thpmod[2]);
396
+ }
397
+ else if (action.command === 'empty') {
398
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag);
399
+ if (item) {
400
+ item.state = "empty";
401
+ }
402
+ }
403
+ else if (action.command === 'cross') {
404
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag);
405
+ if (item) {
406
+ item.state = "cross";
407
+ }
408
+ }
409
+ else if (action.command === 'delete') {
410
+ tracker = tracker.filter((c) => !(c.id === action.tag || c.alias === action.tag || c.name == action.tag)).map((p) => (Object.assign(Object.assign({}, p), { children: p.children.filter((c) => !(c.id === action.tag || c.alias === action.tag || c.name == action.tag)) })));
411
+ }
412
+ else if (action.command === 'exit') {
413
+ process.exit(0);
414
+ }
415
+ else if (action.command === 'heal') {
416
+ tracker.forEach((char) => {
417
+ char.hp = char.maxhp;
418
+ char.children.forEach((child) => {
419
+ child.hp = child.maxhp;
420
+ });
421
+ });
422
+ }
423
+ else if (action.command === 'reset') {
424
+ tracker.forEach((char) => {
425
+ char.state = 'empty';
426
+ });
427
+ }
428
+ else if (action.command === 'set-thp') {
429
+ const item = (_c = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)) !== null && _c !== void 0 ? _c : (tracker.flatMap((c) => c.children).find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag));
430
+ if (item) {
431
+ item.temphp = Math.max(0, action.value);
432
+ }
433
+ }
434
+ else if (action.command === 'set-hp') {
435
+ const item = (_d = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)) !== null && _d !== void 0 ? _d : (tracker.flatMap((c) => c.children).find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag));
436
+ if (item) {
437
+ item.hp = Math.min(item.maxhp, Math.max(0, action.value));
438
+ }
439
+ }
440
+ else if (action.command === 'set-init') {
441
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag);
442
+ if (item) {
443
+ item.init = Math.max(0, action.value);
444
+ }
445
+ }
446
+ else if (action.command === 'set-conditions') {
447
+ const item = (_e = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)) !== null && _e !== void 0 ? _e : (tracker.flatMap((c) => c.children).find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag));
448
+ if (item) {
449
+ item.conditions = action.value.trim();
450
+ }
451
+ }
452
+ else if (action.command === 'add-conditions') {
453
+ const item = (_f = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)) !== null && _f !== void 0 ? _f : (tracker.flatMap((c) => c.children).find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag));
454
+ if (item) {
455
+ const conditions = [...item.conditions.split(',').map((c) => c.toLowerCase().trim()).filter((c) => c.length), action.value.toLowerCase().trim()];
456
+ item.conditions = conditions.join(', ');
457
+ }
458
+ }
459
+ else if (action.command === 'rem-conditions') {
460
+ const item = (_g = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)) !== null && _g !== void 0 ? _g : (tracker.flatMap((c) => c.children).find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag));
461
+ if (item) {
462
+ const conditions = item.conditions.split(',').map((c) => c.toLowerCase().trim()).filter((c) => c.length).filter((c) => !c.includes(action.value.toLowerCase().trim()));
463
+ item.conditions = conditions.join(', ');
464
+ }
465
+ }
466
+ else if (action.command === 'inc-conditions') {
467
+ const item = (_h = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)) !== null && _h !== void 0 ? _h : (tracker.flatMap((c) => c.children).find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag));
468
+ if (item) {
469
+ const conditions = item.conditions.split(',').map((c) => c.toLowerCase().trim()).filter((c) => c.length).map((c) => {
470
+ if (c.includes(action.value.toLowerCase().trim())) {
471
+ const match = c.match(/(.*)\s+(\d+)/);
472
+ if (match && match[1] && match[2]) {
473
+ return `${match[1].trim()} ${parseInt(match[2]) + 1}`;
474
+ }
475
+ }
476
+ return c;
477
+ });
478
+ item.conditions = conditions.join(', ');
479
+ }
480
+ }
481
+ else if (action.command === 'dec-conditions') {
482
+ const item = (_j = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)) !== null && _j !== void 0 ? _j : (tracker.flatMap((c) => c.children).find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag));
483
+ if (item) {
484
+ const conditions = item.conditions.split(',').map((c) => c.toLowerCase().trim()).filter((c) => c.length).map((c) => {
485
+ if (c.includes(action.value.toLowerCase().trim())) {
486
+ const match = c.match(/(.*)\s+(\d+)/);
487
+ if (match && match[1] && match[2]) {
488
+ if (parseInt(match[2]) > 1) {
489
+ return `${match[1].trim()} ${parseInt(match[2]) - 1}`;
490
+ }
491
+ else {
492
+ return match[1].trim();
493
+ }
494
+ }
495
+ }
496
+ return c;
497
+ });
498
+ item.conditions = conditions.join(', ');
499
+ }
500
+ }
501
+ else if (action.command === 'mod-thp') {
502
+ const item = (_k = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)) !== null && _k !== void 0 ? _k : (tracker.flatMap((c) => c.children).find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag));
503
+ if (item) {
504
+ item.temphp = Math.max(0, item.temphp + action.value);
505
+ }
506
+ }
507
+ else if (action.command === 'mod-hp') {
508
+ const item = (_l = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)) !== null && _l !== void 0 ? _l : (tracker.flatMap((c) => c.children).find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag));
509
+ if (item) {
510
+ let hp = item.hp;
511
+ let thp = item.temphp;
512
+ if (action.value < 0) {
513
+ thp += action.value;
514
+ hp += Math.min(thp, 0);
515
+ }
516
+ else {
517
+ hp += action.value;
518
+ }
519
+ item.temphp = Math.max(0, thp);
520
+ item.hp = Math.min(item.maxhp, Math.max(0, hp));
521
+ }
522
+ }
523
+ else if (action.command === 'mod-init') {
524
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag);
525
+ if (item) {
526
+ item.init = Math.max(0, item.init + action.value);
264
527
  }
265
- tracker[index].temphp = Math.max(0, thp);
266
- yield promises_1.default.writeFile(argument, JSON.stringify(tracker, undefined, 2), { encoding: 'utf-8' });
267
528
  }
268
529
  }
530
+ yield promises_1.default.writeFile(argument, JSON.stringify(tracker, undefined, 2), { encoding: 'utf-8' });
269
531
  }
270
532
  }))(),
271
533
  ]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pf2e-sage-stats",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "An RPG Sage's .tsv stat generation tool",
5
5
  "main": "dist/index.js",
6
6
  "author": "ikariott@gmail.com",
@@ -17,6 +17,7 @@
17
17
  "devDependencies": {
18
18
  "@eslint/js": "^9.17.0",
19
19
  "@types/jest": "^29.5.14",
20
+ "@types/jsdom": "^27.0.0",
20
21
  "@types/lodash": "^4.17.13",
21
22
  "@types/pluralize": "^0.0.33",
22
23
  "@types/tsv": "^0.2.4",
@@ -30,11 +31,14 @@
30
31
  },
31
32
  "dependencies": {
32
33
  "abbreviate": "^0.0.3",
34
+ "clipboardy": "^5.1.0",
33
35
  "commander": "^12.1.0",
34
36
  "dedent-js": "^1.0.1",
37
+ "jsdom": "^28.0.0",
35
38
  "lodash": "^4.17.21",
36
39
  "pluralize": "^8.0.0",
37
40
  "tsv": "^0.2.0",
38
41
  "zod": "^3.24.1"
39
- }
42
+ },
43
+ "packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72"
40
44
  }