@kitschpatrol/aphex 0.1.0 → 0.1.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/dist/aphex-swift CHANGED
Binary file
package/dist/index.d.ts CHANGED
@@ -4,8 +4,9 @@ import { OmitDeep, PartialDeep, Simplify } from "type-fest";
4
4
 
5
5
  //#region src/aphex-swift/cli-bridge.d.ts
6
6
  /**
7
- * Start an interactive session with aphex-swift.
8
- * Commands will be sent to this persistent process instead of spawning new ones.
7
+ * Start an interactive session with aphex-swift. Commands will be sent to this
8
+ * persistent process instead of spawning new ones.
9
+ *
9
10
  * @throws {Error} If the session is already active or fails to start
10
11
  */
11
12
  declare function interactiveSessionStart(): Promise<void>;
@@ -25,8 +26,8 @@ type ResourceInfo = {
25
26
  width: number;
26
27
  };
27
28
  /**
28
- * TypeScript type definition for the JSON representation of a PHAsset
29
- * from the iOS Photos framework (CodablePHAsset)
29
+ * TypeScript type definition for the JSON representation of a PHAsset from the
30
+ * iOS Photos framework (CodablePHAsset)
30
31
  */
31
32
  type PhotoInfo = {
32
33
  dateCreated: Date;
@@ -49,12 +50,15 @@ type AlbumInfo = {
49
50
  uuid: string;
50
51
  };
51
52
  /**
52
- * Get photo asset information for given identifiers (ID, filename, album name, or photo path)
53
+ * Get photo asset information for given identifiers (ID, filename, album name,
54
+ * or photo path)
55
+ *
53
56
  * @throws {Error} If the command fails
54
57
  */
55
58
  declare function aphexPhotoInfo(identifiers: string | string[], caseSensitive?: boolean): Promise<PhotoInfo[]>;
56
59
  /**
57
60
  * Get album info
61
+ *
58
62
  * @throws {Error} If the command fails
59
63
  */
60
64
  declare function aphexAlbumInfo(identifiers: string | string[], caseSensitive?: boolean): Promise<AlbumInfo[]>;
@@ -65,7 +69,8 @@ type ImageMimeType = (typeof IMAGE_MIME_TYPES)[number];
65
69
  //#endregion
66
70
  //#region src/pipeline/engines/applescript-gui.d.ts
67
71
  /**
68
- * Mirrors the export UI in Note that note that some options are contingent on others.
72
+ * Mirrors the export UI in Note that note that some options are contingent on
73
+ * others.
69
74
  *
70
75
  * The AppleScript implementation is smart about only "clicking" the necessary
71
76
  * options in the GUI.
@@ -120,8 +125,10 @@ type ExportApplePhotoOptions = {
120
125
  engineOriginal: ExportEngineOptions;
121
126
  engineOriginalAlpha: ExportEngineOptions;
122
127
  /**
123
- * Append a few digits from the image's local identifier in Photos.app, useful to avoid name collisions if exporting multiple album-worth of photos.
124
- * Only applies to `FileNameOptions` `title` or `uuid`. This is NOT the same as a content hash.
128
+ * Append a few digits from the image's local identifier in Photos.app, useful
129
+ * to avoid name collisions if exporting multiple album-worth of photos. Only
130
+ * applies to `FileNameOptions` `title` or `uuid`. This is NOT the same as a
131
+ * content hash.
125
132
  */
126
133
  fileNameAppendUuidFragment: boolean;
127
134
  fileNameNormalizeExtensions: boolean;
@@ -154,8 +161,8 @@ type ImageTags = {
154
161
  credit?: string | undefined; /** Description, used as alt text in HTML content */
155
162
  description?: string | undefined;
156
163
  /**
157
- Image Type, e.g. 'animation', 'diagram', 'illustration', 'screenshot',
158
- 'image', 'photo', 'render', 'video'
164
+ * Image Type, e.g. 'animation', 'diagram', 'illustration', 'screenshot',
165
+ * 'image', 'photo', 'render', 'video'
159
166
  */
160
167
  label?: Label | undefined;
161
168
  preservedFileName?: string | undefined;
@@ -192,20 +199,24 @@ type LossyFormat = 'avif' | 'jpeg' | 'none' | 'webp';
192
199
  * Configuration options for image compression.
193
200
  *
194
201
  * The compression process follows a three-tier approach:
202
+ *
195
203
  * 1. First attempts lossless compression if enabled
196
204
  * 2. Falls back to near-lossless compression if lossless exceeds size limit
197
- * 3. Finally uses lossy compression if other methods fail to meet size requirements
205
+ * 3. Finally uses lossy compression if other methods fail to meet size
206
+ * requirements
198
207
  *
199
208
  * Set format to 'none' to disable that compression tier.
200
209
  */
201
210
  type CompressImageOptions = {
202
211
  /**
203
- * Force compression even if the original image is already within the size limit.
204
- * When false, images smaller than maxFileSizeBytes are copied without modification.
212
+ * Force compression even if the original image is already within the size
213
+ * limit. When false, images smaller than maxFileSizeBytes are copied without
214
+ * modification.
205
215
  */
206
216
  forceCompression: boolean;
207
217
  /**
208
218
  * Format to use for lossless compression (first attempt).
219
+ *
209
220
  * - 'avif': Best compression but slower encoding
210
221
  * - 'png': Good compatibility, optimized with oxipng
211
222
  * - 'webp': Good balance of compression and speed
@@ -214,6 +225,7 @@ type CompressImageOptions = {
214
225
  losslessFormat: LosslessFormat;
215
226
  /**
216
227
  * Format to use for lossy compression (final fallback).
228
+ *
217
229
  * - 'avif': Best compression but very slow encoding
218
230
  * - 'jpeg': Good compatibility, uses mozjpeg encoder
219
231
  * - 'webp': Good balance of compression and speed
@@ -222,19 +234,21 @@ type CompressImageOptions = {
222
234
  lossyFormat: LossyFormat;
223
235
  /**
224
236
  * Quality level for lossy compression (0-1 scale).
237
+ *
225
238
  * - 0: Lowest quality, smallest file size
226
239
  * - 1: Highest quality, largest file size
227
240
  * - Recommended range: 0.85-0.96 for high-quality images
228
241
  */
229
242
  lossyQuality: number;
230
243
  /**
231
- * Maximum allowed file size in bytes.
232
- * Images exceeding this size will be compressed using the configured formats.
233
- * The compression process stops when this target is met or all options are exhausted.
244
+ * Maximum allowed file size in bytes. Images exceeding this size will be
245
+ * compressed using the configured formats. The compression process stops when
246
+ * this target is met or all options are exhausted.
234
247
  */
235
248
  maxFileSizeBytes: number;
236
249
  /**
237
250
  * Format to use for near-lossless compression (second attempt).
251
+ *
238
252
  * - 'webp': Only format currently supporting near-lossless mode
239
253
  * - 'none': Skip near-lossless compression
240
254
  */
@@ -321,16 +335,18 @@ type SyncResult = {
321
335
  /**
322
336
  * End the exiftool process gracefully
323
337
  *
324
- * This should be called when you're done using exiftool to prevent
325
- * hanging processes. It's safe to call multiple times.
338
+ * This should be called when you're done using exiftool to prevent hanging
339
+ * processes. It's safe to call multiple times.
326
340
  */
327
341
  declare function endExiftool(): Promise<void>;
328
342
  //#endregion
329
343
  //#region src/utilities/log.d.ts
330
344
  /**
331
- * Set the logger instance for the module.
332
- * Export this for library consumers to inject their own logger.
333
- * @param logger - Accepts either a LogLayer instance or a Console- or Stream-like log target
345
+ * Set the logger instance for the module. Export this for library consumers to
346
+ * inject their own logger.
347
+ *
348
+ * @param logger - Accepts either a LogLayer instance or a Console- or
349
+ * Stream-like log target
334
350
  */
335
351
  declare function setLogger(logger?: ILogBasic | ILogLayer<unknown>): void;
336
352
  //#endregion
package/dist/index.js CHANGED
@@ -20,9 +20,11 @@ import { deepEqual } from "fast-equals";
20
20
  */
21
21
  let log = createLogger("@kitschpatrol/aphex");
22
22
  /**
23
- * Set the logger instance for the module.
24
- * Export this for library consumers to inject their own logger.
25
- * @param logger - Accepts either a LogLayer instance or a Console- or Stream-like log target
23
+ * Set the logger instance for the module. Export this for library consumers to
24
+ * inject their own logger.
25
+ *
26
+ * @param logger - Accepts either a LogLayer instance or a Console- or
27
+ * Stream-like log target
26
28
  */
27
29
  function setLogger(logger) {
28
30
  log = injectionHelper(logger);
@@ -33,8 +35,8 @@ function setLogger(logger) {
33
35
  * Centralized exiftool instance management
34
36
  *
35
37
  * This module provides a managed exiftool instance with proper lifecycle
36
- * management to prevent hanging processes. The exiftool-vendored library
37
- * spawns persistent Perl processes that must be explicitly ended.
38
+ * management to prevent hanging processes. The exiftool-vendored library spawns
39
+ * persistent Perl processes that must be explicitly ended.
38
40
  */
39
41
  const exiftoolInstance = new ExifTool({
40
42
  maxProcs: 4,
@@ -52,8 +54,8 @@ function getExiftool() {
52
54
  /**
53
55
  * End the exiftool process gracefully
54
56
  *
55
- * This should be called when you're done using exiftool to prevent
56
- * hanging processes. It's safe to call multiple times.
57
+ * This should be called when you're done using exiftool to prevent hanging
58
+ * processes. It's safe to call multiple times.
57
59
  */
58
60
  async function endExiftool() {
59
61
  if (exiftoolInstance.pids.length > 0) {
@@ -65,8 +67,8 @@ async function endExiftool() {
65
67
  /**
66
68
  * Install process exit handlers for automatic cleanup
67
69
  *
68
- * This ensures exiftool processes are cleaned up even if the user
69
- * forgets to call endExiftool(). Only installs handlers once.
70
+ * This ensures exiftool processes are cleaned up even if the user forgets to
71
+ * call endExiftool(). Only installs handlers once.
70
72
  */
71
73
  function scheduleProcessCleanup() {
72
74
  if (processExitHandlerInstalled) return;
@@ -107,9 +109,8 @@ async function getSizeBytes(filePath) {
107
109
  return size;
108
110
  }
109
111
  /**
110
- * Normalizes file extensions, particularly for image files
111
- * Converts to lowercase.
112
- * Converts .jpg to .jpeg and .tif to .tiff
112
+ * Normalizes file extensions, particularly for image files Converts to
113
+ * lowercase. Converts .jpg to .jpeg and .tif to .tiff
113
114
  */
114
115
  function normalizeExtension(filePath) {
115
116
  const directory = path.dirname(filePath);
@@ -123,7 +124,9 @@ function normalizeExtension(filePath) {
123
124
  }
124
125
  /**
125
126
  * Converts a filename to a GitHub-style slug format
127
+ *
126
128
  * @param filePath - The path of the file
129
+ *
127
130
  * @returns The slug version of the filename without extension
128
131
  */
129
132
  function getSlugFilename(filePath) {
@@ -132,7 +135,9 @@ function getSlugFilename(filePath) {
132
135
  /**
133
136
  * Ensures the directory for a given file path exists, creating it if necessary.
134
137
  * Expands '~' to the user's home directory.
138
+ *
135
139
  * @param filePath - The full file path for which to ensure the directory exists
140
+ *
136
141
  * @returns The fully expanded directory path which definitely exists
137
142
  */
138
143
  async function ensureDirectoryExists(filePath) {
@@ -141,7 +146,8 @@ async function ensureDirectoryExists(filePath) {
141
146
  return expandedDirectory;
142
147
  }
143
148
  /**
144
- * Creates a temporary directory with an optional custom prefix based on provided labels
149
+ * Creates a temporary directory with an optional custom prefix based on
150
+ * provided labels
145
151
  */
146
152
  async function getTempDirectory(...labels) {
147
153
  const prefix = labels.length > 0 ? `com.kitschpatrol.aphex.${labels.map((l) => l.toLowerCase()).join(".")}.` : "com.kitschpatrol.aphex.";
@@ -188,8 +194,10 @@ function assertSingleElement(array) {
188
194
  //#endregion
189
195
  //#region src/utilities/paths.ts
190
196
  /**
191
- * import.meta.dirname for Node >= 20.19.0
192
- * @example getDirname(import.meta)
197
+ * Import.meta.dirname for Node >= 20.19.0
198
+ *
199
+ * @example
200
+ * getDirname(import.meta)
193
201
  */
194
202
  function getDirname(meta) {
195
203
  return dirname(fileURLToPath(process.env.PISCINA_WORKER_META_URL ?? meta.url));
@@ -209,7 +217,8 @@ function getPackageWorkersPath(meta) {
209
217
  return path.basename(dirname) === "dist" ? path.join(dirname, "workers") : path.join(dirname, "..", "workers");
210
218
  }
211
219
  /**
212
- * Binary directory path of package or distribution folder (Copied by native build script)
220
+ * Binary directory path of package or distribution folder (Copied by native
221
+ * build script)
213
222
  */
214
223
  function getPackageBinPath(meta) {
215
224
  const dirname = getDirname(meta);
@@ -221,8 +230,9 @@ const SHELL_SPECIAL_CHARS_REGEX = /[\s'"\\]/;
221
230
  let interactiveProcess;
222
231
  let currentRequest;
223
232
  /**
224
- * Start an interactive session with aphex-swift.
225
- * Commands will be sent to this persistent process instead of spawning new ones.
233
+ * Start an interactive session with aphex-swift. Commands will be sent to this
234
+ * persistent process instead of spawning new ones.
235
+ *
226
236
  * @throws {Error} If the session is already active or fails to start
227
237
  */
228
238
  async function interactiveSessionStart() {
@@ -386,7 +396,9 @@ function assertAlbumInfoArray(value) {
386
396
  if (!isAlbumInfoArray(value)) throw new Error("Invalid AlbumInfo array");
387
397
  }
388
398
  /**
389
- * Get photo asset information for given identifiers (ID, filename, album name, or photo path)
399
+ * Get photo asset information for given identifiers (ID, filename, album name,
400
+ * or photo path)
401
+ *
390
402
  * @throws {Error} If the command fails
391
403
  */
392
404
  async function aphexPhotoInfo(identifiers, caseSensitive = false) {
@@ -410,6 +422,7 @@ async function aphexPhotoInfo(identifiers, caseSensitive = false) {
410
422
  }
411
423
  /**
412
424
  * Get album info
425
+ *
413
426
  * @throws {Error} If the command fails
414
427
  */
415
428
  async function aphexAlbumInfo(identifiers, caseSensitive = false) {
@@ -433,6 +446,7 @@ async function aphexAlbumInfo(identifiers, caseSensitive = false) {
433
446
  }
434
447
  /**
435
448
  * Export photos for given identifiers to a destination directory
449
+ *
436
450
  * @throws {Error} If the command fails
437
451
  */
438
452
  async function aphexExport(identifiers, destination, caseSensitive = false, originals = false) {
@@ -464,7 +478,8 @@ function dateReviver$1(key, value) {
464
478
  //#endregion
465
479
  //#region src/aphex-swift/identifiers.ts
466
480
  /**
467
- * Takes a mix of photos, albums, strings, all resolved to a single array of PhotoInfo objects
481
+ * Takes a mix of photos, albums, strings, all resolved to a single array of
482
+ * PhotoInfo objects
468
483
  */
469
484
  async function resolveIdentifiers(identifiers) {
470
485
  const photoInfos = identifiers.filter((identifier) => isPhotoInfo(identifier));
@@ -892,8 +907,10 @@ function escapeForAppleScript(text) {
892
907
  }
893
908
  /**
894
909
  * Export a photo via AppleScript using the Photos app's scripting dictionary
910
+ *
895
911
  * @param photoUuid - UUID of the photo to export
896
912
  * @param forceOriginal - Whether to force export of original version
913
+ *
897
914
  * @returns Promise<string> - Path to the exported file
898
915
  */
899
916
  async function exportViaAppleScript(photoUuid, forceOriginal = false) {
@@ -941,8 +958,8 @@ const defaultExportViaAppleScriptGuiOptions = {
941
958
  tiffBitDepth: 8
942
959
  };
943
960
  /**
944
- * Export a photo via the Photos.app GUI to a temporary directory
945
- * Clean up and move the file as needed afterwards
961
+ * Export a photo via the Photos.app GUI to a temporary directory Clean up and
962
+ * move the file as needed afterwards
946
963
  */
947
964
  async function exportViaAppleScriptGui(uuid, options) {
948
965
  const tempDirectory = await getTempDirectory("engine", "applescript-gui");
@@ -973,8 +990,8 @@ async function exportViaAppleScriptGui(uuid, options) {
973
990
  //#endregion
974
991
  //#region src/pipeline/engines/file-system.ts
975
992
  /**
976
- * Export a photo via direct file system copy of the original or edited file to a temporary directory
977
- * Clean up and move the file as needed afterwards
993
+ * Export a photo via direct file system copy of the original or edited file to
994
+ * a temporary directory Clean up and move the file as needed afterwards
978
995
  */
979
996
  async function exportViaFileSystem(photoUuid, forceOriginal = false) {
980
997
  const [{ edited, original }] = isPhotoInfo(photoUuid) ? [photoUuid] : await aphexPhotoInfo(photoUuid);
@@ -1136,10 +1153,9 @@ async function getEngineForPhoto(photoInfo, options) {
1136
1153
  return engine;
1137
1154
  }
1138
1155
  /**
1139
- * Export a single album to a folder
1140
- * Optionally sync the output directory with the album, deleting any images that are no longer in the album
1141
- * Optionally audit the album for duplicate titles
1142
- * Does NOT further process the images
1156
+ * Export a single album to a folder Optionally sync the output directory with
1157
+ * the album, deleting any images that are no longer in the album Optionally
1158
+ * audit the album for duplicate titles Does NOT further process the images
1143
1159
  */
1144
1160
  //#endregion
1145
1161
  //#region src/pipeline/image-metadata.ts
@@ -1207,8 +1223,9 @@ async function optimizePng(imagePath, destinationDirectory) {
1207
1223
  return destinationImagePath;
1208
1224
  }
1209
1225
  /**
1210
- * Resize a PNG image to fit within a maximum width and height
1211
- * Copies the image unchanged if it already fits within the target size
1226
+ * Resize a PNG image to fit within a maximum width and height Copies the image
1227
+ * unchanged if it already fits within the target size
1228
+ *
1212
1229
  * @returns The destination image path
1213
1230
  */
1214
1231
  async function resizePngToFit(imagePath, destinationDirectory, maxWidth, maxHeight) {
@@ -1314,8 +1331,8 @@ async function convertToWebp(sourceImagePath, destinationDirectory, quality, for
1314
1331
  return destinationImagePath;
1315
1332
  }
1316
1333
  /**
1317
- * Convert an image to a PNG
1318
- * Supports width / height to help quality comparison tools match target sizes in one step
1334
+ * Convert an image to a PNG Supports width / height to help quality comparison
1335
+ * tools match target sizes in one step
1319
1336
  */
1320
1337
  async function convertToPng(sourceImagePath, destinationDirectory, force = false, width, height) {
1321
1338
  const mime = lookupImageMimeType(sourceImagePath, true);
@@ -1347,9 +1364,9 @@ async function convertToPng(sourceImagePath, destinationDirectory, force = false
1347
1364
  return destinationImagePath;
1348
1365
  }
1349
1366
  /**
1350
- * Convert an image to a TGA
1351
- * Only viable input to mozjpeg is TGA, BMP, and some other esoteric stuff...
1352
- * BMP emitted by sips is too modern for cjpeg, so we need to convert to TGA instead
1367
+ * Convert an image to a TGA Only viable input to mozjpeg is TGA, BMP, and some
1368
+ * other esoteric stuff... BMP emitted by sips is too modern for cjpeg, so we
1369
+ * need to convert to TGA instead
1353
1370
  */
1354
1371
  async function convertToTga(sourceImagePath, destinationDirectory, force = false) {
1355
1372
  const mime = lookupImageMimeType(sourceImagePath, [
@@ -1495,9 +1512,8 @@ async function compressImage(sourceImagePath, destinationDirectory, options) {
1495
1512
  const SSIM_REGEX = /All:\s*([\d.]+)/;
1496
1513
  const PSNR_REGEX = /average:\s*([\d.]+)/;
1497
1514
  /**
1498
- * Get two identically sized PNGs from two images.
1499
- * Uses the smaller image size as the target size.
1500
- * Remember to clean up temp
1515
+ * Get two identically sized PNGs from two images. Uses the smaller image size
1516
+ * as the target size. Remember to clean up temp
1501
1517
  */
1502
1518
  async function getTwoIdenticallySizedPng(image1, image2) {
1503
1519
  const tempDirectory1 = await fse.mkdtemp(path.join(os.tmpdir(), `com.kitschpatrol.aphex.get-two-identically-sized-png.${getSlugFilename(image1)}.`));
@@ -1816,6 +1832,7 @@ async function getSyncPlanForImages(identifiers, destinationDirectory, options,
1816
1832
  }
1817
1833
  /**
1818
1834
  * See if there's a difference between the source image and the target image
1835
+ *
1819
1836
  * @returns Undefined if the, or the reason for the difference if found
1820
1837
  */
1821
1838
  async function isDifferent(diffStrategies, sourcePhotoInfo, sourcePhotoFileBaseName, matchPhotoFilePath, matchPhotoTags, exportOptions, forceUpdate) {
@@ -7,14 +7,22 @@
7
7
  * Worker function for processing images in a separate thread
8
8
  *
9
9
  * Note: Each worker spawns its own exiftool instance via the centralized
10
- * exiftool module. These are managed by Piscina's worker lifecycle - when
11
- * the worker terminates, the exiftool process exit handlers will clean up.
10
+ * exiftool module. These are managed by Piscina's worker lifecycle - when the
11
+ * worker terminates, the exiftool process exit handlers will clean up.
12
+ *
12
13
  * @param {object} params - Worker parameters
13
- * @param {string} params.destinationDirectory - Directory where processed image will be saved
14
- * @param {import('../pipeline/image-process.ts').ProcessImageOptions} params.options - Image processing options
14
+ * @param {string} params.destinationDirectory - Directory where processed image
15
+ * will be saved
16
+ * @param {import('../pipeline/image-process.ts').ProcessImageOptions} params.options
17
+ * - Image processing options
18
+ *
15
19
  * @param {string} params.sourceImagePath - Path to the source image file
16
20
  * @param {boolean} params.verbose - Whether to log verbose output
17
- * @returns {Promise<import('../pipeline/image-process.ts').ProcessImageResult>} Processing result with input/output info and report
21
+ *
22
+ * @returns {Promise<
23
+ * import('../pipeline/image-process.ts').ProcessImageResult
24
+ * >}
25
+ * Processing result with input/output info and report
18
26
  */
19
27
  export default async function worker({ destinationDirectory, options, sourceImagePath, verbose }) {
20
28
  // Weird workaround after issues with ESM imports in worker threads and more recent versions of Node / TSX / Piscina / etc?
@@ -26,11 +34,13 @@ export default async function worker({ destinationDirectory, options, sourceImag
26
34
  if (verbose) {
27
35
  console.log(`Processing image on worker thread:\n${sourceImagePath}`)
28
36
  }
37
+
29
38
  const result = await processImage(sourceImagePath, destinationDirectory, options)
30
39
  if (verbose) {
31
40
  console.log(
32
41
  `Finished processing image on worker thread:\n${sourceImagePath}\nTime:\n${result.report.durationMs / 1000} seconds`,
33
42
  )
34
43
  }
44
+
35
45
  return result
36
46
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitschpatrol/aphex",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Apple Photos Export. TypeScript library and CLI tool to export and process images and albums from your macOS Photos.app library.",
5
5
  "keywords": [
6
6
  "apple",
@@ -42,7 +42,7 @@
42
42
  "@sindresorhus/is": "^8.0.0",
43
43
  "defu": "^6.1.7",
44
44
  "execa": "^9.6.1",
45
- "exiftool-vendored": "^35.18.0",
45
+ "exiftool-vendored": "^35.19.0",
46
46
  "fast-equals": "^6.0.0",
47
47
  "fs-extra": "^11.3.4",
48
48
  "github-slugger": "^2.0.0",
@@ -55,8 +55,8 @@
55
55
  },
56
56
  "devDependencies": {
57
57
  "@arethetypeswrong/core": "^0.18.2",
58
- "@clack/prompts": "^1.2.0",
59
- "@kitschpatrol/shared-config": "^7.5.0",
58
+ "@clack/prompts": "^1.3.0",
59
+ "@kitschpatrol/shared-config": "^7.5.2",
60
60
  "@types/fs-extra": "^11.0.4",
61
61
  "@types/node": "~22.18.13",
62
62
  "@vitest/coverage-v8": "^4.1.5",
package/readme.md CHANGED
@@ -9,7 +9,7 @@
9
9
  <!-- badges -->
10
10
 
11
11
  [![NPM Package @kitschpatrol/aphex](https://img.shields.io/npm/v/@kitschpatrol/aphex.svg)](https://npmjs.com/package/@kitschpatrol/aphex)
12
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
12
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/license/mit/)
13
13
 
14
14
  <!-- /badges -->
15
15