Initial release: AI-powered job application framework

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
AI Job Search
2026-03-23 08:34:06 +01:00
committed by Mads Lorentzen
co-authored by Claude Opus 4.6
commit c66d599d75
73 changed files with 6539 additions and 0 deletions
+228
View File
@@ -0,0 +1,228 @@
---
name: jobdanmark-search
version: 1.0.0
description: >
Make sure to use this skill whenever the user mentions anything related to Danish
job listings, job search in Denmark, finding work in Denmark, or job vacancies on
Jobdanmark — even if they don't explicitly mention jobdanmark.dk. Also invoke this
skill for questions about specific Danish job categories, municipalities, job types,
or salaries in a job-search context. Trigger phrases include:
danish jobs, jobs in denmark, find job denmark, job search denmark, danish job listings,
jobdanmark, job opslag, find job, jobsøgning, ledige stillinger, stillingsopslag,
job i Danmark, fuldtidsjob, deltidsjob, studiejob, praktikplads, elev, fleksjob,
IT job denmark, sygeplejersker job, håndværker job, ingeniør job, pædagog job,
kontor job, leder job, salg job, hotel job, kirke job, job aarhus, job københavn,
job odense, job aalborg, job sjælland, job jylland, job fyn, jobkategorier denmark,
ledige job, ansøgningsfrist, søg job, job opslaget, jobopslag, danish vacancies,
work in denmark, employment denmark, job denmark, jobs near me denmark,
apprentice denmark, internship denmark, part-time denmark, full-time denmark.
context: fork
allowed-tools: Bash(bun run skills/jobdanmark-search/cli/src/cli.ts *)
---
# Jobdanmark Search Skill
Access live Danish job listings from the Jobdanmark.dk public API. No authentication needed.
Covers ~15,000+ active job listings across 10 categories and all Danish municipalities.
## When to use this skill
Invoke this skill when the user wants to:
- Search for job listings in Denmark (by keyword, location, category, or job type)
- Browse available jobs in a specific Danish city, municipality, zip code, or region
- Filter jobs by employment type (full-time, part-time, student job, apprentice, etc.)
- Look up the full details of a specific job posting including description and deadline
- List all job categories with current live job counts
- Resolve a job search term into a job title ID or category ID for precise filtering
- Find location suggestions (municipalities, zip codes, regions) for job search filters
## Commands
### Search job listings
```bash
bun run skills/jobdanmark-search/cli/src/cli.ts search [flags]
```
Key flags:
- `--text <keyword>` — free-text search, e.g. `elektriker`, `sygeplejerske`, `softwareudvikler`
- `--category <id>` — category ID (see Categories table below)
- `--jobtitle-id <id>` — job title ID from `autocomplete` results
- `--municipality <name>` — e.g. `Odense`, `København`, `Aarhus`
- `--zip <code>` — zip code, e.g. `5000`, `8000`, `2100`
- `--region <name>` — region name, e.g. `Midtjylland`, `Sjælland`
- `--job-type <types>` — comma-separated: `fuldtid`, `deltid`, `fleksjob`, `elev`, `studiejob`, `praktik`
- `--page <n>` — page number (30 items per page, server-enforced)
- `--limit <n>` — cap total results returned by CLI
- `--format json|table|plain`
> Per-page is fixed at 30 by the API. Use `--page` to paginate.
### Full job detail
```bash
bun run skills/jobdanmark-search/cli/src/cli.ts detail <slug> [--format json|plain]
```
`slug` is the URL path segment returned as `slug` in `search` results (e.g. `it-chef-soeges-til-rah`).
Returns full structured job data from the job page's JSON-LD, including title, organization, location, employment type, deadline, and full HTML description.
### List categories with live counts
```bash
bun run skills/jobdanmark-search/cli/src/cli.ts categories [--format json|table|plain]
```
Returns all 10 job categories with current live job counts. Useful for giving the user an overview of the job market.
### Autocomplete job titles and categories
```bash
bun run skills/jobdanmark-search/cli/src/cli.ts autocomplete --query "<text>" [--limit <n>]
```
Use this to resolve search terms into precise job title IDs (`--jobtitle-id`) or category IDs (`--category`) for `search`. The `value` field in results is the ID to pass to `search`.
### Suggest locations
```bash
bun run skills/jobdanmark-search/cli/src/cli.ts locations --query "<text>" [--limit <n>]
```
Returns matching municipalities, zip codes, and regions. Use `value` from results as `--municipality` or `--zip` in `search`.
---
## Categories
| ID | Danish | English |
|----|--------|---------|
| 227972 | Pædagogik, Uddannelse og Forskning | Education, Research |
| 227973 | Håndværk, Industri, Transport og Landbrug | Crafts, Industry, Transport, Agriculture |
| 227974 | Salg, Kommunikation, Marketing, og Design | Sales, Communication, Marketing, Design |
| 227975 | Pleje, Social og Sundhed | Care, Social, Health |
| 227976 | Hotel, Service, Restauration og Sikkerhed | Hotel, Service, Restaurant, Security |
| 227977 | Kontor, Finans og Økonomi | Office, Finance, Economy |
| 227978 | IT, Ingeniør og Energi | IT, Engineering, Energy |
| 227979 | Ledelse, HR og projektstyring | Management, HR, Project Management |
| 543415 | Kirke, Kultur og Underholdning | Church, Culture, Entertainment |
| 227980 | Øvrige job | Other jobs |
---
## How to use effectively
**Resolve locations first.** Use `locations` to find the correct municipality name or zip code before passing them to `search`:
```bash
bun run skills/jobdanmark-search/cli/src/cli.ts locations --query "Aarhus" --format plain
```
**Resolve job titles for precision.** Use `autocomplete` to get the exact job title ID when the user wants a specific role:
```bash
bun run skills/jobdanmark-search/cli/src/cli.ts autocomplete --query "sygeplejerske" --format plain
```
**Natural workflow: `search` → `detail`.**
1. Use `search` to get a list of matching jobs with their `slug`.
2. Call `detail <slug>` to get the full job posting with description, deadline, and organization details.
**Use `--format table` for comparisons**, `--format json` for data processing, and `--format plain` for single-record detail views.
**Pagination**: The API returns 30 items per page (server-enforced). Use `--page` to navigate through results. Use `--limit` to cap CLI output regardless of page size.
**Combine filters** for precise results — e.g. `--text` + `--job-type` + `--municipality` all work together in a single search request.
---
## Usage examples
### IT jobs in Copenhagen
```bash
bun run skills/jobdanmark-search/cli/src/cli.ts search \
--category 227978 \
--municipality "København" \
--job-type fuldtid \
--format table
```
### Nursing jobs anywhere in Denmark
```bash
bun run skills/jobdanmark-search/cli/src/cli.ts search \
--text "sygeplejerske" \
--category 227975 \
--format table
```
### Student jobs in Aarhus
```bash
bun run skills/jobdanmark-search/cli/src/cli.ts search \
--municipality "Aarhus" \
--job-type studiejob \
--format table
```
### What job categories are most active right now?
```bash
bun run skills/jobdanmark-search/cli/src/cli.ts categories --format table
```
### Full details for a specific job posting
```bash
bun run skills/jobdanmark-search/cli/src/cli.ts detail it-chef-soeges-til-rah --format plain
```
### Find jobs in zip code 8000
```bash
bun run skills/jobdanmark-search/cli/src/cli.ts search --zip 8000 --format table
```
### What electrician jobs are available?
```bash
bun run skills/jobdanmark-search/cli/src/cli.ts search \
--text "elektriker" \
--category 227973 \
--job-type "fuldtid,deltid" \
--format table
```
### Apprentice positions in all of Denmark
```bash
bun run skills/jobdanmark-search/cli/src/cli.ts search \
--job-type elev \
--page 1 \
--format table
```
---
## Output formats
| Format | Best for |
|--------|----------|
| `json` | Default — programmatic use, data processing, passing IDs between commands |
| `table` | Quick human-readable overviews and comparisons |
| `plain` | Single-record detail views (`detail`) |
All errors are written to **stderr** as `{ "error": "...", "code": "..." }` and the process exits with code `1`.
---
## Notes
- All data is from the public Jobdanmark.dk API — no credentials required.
- Pagination is 1-indexed (`--page 1` is the first page). 30 items per page, server-enforced.
- The `detail` command fetches the HTML job page and parses the embedded JSON-LD (schema.org/JobPosting). It does not use a separate JSON API.
- `slug` in search results is extracted from the API's relative `url` field (the path after `/job/`).
- `applicationDeadline` in search results can be `null` (no deadline set).
- Job type values for filters: `fuldtid`, `deltid`, `fleksjob`, `elev`, `studiejob`, `praktik`.
@@ -0,0 +1,452 @@
# jobdanmark-cli
CLI for the [Jobdanmark.dk](https://www.jobdanmark.dk) public job search API.
**Base URL**: `https://jobdanmark.dk`
**Authentication**: None required.
**Format**: All responses are JSON.
---
## Installation
```bash
cd skills/jobdanmark-search/cli
bun install
```
---
## Commands
| Command | Description |
|---------|-------------|
| `search` | Search job listings with filters |
| `detail` | Full detail for a single job posting (by slug) |
| `categories` | List all job categories with live counts |
| `autocomplete` | Suggest job titles and categories for a query |
| `locations` | Suggest municipalities, zip codes, and regions for a query |
All commands accept `--format json|table|plain` (default: `json`).
All errors are written to **stderr** as `{ "error": "...", "code": "..." }` and the process exits with code `1`.
---
## Job Types
| Value | Danish | English |
|-------|--------|---------|
| `fuldtid` | Fuldtid | Full-time |
| `deltid` | Deltid | Part-time |
| `fleksjob` | Fleksjob | Flex job |
| `elev` | Elev | Apprentice |
| `studiejob` | Studiejob | Student job |
| `praktik` | Praktik | Internship |
---
## Categories
| ID | Danish Title |
|----|-------------|
| 227972 | Pædagogik, Uddannelse og Forskning |
| 227973 | Håndværk, Industri, Transport og Landbrug |
| 227974 | Salg, Kommunikation, Marketing, og Design |
| 227975 | Pleje, Social og Sundhed |
| 227976 | Hotel, Service, Restauration og Sikkerhed |
| 227977 | Kontor, Finans og Økonomi |
| 227978 | IT, Ingeniør og Energi |
| 227979 | Ledelse, HR og projektstyring |
| 543415 | Kirke, Kultur og Underholdning |
| 227980 | Øvrige job |
---
## `search` — Search job listings
**Endpoint**: `POST https://jobdanmark.dk/api/jobsearch/search/{page}`
```bash
bun run src/cli.ts search [flags]
```
### Flags
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| `--text` | string | — | Free-text keyword search (job title, keyword) |
| `--category` | number | — | Category ID (see table above) |
| `--jobtitle-id` | number | — | Job title ID from `autocomplete` results |
| `--municipality` | string | — | Municipality name, e.g. `Odense`, `København` |
| `--zip` | string | — | Zip code, e.g. `5000` |
| `--region` | string | — | Region name |
| `--job-type` | string | — | Comma-separated job types: `fuldtid,deltid,fleksjob,elev,studiejob,praktik` |
| `--page` | number | `1` | Page number (30 items per page, server-enforced) |
| `--limit` | number | — | Cap total results returned by CLI (client-side) |
| `--format` | string | `json` | Output format: `json`, `table`, `plain` |
> **Note**: Per-page is fixed at 30 by the API. There is no `--per-page` flag.
> **Note**: Multiple `--job-type` values are passed as a comma-separated string and split by the CLI into the `jobTypes` array in the request body.
### Example
```bash
bun run src/cli.ts search --text "elektriker" --job-type fuldtid --municipality "Odense"
bun run src/cli.ts search --category 227978 --job-type "fuldtid,deltid" --page 2 --format table
bun run src/cli.ts search --text "sygeplejerske" --zip 8000 --limit 10
```
### Request body sent to API
```json
{
"jobTypes": ["fuldtid"],
"filters": [
{ "type": "freetext", "value": "elektriker", "displayText": "elektriker" },
{ "type": "municipality", "value": "Odense", "displayText": "Odense" }
],
"locationMode": "Text",
"distance": 50
}
```
> **Important**: All filter objects include a `displayText` field (required by the server). The `value` field is an integer for `category` and `jobtitle` filter types; a string for all others.
### Response shape
```json
{
"meta": {
"currentPage": 1,
"totalItems": 15610,
"itemsPrPage": 30,
"totalPages": 521
},
"results": [
{
"title": "IT-chef søges til RAH",
"companyName": "Rah Service A/S",
"companyLogo": {
"key": "71f1c950-abcd-1234-efgh-000000000000",
"url": "https://jobdanmark.dk/media/k1epc2kk/rah-service-logo.jpg",
"focalPoint": null
},
"companyLogoSvgMarkup": null,
"overlayColor": "#FFFFFF1F",
"companyAddress": "Ndr Ringvej 4 6950 Ringkøbing",
"jobTypes": ["fuldtid"],
"boostJob": true,
"publishedDate": "12-03-2026",
"applicationDeadline": "10-04-2026",
"url": "https://jobdanmark.dk/job/it-chef-soeges-til-rah",
"slug": "it-chef-soeges-til-rah",
"coverImage": {
"key": "cf06eb46-abcd-1234-efgh-000000000000",
"url": "https://jobdanmark.dk/media/idvbnt4y/rah-service-as-billede.png",
"focalPoint": { "top": 0.488, "left": 0.499 }
},
"silhouetteLogo": false
}
]
}
```
> **Notes**:
> - `url` is normalized to a full URL (CLI prepends `https://jobdanmark.dk` to the relative path from the API).
> - `slug` is extracted from the relative `url` field (the path segment after `/job/`).
> - `applicationDeadline` can be `null`.
> - `companyLogo` can be `null`.
> - `publishedDate` format: `"DD-MM-YYYY"`.
> - `coverImage` can be `null`.
---
## `detail` — Full job posting detail
**Method**: Fetch HTML from `https://jobdanmark.dk/job/{slug}`, extract `<script type="application/ld+json">` block.
```bash
bun run src/cli.ts detail <slug> [--format json|plain]
```
The `slug` is the URL path segment returned as `slug` in `search` results (e.g. `it-chef-soeges-til-rah`).
### Example
```bash
bun run src/cli.ts detail it-chef-soeges-til-rah
bun run src/cli.ts detail it-chef-soeges-til-rah --format plain
```
### Response shape
```json
{
"slug": "it-chef-soeges-til-rah",
"url": "https://jobdanmark.dk/job/it-chef-soeges-til-rah",
"title": "IT-chef søges til RAH",
"datePosted": "2026-03-12",
"validThrough": "2026-04-10",
"employmentType": ["FULL_TIME"],
"hiringOrganization": {
"name": "Rah Service A/S",
"logo": "https://jobdanmark.dk/media/k1epc2kk/rah-service-logo.jpg?width=50"
},
"jobLocation": {
"streetAddress": "Ndr Ringvej 4 6950 Ringkøbing",
"addressLocality": "Ringkøbing",
"addressRegion": "Vestjylland",
"postalCode": "6950",
"addressCountry": "DK"
},
"description": "<p>Full HTML description...</p>"
}
```
> **Note**: The `hiringOrganization.logo` and `validThrough` fields may be `null` if not present in the JSON-LD. `jobLocation` fields may be `null` if the location data is absent.
---
## `categories` — List categories with live job counts
**Endpoint**: `GET https://jobdanmark.dk/api/categorycount/getcounts`
```bash
bun run src/cli.ts categories [flags]
```
### Flags
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| `--limit` | number | — | Cap number of categories returned |
| `--format` | string | `json` | Output format: `json`, `table`, `plain` |
### Example
```bash
bun run src/cli.ts categories
bun run src/cli.ts categories --format table
```
### Response shape
```json
[
{
"id": 227972,
"title": "Pædagogik, Uddannelse og Forskning",
"helpText": "Pædagog, lærer, forsker og meget mere...",
"count": 1856
},
{
"id": 227973,
"title": "Håndværk, Industri, Transport og Landbrug",
"helpText": "Elektriker, VVS, maler og meget mere...",
"count": 2816
},
{
"id": 227974,
"title": "Salg, Kommunikation, Marketing, og Design",
"helpText": "Sælger, marketingkoordinator og meget mere...",
"count": 1423
},
{
"id": 227975,
"title": "Pleje, Social og Sundhed",
"helpText": "Sygeplejerske, social- og sundhedsassistent og meget mere...",
"count": 2104
},
{
"id": 227976,
"title": "Hotel, Service, Restauration og Sikkerhed",
"helpText": "Tjener, kok, receptionist og meget mere...",
"count": 987
},
{
"id": 227977,
"title": "Kontor, Finans og Økonomi",
"helpText": "Bogholder, revisor, kontorassistent og meget mere...",
"count": 1234
},
{
"id": 227978,
"title": "IT, Ingeniør og Energi",
"helpText": "Programmør, datamatiker, ingeniør og meget mere...",
"count": 1567
},
{
"id": 227979,
"title": "Ledelse, HR og projektstyring",
"helpText": "Leder, HR-konsulent, projektleder og meget mere...",
"count": 876
},
{
"id": 543415,
"title": "Kirke, Kultur og Underholdning",
"helpText": "Præst, musiker, skuespiller og meget mere...",
"count": 234
},
{
"id": 227980,
"title": "Øvrige job",
"helpText": "Andre job der ikke passer i de øvrige kategorier...",
"count": 2329
}
]
```
---
## `autocomplete` — Suggest job titles and categories
**Endpoint**: `GET https://jobdanmark.dk/api/search/autocomplete?q={query}`
```bash
bun run src/cli.ts autocomplete --query <text> [flags]
```
### Flags
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| `--query` | string | **required** | Search text to autocomplete |
| `--limit` | number | — | Cap total suggestions returned |
| `--format` | string | `json` | Output format: `json`, `table`, `plain` |
### Example
```bash
bun run src/cli.ts autocomplete --query "it"
bun run src/cli.ts autocomplete --query "sygeplejerske" --format table
```
### Response shape
```json
[
{
"title": "Jobtitler",
"items": [
{
"id": "title__454",
"text": "IT-konsulent",
"value": 454,
"category": "jobtitle",
"slug": "it-konsulent"
},
{
"id": "title__123",
"text": "IT-supporter",
"value": 123,
"category": "jobtitle",
"slug": "it-supporter"
}
]
},
{
"title": "Kategorier",
"items": [
{
"id": "category__227978",
"text": "IT, Ingeniør og Energi",
"value": 227978,
"category": "category",
"slug": "it-ingenioer-og-energi"
}
]
}
]
```
> **Note**: Returns an empty array `[]` when no suggestions match. `value` is a numeric ID usable as `--jobtitle-id` or `--category` in `search`. Groups with no matching items are omitted from the response.
---
## `locations` — Suggest location filters
**Endpoint**: `GET https://jobdanmark.dk/api/search/locations?q={query}`
```bash
bun run src/cli.ts locations --query <text> [flags]
```
### Flags
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| `--query` | string | **required** | Location text to search (city, zip code, region) |
| `--limit` | number | — | Cap total suggestions returned |
| `--format` | string | `json` | Output format: `json`, `table`, `plain` |
### Example
```bash
bun run src/cli.ts locations --query "Odense"
bun run src/cli.ts locations --query "8000" --format table
bun run src/cli.ts locations --query "Sjælland"
```
### Response shape
```json
[
{
"title": "Kommune",
"items": [
{
"id": "municipality__0461",
"text": "Odense",
"value": "Odense",
"category": "municipality",
"slug": "odense"
}
]
},
{
"title": "Postnummer",
"items": [
{
"id": "zip__5000",
"text": "5000 Odense C",
"value": "5000",
"category": "zip",
"slug": "5000-odense-c"
},
{
"id": "zip__5200",
"text": "5200 Odense V",
"value": "5200",
"category": "zip",
"slug": "5200-odense-v"
}
]
}
]
```
> **Note**: Returns `[]` when no locations match. The `value` field for municipalities is the display name (string); for zip codes it is the numeric string. Use `value` directly as `--municipality` or `--zip` in `search`.
---
## Error handling
All errors are written to **stderr** in JSON format and exit with code `1`:
```json
{ "error": "Job not found", "code": "NOT_FOUND" }
{ "error": "API request failed: 400 Bad Request", "code": "API_ERROR" }
{ "error": "--query is required", "code": "MISSING_REQUIRED" }
{ "error": "Failed to parse JSON-LD from job page", "code": "PARSE_ERROR" }
```
---
## URL construction
- Job detail pages: `https://jobdanmark.dk/job/{slug}`
- Company logo images: `https://jobdanmark.dk{companyLogo.url}` (prepend base URL to relative path)
- Cover images: `https://jobdanmark.dk{coverImage.url}` (prepend base URL to relative path)
@@ -0,0 +1,25 @@
{
"name": "jobdanmark-cli",
"version": "1.0.0",
"description": "CLI for the Jobdanmark.dk public job search API",
"type": "module",
"main": "src/cli.ts",
"bin": {
"jobdanmark": "src/cli.ts"
},
"scripts": {
"start": "bun run src/cli.ts",
"test": "bun test --timeout 30000",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@bunli/core": "latest",
"@bunli/utils": "latest",
"node-html-parser": "^6.1.0",
"zod": "^3.23.0"
},
"devDependencies": {
"typescript": "^5.4.0",
"@types/bun": "latest"
}
}
@@ -0,0 +1,20 @@
import { createCLI } from "@bunli/core"
import { search } from "./commands/search.js"
import { detail } from "./commands/detail.js"
import { categories } from "./commands/categories.js"
import { autocomplete } from "./commands/autocomplete.js"
import { locations } from "./commands/locations.js"
const cli = await createCLI({
name: "jobdanmark-cli",
version: "1.0.0",
description: "CLI for the Jobdanmark.dk public job search API",
})
cli.command(search)
cli.command(detail)
cli.command(categories)
cli.command(autocomplete)
cli.command(locations)
await cli.run()
@@ -0,0 +1,67 @@
export const BASE_URL = "https://jobdanmark.dk"
export async function apiFetch<T>(path: string, params?: Record<string, string>): Promise<T> {
let url = `${BASE_URL}${path}`
if (params && Object.keys(params).length > 0) {
const qs = new URLSearchParams(params)
url += `?${qs.toString()}`
}
const maxRetries = 6
let delay = 500
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url)
if (response.status === 429 || response.status >= 500) {
if (attempt === maxRetries) {
throw new Error(`API request failed: ${response.status} ${response.statusText}`)
}
const jitter = Math.floor(Math.random() * 500)
await new Promise((resolve) => setTimeout(resolve, delay + jitter))
delay = Math.min(delay * 2, 5000)
continue
}
if (!response.ok) {
throw new Error(`API request failed: ${response.status} ${response.statusText}`)
}
return response.json() as Promise<T>
}
throw new Error("API request failed after max retries")
}
export async function apiPost<T>(path: string, body: unknown): Promise<T> {
const url = `${BASE_URL}${path}`
const maxRetries = 6
let delay = 500
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
})
if (response.status === 429 || response.status >= 500) {
if (attempt === maxRetries) {
throw new Error(`API request failed: ${response.status} ${response.statusText}`)
}
const jitter = Math.floor(Math.random() * 500)
await new Promise((resolve) => setTimeout(resolve, delay + jitter))
delay = Math.min(delay * 2, 5000)
continue
}
if (!response.ok) {
throw new Error(`API request failed: ${response.status} ${response.statusText}`)
}
return response.json() as Promise<T>
}
throw new Error("API request failed after max retries")
}
export function writeError(error: string, code: string): void {
process.stderr.write(JSON.stringify({ error, code }) + "\n")
}
export function stripHtml(html: string): string {
return html.replace(/<[^>]*>/g, "").replace(/\s+/g, " ").trim()
}
@@ -0,0 +1,39 @@
import { join } from "path";
const CLI_PATH = join(import.meta.dir, "../src/cli.ts");
export interface CLIResult {
stdout: string;
stderr: string;
exitCode: number;
}
export async function runCLI(args: string[]): Promise<CLIResult> {
const proc = Bun.spawn(["bun", "run", CLI_PATH, ...args], {
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
proc.exited,
]);
return { stdout: stdout.trim(), stderr: stderr.trim(), exitCode };
}
export function parseJSON<T = unknown>(result: CLIResult): T {
if (result.exitCode !== 0) {
throw new Error(
`CLI exited with code ${result.exitCode}. stderr: ${result.stderr}`
);
}
try {
return JSON.parse(result.stdout) as T;
} catch {
throw new Error(
`Failed to parse JSON. stdout: ${result.stdout}\nstderr: ${result.stderr}`
);
}
}