{"owner":"voidcosmos","repo":"npkill","hasSkills":true,"totalSkillsCount":1,"totalTokensCount":1718,"categories":["project-spec"],"hasMcp":false,"mcpConfig":null,"found":["API.md"],"skills":{"API.md":"# NPKill API\n\nThis document does not include all project documentation at this stage. It brings together the basic concepts.\nFor more details see the project interfaces.\n\n- [NPKill API](#npkill-api)\n  - [Interface: `Npkill`](#interface-npkill)\n    - [`startScan$(rootPath, options?)`](#startscanrootpath-options)\n    - [`stopScan()`](#stopscan)\n    - [`getSize$(path, options?)`](#getsizepath-options)\n    - [`getNewestFile$(path)`](#getnewestfilepath)\n    - [`delete$(path, options?)`](#deletepath-options)\n    - [`getLogs$()`](#getlogs)\n    - [`isValidRootFolder(path)`](#isvalidrootfolderpath)\n    - [`getVersion()`](#getversion)\n  - [Interfaces & Types](#interfaces-types)\n    - [`ScanOptions`](#scanoptions)\n    - [`ScanFoundFolder`](#scanfoundfolder)\n    - [`RiskAnalysis`](#riskanalysis)\n    - [`GetSizeOptions`](#getsizeoptions)\n    - [`GetSizeResult`](#getsizeresult)\n    - [`GetNewestFileResult`](#getnewestfileresult)\n    - [`DeleteOptions`](#deleteoptions)\n  - [Usage Example](#usage-example)\n\n---\n\n## Interface: `Npkill`\n\nThe core of the system is the `NpkillInterface`. It offers methods to:\n\n- Scan folders recursively.\n- Get metadata about folders (size, last modified).\n- Perform safe deletions.\n- Stream logs and validate folders.\n\n### `startScan$(rootPath, options?)`\n\nStarts a recursive scan from a given root folder.\n\n- **Parameters**:\n  - `rootPath`: `string` — Folder to start scanning from.\n  - `options`: [`ScanOptions`](#scanoptions) — Optional scan configuration.\n\n- **Returns**: `Observable<ScanFoundFolder>`\n- **Description**: Emits each matching folder as it's found.\n\n---\n\n### `stopScan()`\n\nStops any ongoing scan and releases resources.\n\n---\n\n### `getSize$(path, options?)`\n\nReturns the total size of a directory.\n\n- **Parameters**:\n  - `path`: `string` — Path to folder.\n  - `options`: [`GetSizeOptions`](#getsizeoptions)\n\n- **Returns**: `Observable<GetSizeResult>`\n\n---\n\n### `getNewestFile$(path)`\n\nGets the most recently modified file inside a directory (recursively).\n\n- **Parameters**:\n  - `path`: `string`\n\n- **Returns**: `Observable<GetNewestFileResult | null>`\n\n---\n\n### `delete$(path, options?)`\n\nDeletes a folder, optionally as a dry-run. Only allowed if the folder is within the `target` of the initial scan.\n\n- **Parameters**:\n  - `path`: `string`\n  - `options`: [`DeleteOptions`](#deleteoptions)\n\n- **Returns**: `Observable<DeleteResult>`\n- **Throws**: If the path is outside the original target.\n\n---\n\n### `getLogs$()`\n\nStreams internal log entries.\n\n- **Returns**: `Observable<LogEntry[]>`\n\n---\n\n### `isValidRootFolder(path)`\n\nValidates whether a folder is suitable for scanning.\n\n- **Parameters**:\n  - `path`: `string`\n\n- **Returns**: [`IsValidRootFolderResult`](#isvalidrootfolderresult)\n\n---\n\n### `getVersion()`\n\nReturns the current version of npkill from `package.json`.\n\n- **Returns**: `string`\n\n---\n\n## Interfaces & Types\n\n---\n\n### `ScanOptions`\n\n```ts\ninterface ScanOptions {\n  targets: string[];\n  exclude?: string[];\n  sortBy?: 'path' | 'size' | 'age';\n  performRiskAnalysis?: boolean; // Default: true\n}\n```\n\n---\n\n### `ScanFoundFolder`\n\n```ts\ninterface ScanFoundFolder {\n  path: string;\n  riskAnalysis?: RiskAnalysis;\n}\n```\n\n---\n\n### `RiskAnalysis`\n\nDetermines whether a result is safe to delete. That is, if it is likely to belong to some application and deleting it could break it.\n\n```ts\ninterface RiskAnalysis {\n  isSensitive: boolean;\n  reason?: string;\n}\n```\n\n---\n\n### `GetSizeOptions`\n\n```ts\ninterface GetSizeOptions {\n  unit?: 'bytes'; // Default: 'bytes'\n}\n```\n\n---\n\n### `GetSizeResult`\n\n```ts\ninterface GetSizeResult {\n  size: number;\n  unit: 'bytes';\n}\n```\n\n---\n\n### `GetNewestFileResult`\n\n```ts\ninterface GetNewestFileResult {\n  path: string;\n  name: string;\n  timestamp: number;\n}\n```\n\n---\n\n### `DeleteOptions`\n\n```ts\ninterface DeleteOptions {\n  dryRun?: boolean;\n}\n```\n\n---\n\n## Usage Example\n\nThis is a minimal example where:\n\n1. it will start a search for `.nx` folders.\n2. Get the most recent file\n3. Get the total size of the directory\n\n```ts\nimport { Npkill } from 'npkill';\nimport { mergeMap, filter, map } from 'rxjs';\n\nconst npkill = new Npkill();\n\nlet files: {\n  path: string;\n  size: number;\n  newestFile: string;\n}[] = [];\n\nnpkill\n  .startScan$('/home/user/projects/', { target: '.nx' })\n  .pipe(\n    // Step 1: For each scan result, get the newest file\n    mergeMap((scanResult) =>\n      npkill.getNewestFile$(scanResult.path).pipe(\n        // Step 2: If no newest file, skip this result\n        filter((newestFile) => newestFile !== null),\n        // Step 3: Combine scanResult and newestFile\n        map((newestFile) => ({\n          path: scanResult.path,\n          newestFile: newestFile.path,\n        })),\n      ),\n    ),\n    // Step 4: For each result, get the folder size\n    mergeMap((result) =>\n      npkill.getSize$(result.path).pipe(\n        map(({ size }) => ({\n          ...result,\n          size,\n        })),\n      ),\n    ),\n  )\n  .subscribe({\n    next: (result) => {\n      files.push(result);\n    },\n    complete: () => {\n      console.log('✅ Scan complete. Found folders:', files.length);\n      console.table(files);\n      console.log(JSON.stringify(files));\n    },\n  });\n```\n\nOutput:\n\n```bash\n✅ Scan complete. Found folders: 3\n┌─────────┬───────────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────┬─────────┐\n│ (index) │ path                                      │ newestFile                                                               │ size    │\n├─────────┼───────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────┼─────────┤\n│ 0       │ '/home/user/projects/hello-world/.nx'     │ '/home/user/projects/hello-world/.nx/cache/18.3.4-nx.linux-x64-gnu.node' │ 9388032 │\n│ 1       │ '/home/user/projects/another-project/.nx' │ '/home/user/projects/another-project/.nx/workspace-data/d/daemon.log'    │ 3182592 │\n│ 2       │ '/home/user/projects/ARCHIVED/demo/.nx'   │ '/home/user/projects/ARCHIVED/demo/.nx/cache/d/daemon.log'               │ 2375680 │\n└─────────┴───────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────┴─────────┘\n[\n  {\n    \"path\": \"/home/user/projects/hello-world/.nx\",\n    \"newestFile\": \"/home/user/projects/hello-world/.nx/cache/18.3.4-nx.linux-x64-gnu.node\",\n    \"size\": 9388032\n  },\n  {\n    \"path\": \"/home/user/projects/another-project/.nx\",\n    \"newestFile\": \"/home/user/projects/another-project/.nx/workspace-data/d/daemon.log\",\n    \"size\": 3182592\n  },\n  ........\n]\n```\n"},"files":{"API.md":"# NPKill API\n\nThis document does not include all project documentation at this stage. It brings together the basic concepts.\nFor more details see the project interfaces.\n\n- [NPKill API](#npkill-api)\n  - [Interface: `Npkill`](#interface-npkill)\n    - [`startScan$(rootPath, options?)`](#startscanrootpath-options)\n    - [`stopScan()`](#stopscan)\n    - [`getSize$(path, options?)`](#getsizepath-options)\n    - [`getNewestFile$(path)`](#getnewestfilepath)\n    - [`delete$(path, options?)`](#deletepath-options)\n    - [`getLogs$()`](#getlogs)\n    - [`isValidRootFolder(path)`](#isvalidrootfolderpath)\n    - [`getVersion()`](#getversion)\n  - [Interfaces & Types](#interfaces-types)\n    - [`ScanOptions`](#scanoptions)\n    - [`ScanFoundFolder`](#scanfoundfolder)\n    - [`RiskAnalysis`](#riskanalysis)\n    - [`GetSizeOptions`](#getsizeoptions)\n    - [`GetSizeResult`](#getsizeresult)\n    - [`GetNewestFileResult`](#getnewestfileresult)\n    - [`DeleteOptions`](#deleteoptions)\n  - [Usage Example](#usage-example)\n\n---\n\n## Interface: `Npkill`\n\nThe core of the system is the `NpkillInterface`. It offers methods to:\n\n- Scan folders recursively.\n- Get metadata about folders (size, last modified).\n- Perform safe deletions.\n- Stream logs and validate folders.\n\n### `startScan$(rootPath, options?)`\n\nStarts a recursive scan from a given root folder.\n\n- **Parameters**:\n  - `rootPath`: `string` — Folder to start scanning from.\n  - `options`: [`ScanOptions`](#scanoptions) — Optional scan configuration.\n\n- **Returns**: `Observable<ScanFoundFolder>`\n- **Description**: Emits each matching folder as it's found.\n\n---\n\n### `stopScan()`\n\nStops any ongoing scan and releases resources.\n\n---\n\n### `getSize$(path, options?)`\n\nReturns the total size of a directory.\n\n- **Parameters**:\n  - `path`: `string` — Path to folder.\n  - `options`: [`GetSizeOptions`](#getsizeoptions)\n\n- **Returns**: `Observable<GetSizeResult>`\n\n---\n\n### `getNewestFile$(path)`\n\nGets the most recently modified file inside a directory (recursively).\n\n- **Parameters**:\n  - `path`: `string`\n\n- **Returns**: `Observable<GetNewestFileResult | null>`\n\n---\n\n### `delete$(path, options?)`\n\nDeletes a folder, optionally as a dry-run. Only allowed if the folder is within the `target` of the initial scan.\n\n- **Parameters**:\n  - `path`: `string`\n  - `options`: [`DeleteOptions`](#deleteoptions)\n\n- **Returns**: `Observable<DeleteResult>`\n- **Throws**: If the path is outside the original target.\n\n---\n\n### `getLogs$()`\n\nStreams internal log entries.\n\n- **Returns**: `Observable<LogEntry[]>`\n\n---\n\n### `isValidRootFolder(path)`\n\nValidates whether a folder is suitable for scanning.\n\n- **Parameters**:\n  - `path`: `string`\n\n- **Returns**: [`IsValidRootFolderResult`](#isvalidrootfolderresult)\n\n---\n\n### `getVersion()`\n\nReturns the current version of npkill from `package.json`.\n\n- **Returns**: `string`\n\n---\n\n## Interfaces & Types\n\n---\n\n### `ScanOptions`\n\n```ts\ninterface ScanOptions {\n  targets: string[];\n  exclude?: string[];\n  sortBy?: 'path' | 'size' | 'age';\n  performRiskAnalysis?: boolean; // Default: true\n}\n```\n\n---\n\n### `ScanFoundFolder`\n\n```ts\ninterface ScanFoundFolder {\n  path: string;\n  riskAnalysis?: RiskAnalysis;\n}\n```\n\n---\n\n### `RiskAnalysis`\n\nDetermines whether a result is safe to delete. That is, if it is likely to belong to some application and deleting it could break it.\n\n```ts\ninterface RiskAnalysis {\n  isSensitive: boolean;\n  reason?: string;\n}\n```\n\n---\n\n### `GetSizeOptions`\n\n```ts\ninterface GetSizeOptions {\n  unit?: 'bytes'; // Default: 'bytes'\n}\n```\n\n---\n\n### `GetSizeResult`\n\n```ts\ninterface GetSizeResult {\n  size: number;\n  unit: 'bytes';\n}\n```\n\n---\n\n### `GetNewestFileResult`\n\n```ts\ninterface GetNewestFileResult {\n  path: string;\n  name: string;\n  timestamp: number;\n}\n```\n\n---\n\n### `DeleteOptions`\n\n```ts\ninterface DeleteOptions {\n  dryRun?: boolean;\n}\n```\n\n---\n\n## Usage Example\n\nThis is a minimal example where:\n\n1. it will start a search for `.nx` folders.\n2. Get the most recent file\n3. Get the total size of the directory\n\n```ts\nimport { Npkill } from 'npkill';\nimport { mergeMap, filter, map } from 'rxjs';\n\nconst npkill = new Npkill();\n\nlet files: {\n  path: string;\n  size: number;\n  newestFile: string;\n}[] = [];\n\nnpkill\n  .startScan$('/home/user/projects/', { target: '.nx' })\n  .pipe(\n    // Step 1: For each scan result, get the newest file\n    mergeMap((scanResult) =>\n      npkill.getNewestFile$(scanResult.path).pipe(\n        // Step 2: If no newest file, skip this result\n        filter((newestFile) => newestFile !== null),\n        // Step 3: Combine scanResult and newestFile\n        map((newestFile) => ({\n          path: scanResult.path,\n          newestFile: newestFile.path,\n        })),\n      ),\n    ),\n    // Step 4: For each result, get the folder size\n    mergeMap((result) =>\n      npkill.getSize$(result.path).pipe(\n        map(({ size }) => ({\n          ...result,\n          size,\n        })),\n      ),\n    ),\n  )\n  .subscribe({\n    next: (result) => {\n      files.push(result);\n    },\n    complete: () => {\n      console.log('✅ Scan complete. Found folders:', files.length);\n      console.table(files);\n      console.log(JSON.stringify(files));\n    },\n  });\n```\n\nOutput:\n\n```bash\n✅ Scan complete. Found folders: 3\n┌─────────┬───────────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────┬─────────┐\n│ (index) │ path                                      │ newestFile                                                               │ size    │\n├─────────┼───────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────┼─────────┤\n│ 0       │ '/home/user/projects/hello-world/.nx'     │ '/home/user/projects/hello-world/.nx/cache/18.3.4-nx.linux-x64-gnu.node' │ 9388032 │\n│ 1       │ '/home/user/projects/another-project/.nx' │ '/home/user/projects/another-project/.nx/workspace-data/d/daemon.log'    │ 3182592 │\n│ 2       │ '/home/user/projects/ARCHIVED/demo/.nx'   │ '/home/user/projects/ARCHIVED/demo/.nx/cache/d/daemon.log'               │ 2375680 │\n└─────────┴───────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────┴─────────┘\n[\n  {\n    \"path\": \"/home/user/projects/hello-world/.nx\",\n    \"newestFile\": \"/home/user/projects/hello-world/.nx/cache/18.3.4-nx.linux-x64-gnu.node\",\n    \"size\": 9388032\n  },\n  {\n    \"path\": \"/home/user/projects/another-project/.nx\",\n    \"newestFile\": \"/home/user/projects/another-project/.nx/workspace-data/d/daemon.log\",\n    \"size\": 3182592\n  },\n  ........\n]\n```\n"},"items":[{"name":"API.md","path":"API.md","rawUrl":"https://raw.githubusercontent.com/voidcosmos/npkill/HEAD/API.md","title":"API Interface Contract & Specifications","category":"project-spec","format":"markdown","content":"# NPKill API\n\nThis document does not include all project documentation at this stage. It brings together the basic concepts.\nFor more details see the project interfaces.\n\n- [NPKill API](#npkill-api)\n  - [Interface: `Npkill`](#interface-npkill)\n    - [`startScan$(rootPath, options?)`](#startscanrootpath-options)\n    - [`stopScan()`](#stopscan)\n    - [`getSize$(path, options?)`](#getsizepath-options)\n    - [`getNewestFile$(path)`](#getnewestfilepath)\n    - [`delete$(path, options?)`](#deletepath-options)\n    - [`getLogs$()`](#getlogs)\n    - [`isValidRootFolder(path)`](#isvalidrootfolderpath)\n    - [`getVersion()`](#getversion)\n  - [Interfaces & Types](#interfaces-types)\n    - [`ScanOptions`](#scanoptions)\n    - [`ScanFoundFolder`](#scanfoundfolder)\n    - [`RiskAnalysis`](#riskanalysis)\n    - [`GetSizeOptions`](#getsizeoptions)\n    - [`GetSizeResult`](#getsizeresult)\n    - [`GetNewestFileResult`](#getnewestfileresult)\n    - [`DeleteOptions`](#deleteoptions)\n  - [Usage Example](#usage-example)\n\n---\n\n## Interface: `Npkill`\n\nThe core of the system is the `NpkillInterface`. It offers methods to:\n\n- Scan folders recursively.\n- Get metadata about folders (size, last modified).\n- Perform safe deletions.\n- Stream logs and validate folders.\n\n### `startScan$(rootPath, options?)`\n\nStarts a recursive scan from a given root folder.\n\n- **Parameters**:\n  - `rootPath`: `string` — Folder to start scanning from.\n  - `options`: [`ScanOptions`](#scanoptions) — Optional scan configuration.\n\n- **Returns**: `Observable<ScanFoundFolder>`\n- **Description**: Emits each matching folder as it's found.\n\n---\n\n### `stopScan()`\n\nStops any ongoing scan and releases resources.\n\n---\n\n### `getSize$(path, options?)`\n\nReturns the total size of a directory.\n\n- **Parameters**:\n  - `path`: `string` — Path to folder.\n  - `options`: [`GetSizeOptions`](#getsizeoptions)\n\n- **Returns**: `Observable<GetSizeResult>`\n\n---\n\n### `getNewestFile$(path)`\n\nGets the most recently modified file inside a directory (recursively).\n\n- **Parameters**:\n  - `path`: `string`\n\n- **Returns**: `Observable<GetNewestFileResult | null>`\n\n---\n\n### `delete$(path, options?)`\n\nDeletes a folder, optionally as a dry-run. Only allowed if the folder is within the `target` of the initial scan.\n\n- **Parameters**:\n  - `path`: `string`\n  - `options`: [`DeleteOptions`](#deleteoptions)\n\n- **Returns**: `Observable<DeleteResult>`\n- **Throws**: If the path is outside the original target.\n\n---\n\n### `getLogs$()`\n\nStreams internal log entries.\n\n- **Returns**: `Observable<LogEntry[]>`\n\n---\n\n### `isValidRootFolder(path)`\n\nValidates whether a folder is suitable for scanning.\n\n- **Parameters**:\n  - `path`: `string`\n\n- **Returns**: [`IsValidRootFolderResult`](#isvalidrootfolderresult)\n\n---\n\n### `getVersion()`\n\nReturns the current version of npkill from `package.json`.\n\n- **Returns**: `string`\n\n---\n\n## Interfaces & Types\n\n---\n\n### `ScanOptions`\n\n```ts\ninterface ScanOptions {\n  targets: string[];\n  exclude?: string[];\n  sortBy?: 'path' | 'size' | 'age';\n  performRiskAnalysis?: boolean; // Default: true\n}\n```\n\n---\n\n### `ScanFoundFolder`\n\n```ts\ninterface ScanFoundFolder {\n  path: string;\n  riskAnalysis?: RiskAnalysis;\n}\n```\n\n---\n\n### `RiskAnalysis`\n\nDetermines whether a result is safe to delete. That is, if it is likely to belong to some application and deleting it could break it.\n\n```ts\ninterface RiskAnalysis {\n  isSensitive: boolean;\n  reason?: string;\n}\n```\n\n---\n\n### `GetSizeOptions`\n\n```ts\ninterface GetSizeOptions {\n  unit?: 'bytes'; // Default: 'bytes'\n}\n```\n\n---\n\n### `GetSizeResult`\n\n```ts\ninterface GetSizeResult {\n  size: number;\n  unit: 'bytes';\n}\n```\n\n---\n\n### `GetNewestFileResult`\n\n```ts\ninterface GetNewestFileResult {\n  path: string;\n  name: string;\n  timestamp: number;\n}\n```\n\n---\n\n### `DeleteOptions`\n\n```ts\ninterface DeleteOptions {\n  dryRun?: boolean;\n}\n```\n\n---\n\n## Usage Example\n\nThis is a minimal example where:\n\n1. it will start a search for `.nx` folders.\n2. Get the most recent file\n3. Get the total size of the directory\n\n```ts\nimport { Npkill } from 'npkill';\nimport { mergeMap, filter, map } from 'rxjs';\n\nconst npkill = new Npkill();\n\nlet files: {\n  path: string;\n  size: number;\n  newestFile: string;\n}[] = [];\n\nnpkill\n  .startScan$('/home/user/projects/', { target: '.nx' })\n  .pipe(\n    // Step 1: For each scan result, get the newest file\n    mergeMap((scanResult) =>\n      npkill.getNewestFile$(scanResult.path).pipe(\n        // Step 2: If no newest file, skip this result\n        filter((newestFile) => newestFile !== null),\n        // Step 3: Combine scanResult and newestFile\n        map((newestFile) => ({\n          path: scanResult.path,\n          newestFile: newestFile.path,\n        })),\n      ),\n    ),\n    // Step 4: For each result, get the folder size\n    mergeMap((result) =>\n      npkill.getSize$(result.path).pipe(\n        map(({ size }) => ({\n          ...result,\n          size,\n        })),\n      ),\n    ),\n  )\n  .subscribe({\n    next: (result) => {\n      files.push(result);\n    },\n    complete: () => {\n      console.log('✅ Scan complete. Found folders:', files.length);\n      console.table(files);\n      console.log(JSON.stringify(files));\n    },\n  });\n```\n\nOutput:\n\n```bash\n✅ Scan complete. Found folders: 3\n┌─────────┬───────────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────┬─────────┐\n│ (index) │ path                                      │ newestFile                                                               │ size    │\n├─────────┼───────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────┼─────────┤\n│ 0       │ '/home/user/projects/hello-world/.nx'     │ '/home/user/projects/hello-world/.nx/cache/18.3.4-nx.linux-x64-gnu.node' │ 9388032 │\n│ 1       │ '/home/user/projects/another-project/.nx' │ '/home/user/projects/another-project/.nx/workspace-data/d/daemon.log'    │ 3182592 │\n│ 2       │ '/home/user/projects/ARCHIVED/demo/.nx'   │ '/home/user/projects/ARCHIVED/demo/.nx/cache/d/daemon.log'               │ 2375680 │\n└─────────┴───────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────┴─────────┘\n[\n  {\n    \"path\": \"/home/user/projects/hello-world/.nx\",\n    \"newestFile\": \"/home/user/projects/hello-world/.nx/cache/18.3.4-nx.linux-x64-gnu.node\",\n    \"size\": 9388032\n  },\n  {\n    \"path\": \"/home/user/projects/another-project/.nx\",\n    \"newestFile\": \"/home/user/projects/another-project/.nx/workspace-data/d/daemon.log\",\n    \"size\": 3182592\n  },\n  ........\n]\n```\n","isInternal":false,"tokens":1718,"sizeBytes":7467}],"systemPromptSnippet":"<yakaai_skills repo=\"voidcosmos/npkill\">\n<!-- File: API.md (Tokens: ~1718 | Category: project-spec) -->\n# NPKill API\n\nThis document does not include all project documentation at this stage. It brings together the basic concepts.\nFor more details see the project interfaces.\n\n- [NPKill API](#npkill-api)\n  - [Interface: `Npkill`](#interface-npkill)\n    - [`startScan$(rootPath, options?)`](#startscanrootpath-options)\n    - [`stopScan()`](#stopscan)\n    - [`getSize$(path, options?)`](#getsizepath-options)\n    - [`getNewestFile$(path)`](#getnewestfilepath)\n    - [`delete$(path, options?)`](#deletepath-options)\n    - [`getLogs$()`](#getlogs)\n    - [`isValidRootFolder(path)`](#isvalidrootfolderpath)\n    - [`getVersion()`](#getversion)\n  - [Interfaces & Types](#interfaces-types)\n    - [`ScanOptions`](#scanoptions)\n    - [`ScanFoundFolder`](#scanfoundfolder)\n    - [`RiskAnalysis`](#riskanalysis)\n    - [`GetSizeOptions`](#getsizeoptions)\n    - [`GetSizeResult`](#getsizeresult)\n    - [`GetNewestFileResult`](#getnewestfileresult)\n    - [`DeleteOptions`](#deleteoptions)\n  - [Usage Example](#usage-example)\n\n---\n\n## Interface: `Npkill`\n\nThe core of the system is the `NpkillInterface`. It offers methods to:\n\n- Scan folders recursively.\n- Get metadata about folders (size, last modified).\n- Perform safe deletions.\n- Stream logs and validate folders.\n\n### `startScan$(rootPath, options?)`\n\nStarts a recursive scan from a given root folder.\n\n- **Parameters**:\n  - `rootPath`: `string` — Folder to start scanning from.\n  - `options`: [`ScanOptions`](#scanoptions) — Optional scan configuration.\n\n- **Returns**: `Observable<ScanFoundFolder>`\n- **Description**: Emits each matching folder as it's found.\n\n---\n\n### `stopScan()`\n\nStops any ongoing scan and releases resources.\n\n---\n\n### `getSize$(path, options?)`\n\nReturns the total size of a directory.\n\n- **Parameters**:\n  - `path`: `string` — Path to folder.\n  - `options`: [`GetSizeOptions`](#getsizeoptions)\n\n- **Returns**: `Observable<GetSizeResult>`\n\n---\n\n### `getNewestFile$(path)`\n\nGets the most recently modified file inside a directory (recursively).\n\n- **Parameters**:\n  - `path`: `string`\n\n- **Returns**: `Observable<GetNewestFileResult | null>`\n\n---\n\n### `delete$(path, options?)`\n\nDeletes a folder, optionally as a dry-run. Only allowed if the folder is within the `target` of the initial scan.\n\n- **Parameters**:\n  - `path`: `string`\n  - `options`: [`DeleteOptions`](#deleteoptions)\n\n- **Returns**: `Observable<DeleteResult>`\n- **Throws**: If the path is outside the original target.\n\n---\n\n### `getLogs$()`\n\nStreams internal log entries.\n\n- **Returns**: `Observable<LogEntry[]>`\n\n---\n\n### `isValidRootFolder(path)`\n\nValidates whether a folder is suitable for scanning.\n\n- **Parameters**:\n  - `path`: `string`\n\n- **Returns**: [`IsValidRootFolderResult`](#isvalidrootfolderresult)\n\n---\n\n### `getVersion()`\n\nReturns the current version of npkill from `package.json`.\n\n- **Returns**: `string`\n\n---\n\n## Interfaces & Types\n\n---\n\n### `ScanOptions`\n\n```ts\ninterface ScanOptions {\n  targets: string[];\n  exclude?: string[];\n  sortBy?: 'path' | 'size' | 'age';\n  performRiskAnalysis?: boolean; // Default: true\n}\n```\n\n---\n\n### `ScanFoundFolder`\n\n```ts\ninterface ScanFoundFolder {\n  path: string;\n  riskAnalysis?: RiskAnalysis;\n}\n```\n\n---\n\n### `RiskAnalysis`\n\nDetermines whether a result is safe to delete. That is, if it is likely to belong to some application and deleting it could break it.\n\n```ts\ninterface RiskAnalysis {\n  isSensitive: boolean;\n  reason?: string;\n}\n```\n\n---\n\n### `GetSizeOptions`\n\n```ts\ninterface GetSizeOptions {\n  unit?: 'bytes'; // Default: 'bytes'\n}\n```\n\n---\n\n### `GetSizeResult`\n\n```ts\ninterface GetSizeResult {\n  size: number;\n  unit: 'bytes';\n}\n```\n\n---\n\n### `GetNewestFileResult`\n\n```ts\ninterface GetNewestFileResult {\n  path: string;\n  name: string;\n  timestamp: number;\n}\n```\n\n---\n\n### `DeleteOptions`\n\n```ts\ninterface DeleteOptions {\n  dryRun?: boolean;\n}\n```\n\n---\n\n## Usage Example\n\nThis is a minimal example where:\n\n1. it will start a search for `.nx` folders.\n2. Get the most recent file\n3. Get the total size of the directory\n\n```ts\nimport { Npkill } from 'npkill';\nimport { mergeMap, filter, map } from 'rxjs';\n\nconst npkill = new Npkill();\n\nlet files: {\n  path: string;\n  size: number;\n  newestFile: string;\n}[] = [];\n\nnpkill\n  .startScan$('/home/user/projects/', { target: '.nx' })\n  .pipe(\n    // Step 1: For each scan result, get the newest file\n    mergeMap((scanResult) =>\n      npkill.getNewestFile$(scanResult.path).pipe(\n        // Step 2: If no newest file, skip this result\n        filter((newestFile) => newestFile !== null),\n        // Step 3: Combine scanResult and newestFile\n        map((newestFile) => ({\n          path: scanResult.path,\n          newestFile: newestFile.path,\n        })),\n      ),\n    ),\n    // Step 4: For each result, get the folder size\n    mergeMap((result) =>\n      npkill.getSize$(result.path).pipe(\n        map(({ size }) => ({\n          ...result,\n          size,\n        })),\n      ),\n    ),\n  )\n  .subscribe({\n    next: (result) => {\n      files.push(result);\n    },\n    complete: () => {\n      console.log('✅ Scan complete. Found folders:', files.length);\n      console.table(files);\n      console.log(JSON.stringify(files));\n    },\n  });\n```\n\nOutput:\n\n```bash\n✅ Scan complete. Found folders: 3\n┌─────────┬───────────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────┬─────────┐\n│ (index) │ path                                      │ newestFile                                                               │ size    │\n├─────────┼───────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────┼─────────┤\n│ 0       │ '/home/user/projects/hello-world/.nx'     │ '/home/user/projects/hello-world/.nx/cache/18.3.4-nx.linux-x64-gnu.node' │ 9388032 │\n│ 1       │ '/home/user/projects/another-project/.nx' │ '/home/user/projects/another-project/.nx/workspace-data/d/daemon.log'    │ 3182592 │\n│ 2       │ '/home/user/projects/ARCHIVED/demo/.nx'   │ '/home/user/projects/ARCHIVED/demo/.nx/cache/d/daemon.log'               │ 2375680 │\n└─────────┴───────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────┴─────────┘\n[\n  {\n    \"path\": \"/home/user/projects/hello-world/.nx\",\n    \"newestFile\": \"/home/user/projects/hello-world/.nx/cache/18.3.4-nx.linux-x64-gnu.node\",\n    \"size\": 9388032\n  },\n  {\n    \"path\": \"/home/user/projects/another-project/.nx\",\n    \"newestFile\": \"/home/user/projects/another-project/.nx/workspace-data/d/daemon.log\",\n    \"size\": 3182592\n  },\n  ........\n]\n```\n</yakaai_skills>"}