laufen 0.1.2 → 0.1.4

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.mjs CHANGED
@@ -494,7 +494,10 @@ function resolveWorkspacePackages() {
494
494
  return [];
495
495
  });
496
496
  const rootPkg = readPackageInfo(root);
497
- if (rootPkg) return [rootPkg, ...packages];
497
+ if (rootPkg) return [{
498
+ ...rootPkg,
499
+ name: "<root>"
500
+ }, ...packages];
498
501
  return packages;
499
502
  }
500
503
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["path","path","path","path","path","path","path","handleInit","handleList","handleInfo","handleRun","handleCreate"],"sources":["../src/lib/handler.ts","../src/lib/result.ts","../src/templates/script.ts","../src/utils/fs.ts","../src/utils/prompt.ts","../src/handlers/create.ts","../src/lib/workspace.ts","../src/lib/paths.ts","../src/runtime/runner.ts","../src/lib/discovery.ts","../src/utils/resolve-script.ts","../src/handlers/info.ts","../src/templates/config.ts","../src/handlers/init.ts","../src/utils/tree.ts","../src/handlers/list.ts","../src/utils/argv.ts","../src/handlers/run.ts","../src/index.ts"],"sourcesContent":["import * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport type { z } from 'zod';\n\nimport { formatArgErrors } from '../utils/cli.ts';\nimport type { HandlerResult } from './result.ts';\n\n/**\n * Object-form handler configuration that pairs a Zod schema for the\n * Clerc context with the handler function. The context is `safeParse`d\n * at runtime; validation failures exit cleanly with formatted errors.\n */\nexport interface HandlerConfig<S extends z.ZodType> {\n readonly parameters: S;\n readonly handler: (ctx: z.infer<S>) => HandlerResult | Promise<HandlerResult>;\n}\n\n/**\n * Define a Clerc-compatible command handler from a function that returns\n * `HandlerResult`. Centralizes error display and `process.exit` so\n * individual handlers never need to deal with side-effectful exits.\n */\nexport function defineHandler<Ctx>(\n fn: (ctx: Ctx) => HandlerResult | Promise<HandlerResult>,\n): (ctx: Ctx) => Promise<void>;\n\n/**\n * Define a Clerc-compatible command handler from a {@link HandlerConfig}\n * object. The Clerc context is validated against the provided Zod schema\n * before the handler is called.\n */\nexport function defineHandler<S extends z.ZodType>(\n config: HandlerConfig<S>,\n): (ctx: unknown) => Promise<void>;\n\nexport function defineHandler(\n fnOrConfig: ((ctx: never) => HandlerResult | Promise<HandlerResult>) | HandlerConfig<z.ZodType>,\n): (ctx: never) => Promise<void> {\n if (isHandlerConfig(fnOrConfig)) {\n return async (ctx: unknown): Promise<void> => {\n const parsed = fnOrConfig.parameters.safeParse(ctx);\n\n if (!parsed.success) {\n p.log.error(formatArgErrors(parsed.error.issues));\n process.exit(1);\n return;\n }\n\n await handleResult(fnOrConfig.handler(parsed.data));\n };\n }\n\n const fn = fnOrConfig;\n return async (ctx: never): Promise<void> => {\n await handleResult(fn(ctx));\n };\n}\n\n/**\n * Discriminate a plain handler function from a {@link HandlerConfig} object.\n */\nfunction isHandlerConfig(value: unknown): value is HandlerConfig<z.ZodType> {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n if (!('parameters' in value) || !('handler' in value)) {\n return false;\n }\n const obj = value as Record<string, unknown>;\n if (typeof obj.handler !== 'function') {\n return false;\n }\n return (\n typeof obj.parameters === 'object' && obj.parameters !== null && 'safeParse' in obj.parameters\n );\n}\n\n/**\n * Shared exit logic for resolved handler results.\n * Displays errors via `@clack/prompts` and calls `process.exit`.\n */\nasync function handleResult(result: HandlerResult | Promise<HandlerResult>): Promise<void> {\n const [error] = await Promise.resolve(result);\n\n if (error === null) {\n return;\n }\n\n if (error.exitCode === 0) {\n if (error.message) {\n p.log.info(error.message);\n }\n process.exit(0);\n return;\n }\n\n p.log.error(error.message);\n\n if (error.hint) {\n p.log.message(pc.dim(error.hint));\n }\n\n process.exit(error.exitCode ?? 1);\n}\n","/**\n * Structured error returned by handlers instead of throwing or calling process.exit.\n */\nexport interface HandlerError {\n readonly message: string;\n readonly hint?: string;\n readonly exitCode?: number;\n}\n\n/**\n * Generic discriminated tuple: success or failure.\n *\n * @typeParam T - The success value type\n * @typeParam E - The error type (defaults to `Error`)\n */\nexport type Result<T, E = Error> = readonly [E, null] | readonly [null, T];\n\n/**\n * Discriminated tuple: success or failure for handler operations.\n */\nexport type HandlerResult<T = void> = Result<T, HandlerError>;\n\n/**\n * Construct a success result.\n */\nexport function ok(): HandlerResult<void>;\nexport function ok<T>(value: T): HandlerResult<T>;\nexport function ok<T>(value?: T): HandlerResult<T> {\n return [null, value as T] as const;\n}\n\n/**\n * Construct a failure result.\n */\nexport function fail(error: HandlerError): HandlerResult<never> {\n return [error, null] as const;\n}\n","/**\n * Strip characters not in `[a-zA-Z0-9_-]` from a script name\n * to prevent template injection.\n *\n * @param name - Raw name input\n * @returns Sanitized name safe for template interpolation\n */\nexport function sanitizeName(name: string): string {\n return name.replaceAll(/[^a-zA-Z0-9_-]/g, '');\n}\n\n/**\n * Escape characters that could break a single-quoted string literal\n * (backslash and single quote).\n *\n * @param value - String to escape\n * @returns Escaped string safe for single-quoted template contexts\n */\nfunction escapeStringLiteral(value: string): string {\n return value.replaceAll('\\\\', '\\\\\\\\').replaceAll(\"'\", \"\\\\'\");\n}\n\n/**\n * Escape characters that could break a template literal (backtick and `${`).\n *\n * @param value - String to escape\n * @returns Escaped string safe for backtick template contexts\n */\nfunction escapeTemplateLiteral(value: string): string {\n return value.replaceAll('\\\\', '\\\\\\\\').replaceAll('`', '\\\\`').replaceAll('${', '\\\\${');\n}\n\n/**\n * Template for a new lauf script — written by `lauf create <name>`.\n *\n * @param name - Script name used in the template description\n * @returns Template string for the script file\n */\nexport function scriptTemplate(name: string): string {\n const safeName = sanitizeName(name);\n const singleQuoteSafe = escapeStringLiteral(safeName);\n const templateSafe = escapeTemplateLiteral(safeName);\n\n return `import { lauf, z } from 'laufen'\n\nexport default lauf({\n description: '${singleQuoteSafe} script',\n args: {\n verbose: z.boolean().default(false).describe('Enable verbose logging'),\n },\n async run(ctx) {\n if (ctx.args.verbose) {\n ctx.logger.info(\\`Running ${templateSafe} in \\${ctx.packageDir}\\`)\n }\n\n // TODO: implement ${safeName}\n ctx.logger.success('Hello from ${singleQuoteSafe}!')\n },\n})\n`;\n}\n","import * as fs from 'node:fs';\n\nimport { attempt } from 'es-toolkit';\n\n/**\n * Safely create directories recursively. Returns `[error, null]` or `[null, string | undefined]`.\n */\nexport function safeMkdirSync(dirPath: string): [unknown, null] | [null, string | undefined] {\n return attempt(() => fs.mkdirSync(dirPath, { recursive: true }));\n}\n","import * as p from '@clack/prompts';\n\nimport { fail, ok } from '../lib/result.ts';\nimport type { HandlerResult } from '../lib/result.ts';\nimport type { DiscoveredScript } from '../lib/types.ts';\n\n/**\n * Present an interactive select menu of discovered scripts.\n *\n * @param scripts - Available scripts to choose from\n * @returns The selected script, or a fail result on cancel/empty\n */\nexport async function promptForScript(\n scripts: readonly DiscoveredScript[],\n): Promise<HandlerResult<DiscoveredScript>> {\n if (scripts.length === 0) {\n return fail({\n message: 'No scripts found',\n hint: 'Run `lauf init` to get started.',\n });\n }\n\n const selected = await p.select({\n message: 'Select a script',\n options: scripts.map((s) => ({ value: s, label: s.name })),\n });\n\n if (p.isCancel(selected)) {\n p.cancel('Cancelled');\n return fail({ message: 'Cancelled', exitCode: 0 });\n }\n\n return ok(selected);\n}\n\n/**\n * Present an interactive text input prompt.\n *\n * @param message - Prompt message to display\n * @param placeholder - Optional placeholder text\n * @returns The entered string, or a fail result on cancel\n */\nexport async function promptForText(\n message: string,\n placeholder?: string,\n): Promise<HandlerResult<string>> {\n const value = await p.text({\n message,\n placeholder,\n });\n\n if (p.isCancel(value)) {\n p.cancel('Cancelled');\n return fail({ message: 'Cancelled', exitCode: 0 });\n }\n\n return ok(value);\n}\n","// oxlint-disable import/max-dependencies\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport * as p from '@clack/prompts';\nimport { attempt } from 'es-toolkit';\nimport pc from 'picocolors';\nimport { z } from 'zod';\n\nimport { safeLoadLaufConfigWithMeta } from '../lib/config.ts';\nimport { defineHandler } from '../lib/handler.ts';\nimport { fail, ok } from '../lib/result.ts';\nimport type { HandlerResult } from '../lib/result.ts';\nimport { scriptTemplate } from '../templates/script.ts';\nimport { readPackageJSON, safeParseError } from '../utils/cli.ts';\nimport { safeMkdirSync } from '../utils/fs.ts';\nimport { promptForText } from '../utils/prompt.ts';\n\nconst createParams = z.object({\n parameters: z.object({ name: z.string().min(1).optional() }),\n flags: z.object({ dir: z.string().optional() }),\n});\n\n/**\n * Handler for the `lauf create [name]` CLI command.\n *\n * Scaffolds a new lauf script file in the target directory\n * with a starter template including typed arguments.\n */\nexport default defineHandler({\n parameters: createParams,\n // oxlint-disable-next-line max-lines-per-function\n handler: async (ctx) => {\n const [nameError, name] = await resolveName(ctx.parameters.name);\n if (nameError) {\n return fail(nameError);\n }\n\n const { dir } = ctx.flags;\n\n const [configError, loaded] = await safeLoadLaufConfigWithMeta(process.cwd());\n if (configError) {\n return fail({ message: `Failed to load lauf config: ${safeParseError(configError)}` });\n }\n\n const targetDir = resolveTargetDir(dir, loaded.config.scripts, loaded.configDir);\n const normalizedTarget = path.normalize(targetDir);\n\n if (\n normalizedTarget !== loaded.configDir &&\n !normalizedTarget.startsWith(`${loaded.configDir}${path.sep}`)\n ) {\n return fail({\n message: `Target directory escapes config root: ${normalizedTarget}`,\n });\n }\n\n const stem = name.replace(/\\.lauf\\.ts$|\\.ts$/, '');\n const fileName = `${stem}.lauf.ts`;\n const filePath = path.join(targetDir, fileName);\n\n const resolvedFilePath = path.resolve(filePath);\n const normalizedTargetForFile = path.resolve(targetDir);\n /* v8 ignore next 5 -- defensive guard: unreachable with current name sanitisation */\n if (!resolvedFilePath.startsWith(`${normalizedTargetForFile}${path.sep}`)) {\n return fail({\n message: `File path escapes target directory: ${resolvedFilePath}`,\n });\n }\n\n const [mkdirError] = safeMkdirSync(targetDir);\n if (mkdirError) {\n return fail({ message: `Failed to create directory ${targetDir}: ${mkdirError}` });\n }\n\n const [writeError] = safeWriteFileExclusive(filePath, scriptTemplate(stem));\n if (writeError) {\n if (writeError instanceof Error) {\n const nodeError = writeError as NodeJS.ErrnoException;\n if (nodeError.code === 'EEXIST') {\n return fail({ message: `File already exists: ${filePath}` });\n }\n }\n return fail({ message: `Failed to write ${filePath}: ${writeError}` });\n }\n\n const [, pkg] = readPackageJSON(loaded.configDir);\n /* v8 ignore next -- fallback branch: readPackageJSON always returns a name in practice */\n const packageName = (pkg && pkg.name) || path.basename(loaded.configDir);\n const qualifiedName = `${packageName}/${stem}`;\n\n const relative = path.relative(loaded.configDir, filePath);\n p.log.success(`Created ${relative}`);\n p.log.message(pc.dim(`Run it with: lauf run ${qualifiedName}`));\n\n return ok();\n },\n});\n\n/**\n * Resolve the script name — from the CLI parameter or via interactive prompt.\n */\nfunction resolveName(name: string | undefined): Promise<HandlerResult<string>> {\n if (name) {\n return Promise.resolve(ok(name));\n }\n return promptForText('Enter a name for the new script', 'my-script');\n}\n\n/**\n * Resolve the target directory for a new script.\n *\n * When no --dir flag is provided, resolves relative to process.cwd().\n * When --dir is provided with an absolute path, uses it directly.\n * When --dir is provided with a relative path, resolves from the config directory.\n *\n * @param dir - Optional directory override\n * @param patterns - Script glob patterns from config\n * @param configDir - Directory containing the active config\n * @returns Absolute path to the target scripts directory\n * @private\n */\nfunction resolveTargetDir(dir: string | undefined, patterns: string[], configDir: string): string {\n if (dir) {\n if (path.isAbsolute(dir)) {\n return path.normalize(dir);\n }\n return path.resolve(configDir, dir);\n }\n const firstPattern = patterns[0];\n if (firstPattern) {\n return path.resolve(process.cwd(), path.dirname(firstPattern));\n }\n return path.resolve(process.cwd(), 'scripts');\n}\n\n/**\n * Atomically write a file, failing if it already exists (wx flag).\n * Prevents TOCTOU race between existence check and write.\n */\nfunction safeWriteFileExclusive(filePath: string, content: string): [unknown, null] | [null, void] {\n return attempt(() => fs.writeFileSync(filePath, content, { encoding: 'utf-8', flag: 'wx' }));\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { attempt } from 'es-toolkit';\nimport * as yaml from 'yaml';\n\nimport { safeParseJSON } from '../utils/json.ts';\n\nconst MAX_PARENT_ITERATIONS = 200;\n\ntype WorkspaceManager = 'pnpm' | 'npm' | 'yarn' | 'bun' | 'lerna' | 'single';\n\ninterface WorkspaceInfo {\n readonly manager: WorkspaceManager;\n readonly root: string;\n readonly globs: readonly string[];\n}\n\ninterface WorkspaceDetector {\n readonly manager: WorkspaceManager;\n readonly marker: string;\n readonly extractGlobs: (content: string) => readonly string[] | undefined;\n}\n\n/**\n * Safe YAML parse wrapper matching the `safeParseJSON` convention.\n *\n * @param content - Raw YAML string\n * @returns Tuple of [error, result]\n */\nfunction safeParseYaml(content: string): [Error, null] | [null, unknown] {\n return attempt(() => yaml.parse(content) as unknown);\n}\n\n/**\n * Validate that every element in an array is a string.\n *\n * @param arr - Array to validate\n * @returns `true` if all elements are strings\n */\nfunction isStringArray(arr: readonly unknown[]): arr is readonly string[] {\n return arr.every((x) => typeof x === 'string');\n}\n\n/**\n * Extract workspace globs from `pnpm-workspace.yaml`.\n *\n * @param content - Raw YAML content\n * @returns Array of glob patterns, or `undefined` if parsing fails\n */\nfunction extractPnpmGlobs(content: string): readonly string[] | undefined {\n const [err, config] = safeParseYaml(content);\n if (err) {\n return undefined;\n }\n if (\n config &&\n typeof config === 'object' &&\n 'packages' in config &&\n Array.isArray(config.packages) &&\n isStringArray(config.packages)\n ) {\n return config.packages;\n }\n return undefined;\n}\n\n/**\n * Extract workspace globs from `package.json`.\n *\n * Handles both `\"workspaces\": [...]` and `\"workspaces\": { \"packages\": [...] }` shapes.\n *\n * @param content - Raw JSON content\n * @returns Array of glob patterns, or `undefined` if no workspaces field\n */\nfunction extractPackageJsonGlobs(content: string): readonly string[] | undefined {\n const [err, parsed] = safeParseJSON(content);\n if (err || parsed === null) {\n return undefined;\n }\n\n /* v8 ignore next 3 -- defensive: safeParseJSON only returns objects/arrays for valid JSON */\n if (typeof parsed !== 'object') {\n return undefined;\n }\n\n const pkg = parsed as Record<string, unknown>;\n const workspaces = pkg.workspaces;\n\n if (Array.isArray(workspaces) && isStringArray(workspaces)) {\n return workspaces;\n }\n\n if (workspaces && typeof workspaces === 'object' && 'packages' in workspaces) {\n const inner = (workspaces as Record<string, unknown>).packages;\n if (Array.isArray(inner) && isStringArray(inner)) {\n return inner;\n }\n }\n\n return undefined;\n}\n\n/**\n * Extract workspace globs from `lerna.json`.\n *\n * @param content - Raw JSON content\n * @returns Array of glob patterns, or `undefined` if parsing fails\n */\nfunction extractLernaGlobs(content: string): readonly string[] | undefined {\n const [err, parsed] = safeParseJSON(content);\n if (err || !parsed || typeof parsed !== 'object') {\n return undefined;\n }\n const config = parsed as Record<string, unknown>;\n if (Array.isArray(config.packages) && isStringArray(config.packages)) {\n return config.packages;\n }\n return undefined;\n}\n\n/**\n * Ordered list of workspace detectors, checked in priority order.\n *\n * pnpm is checked first (dedicated workspace file), then package.json\n * workspaces (npm/yarn/bun), then lerna.\n */\nconst DETECTORS: readonly WorkspaceDetector[] = [\n { manager: 'pnpm', marker: 'pnpm-workspace.yaml', extractGlobs: extractPnpmGlobs },\n { manager: 'npm', marker: 'package.json', extractGlobs: extractPackageJsonGlobs },\n { manager: 'lerna', marker: 'lerna.json', extractGlobs: extractLernaGlobs },\n];\n\n/**\n * Refine manager name for package.json-based workspaces by checking lockfiles.\n *\n * @param dir - Directory to check for lockfiles\n * @returns Refined manager name\n */\nfunction refinePackageJsonManager(dir: string): WorkspaceManager {\n if (fs.existsSync(path.join(dir, 'yarn.lock'))) {\n return 'yarn';\n }\n if (fs.existsSync(path.join(dir, 'bun.lockb')) || fs.existsSync(path.join(dir, 'bun.lock'))) {\n return 'bun';\n }\n return 'npm';\n}\n\n/**\n * Resolve the manager for a given detector match.\n *\n * @param dir - Directory where the marker was found\n * @param detector - The matched detector\n * @returns Resolved workspace manager\n */\nfunction resolveManager(dir: string, detector: WorkspaceDetector): WorkspaceManager {\n if (detector.marker === 'package.json') {\n return refinePackageJsonManager(dir);\n }\n return detector.manager;\n}\n\n/**\n * Try a single detector against a directory.\n *\n * @param dir - Directory to scan\n * @param detector - Detector to try\n * @returns WorkspaceInfo if the detector matches, or `undefined`\n */\nfunction detectFromMarker(dir: string, detector: WorkspaceDetector): WorkspaceInfo | undefined {\n const markerPath = path.join(dir, detector.marker);\n if (!fs.existsSync(markerPath)) {\n return undefined;\n }\n const content = fs.readFileSync(markerPath, 'utf-8');\n const globs = detector.extractGlobs(content);\n if (globs === undefined) {\n return undefined;\n }\n const manager = resolveManager(dir, detector);\n return { manager, root: dir, globs };\n}\n\n/**\n * Try all detectors against a single directory.\n *\n * @param dir - Directory to scan\n * @returns WorkspaceInfo if any detector matches, or `undefined`\n */\nfunction tryDetectors(dir: string): WorkspaceInfo | undefined {\n const results = DETECTORS.map((detector) => detectFromMarker(dir, detector));\n return results.find((r): r is WorkspaceInfo => r !== null && r !== undefined);\n}\n\n/**\n * Walk up from `startDir` checking each directory for workspace markers.\n *\n * @param startDir - Starting directory\n * @returns WorkspaceInfo for the detected workspace, or a `'single'` fallback\n */\nfunction resolveWorkspace(startDir: string = process.cwd()): WorkspaceInfo {\n const walk = (dir: string, prevDir: string, iterations: number): WorkspaceInfo => {\n if (dir === prevDir || iterations >= MAX_PARENT_ITERATIONS) {\n return { manager: 'single', root: startDir, globs: [] };\n }\n\n const detected = tryDetectors(dir);\n if (detected) {\n return detected;\n }\n\n return walk(path.dirname(dir), dir, iterations + 1);\n };\n\n return walk(startDir, '', 0);\n}\n\n/**\n * Module-level cache keyed by cwd. The Map reference is const;\n * only its entries are mutated (acceptable for a cache).\n */\n// oxlint-disable-next-line prefer-const -- Map is const; entries are cache mutations\nconst workspaceCache = new Map<string, WorkspaceInfo>();\n\n/**\n * Return the full workspace info (manager, root, globs).\n *\n * Computes the workspace info on first access for the given cwd\n * and caches the result.\n */\nexport function getWorkspaceInfo(cwd: string = process.cwd()): WorkspaceInfo {\n const cached = workspaceCache.get(cwd);\n if (cached !== undefined) {\n return cached;\n }\n const info = resolveWorkspace(cwd);\n // oxlint-disable-next-line immutable-data -- cache mutation\n workspaceCache.set(cwd, info);\n return info;\n}\n\n/**\n * Return just the workspace root path.\n */\nexport function getWorkspaceRoot(cwd: string = process.cwd()): string {\n return getWorkspaceInfo(cwd).root;\n}\n\n/**\n * Reset the cached workspace info. Intended for testing only.\n */\nexport function resetWorkspaceCache(): void {\n // oxlint-disable-next-line immutable-data -- cache clear for testing\n workspaceCache.clear();\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport { attempt } from 'es-toolkit';\nimport fg from 'fast-glob';\n\nimport { safeParseJSON } from '../utils/json.ts';\nimport type { Result } from './result.ts';\nimport { getWorkspaceInfo, getWorkspaceRoot } from './workspace.ts';\n\nexport { getWorkspaceRoot };\n\n/**\n * Absolute path to the lauf package directory.\n */\nexport const LAUF_ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url)));\n\n/**\n * Resolve the path to the tsx binary in lauf's own node_modules.\n * Returns an error string if tsx is not found, directing the user\n * to run their package manager's install command.\n *\n * @returns Tuple of [errorMessage, null] or [null, absolutePath]\n */\nexport function resolveTsx(): Result<string> {\n const tsxPath = path.join(LAUF_ROOT, 'node_modules/.bin/tsx');\n if (fs.existsSync(tsxPath)) {\n return [null, tsxPath];\n }\n return [\n new Error(\n `tsx binary not found at ${tsxPath}. Run your package manager's install command (e.g. \"pnpm install\") to install dependencies.`,\n ),\n null,\n ];\n}\n\ninterface PackageInfo {\n readonly name: string;\n readonly dir: string;\n}\n\n/**\n * Resolve all workspace packages from detected workspace globs.\n *\n * Includes the workspace root itself if it has a valid `package.json`.\n *\n * @returns Array of package info objects with name and absolute directory path\n */\nexport function resolveWorkspacePackages(): readonly PackageInfo[] {\n const { root, globs } = getWorkspaceInfo();\n\n const dirs = fg.sync(\n globs.map((g) => g.replace(/\\/$/, '')),\n {\n cwd: root,\n onlyDirectories: true,\n absolute: true,\n },\n );\n\n const packages = dirs.flatMap((dir) => {\n const info = readPackageInfo(dir);\n if (info) {\n return [info];\n }\n return [];\n });\n\n const rootPkg = readPackageInfo(root);\n if (rootPkg) {\n return [rootPkg, ...packages];\n }\n return packages;\n}\n\n/**\n * Read the package name from a directory's `package.json`.\n *\n * @param dir - Absolute path to the directory\n * @returns Package info if valid, or `undefined` if missing or malformed\n * @private\n */\nfunction readPackageInfo(dir: string): PackageInfo | undefined {\n const pkgJsonPath = path.join(dir, 'package.json');\n const [error, content] = attempt(() => fs.readFileSync(pkgJsonPath, 'utf-8'));\n if (error || content === null) {\n return undefined;\n }\n\n const [parseError, pkg] = safeParseJSON(content);\n if (parseError || pkg === null) {\n return undefined;\n }\n\n if (typeof pkg !== 'object' || !('name' in pkg) || typeof pkg.name !== 'string') {\n return undefined;\n }\n\n return { name: pkg.name, dir };\n}\n","import type { ChildProcess } from 'node:child_process';\nimport { spawn } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport * as p from '@clack/prompts';\nimport { attempt } from 'es-toolkit';\n\nimport { LAUF_ROOT, getWorkspaceRoot, resolveTsx } from '../lib/paths.ts';\nimport type { DiscoveredScript, RunResult } from '../lib/types.ts';\nimport { safeParseError } from '../utils/cli.ts';\n\nconst EXECUTOR_DIST_PATH = path.join(LAUF_ROOT, 'dist', 'runtime', 'executor.mjs');\nconst EXECUTOR_SRC_PATH = path.join(LAUF_ROOT, 'src', 'runtime', 'executor.ts');\n\n/**\n * Resolve the executor entry point, preferring the built dist version\n * and falling back to the source .ts path if dist is unavailable.\n *\n * Returns undefined if neither exists.\n */\nfunction resolveExecutorPath(): string | undefined {\n const [distErr, distExists] = attempt(() => fs.existsSync(EXECUTOR_DIST_PATH));\n if (!distErr && distExists) {\n return EXECUTOR_DIST_PATH;\n }\n\n const [srcErr, srcExists] = attempt(() => fs.existsSync(EXECUTOR_SRC_PATH));\n if (!srcErr && srcExists) {\n return EXECUTOR_SRC_PATH;\n }\n\n return undefined;\n}\n\n/**\n * Execute a discovered script by spawning tsx with the executor entry point.\n *\n * The script runs in a child process with context passed via environment\n * variables. The promise resolves when the child process exits.\n *\n * NOTE: The full parent process environment is intentionally inherited by\n * child scripts so that user-authored scripts can access env vars they\n * depend on (e.g. API keys, database URLs, CI variables). Filtering the\n * environment would break legitimate use cases. The metadata extraction\n * path (in list.ts) should apply its own filtering if needed.\n *\n * @param script - The discovered script to run\n * @param args - Parsed arguments to pass to the script\n * @param options - Optional flags (help mode, config directory)\n * @returns Promise resolving to the run result with exit code\n */\ninterface RunScriptOptions {\n readonly help?: boolean;\n readonly configDir?: string;\n}\n\n// oxlint-disable-next-line max-lines-per-function\nexport function runScript(\n script: DiscoveredScript,\n args: Record<string, unknown>,\n options?: RunScriptOptions,\n): Promise<RunResult> {\n const tsxResult = resolveTsx();\n if (tsxResult[0]) {\n p.log.error(tsxResult[0].message);\n return Promise.resolve({ exitCode: 1, script });\n }\n\n const executorPath = resolveExecutorPath();\n if (!executorPath) {\n p.log.error('Executor entry point not found. Run `pnpm build` to generate it.');\n return Promise.resolve({ exitCode: 1, script });\n }\n\n // TypeScript doesn't narrow tuple index [1] after checking [0]; safe cast after guard above\n const tsxPath = tsxResult[1] as string;\n const configDir = resolveConfigDir(options);\n const helpEnv = resolveHelpEnv(options);\n\n return new Promise((resolve) => {\n const child = spawn(tsxPath, [executorPath], {\n cwd: script.packageDir,\n stdio: 'inherit',\n env: {\n ...process.env,\n NODE_PATH: buildNodePath(),\n LAUF_SCRIPT_PATH: script.path,\n LAUF_ARGS: JSON.stringify(args),\n LAUF_WORKSPACE_ROOT: getWorkspaceRoot(),\n LAUF_CONFIG_DIR: configDir,\n LAUF_PACKAGE_DIR: script.packageDir,\n LAUF_SCRIPT_NAME: script.name,\n ...helpEnv,\n },\n });\n\n const signalCleanup = registerSignalForwarding(child);\n\n // AbortController signals settlement so only the first event\n // (close or error) resolves the promise. The second event is a no-op.\n const ac = new AbortController();\n\n child.once('close', (code) => {\n if (ac.signal.aborted) {\n return;\n }\n ac.abort();\n signalCleanup();\n resolve({\n exitCode: code ?? 1,\n script,\n });\n });\n\n child.once('error', (err) => {\n if (ac.signal.aborted) {\n return;\n }\n ac.abort();\n signalCleanup();\n p.log.error(`Failed to spawn script executor: ${safeParseError(err)}`);\n resolve({\n exitCode: 1,\n script,\n });\n });\n });\n}\n\n/**\n * Resolve the config directory from options, falling back to workspace root.\n */\nfunction resolveConfigDir(options: RunScriptOptions | undefined): string {\n if (options && options.configDir) {\n return options.configDir;\n }\n return getWorkspaceRoot();\n}\n\n/**\n * Resolve help-mode environment variables from options.\n */\nfunction resolveHelpEnv(options: RunScriptOptions | undefined): Record<string, string> {\n if (options && options.help) {\n return { LAUF_HELP: '1' };\n }\n return {};\n}\n\n/**\n * Register SIGINT and SIGTERM forwarding to the child process.\n * Returns a cleanup function that removes the signal handlers.\n *\n * @param child - The spawned child process\n * @returns Cleanup function to remove signal handlers\n * @private\n */\nfunction registerSignalForwarding(child: ChildProcess): () => void {\n const forwardSigint = (): void => {\n child.kill('SIGINT');\n };\n const forwardSigterm = (): void => {\n child.kill('SIGTERM');\n };\n\n process.on('SIGINT', forwardSigint);\n process.on('SIGTERM', forwardSigterm);\n\n return () => {\n process.removeListener('SIGINT', forwardSigint);\n process.removeListener('SIGTERM', forwardSigterm);\n };\n}\n\n/**\n * Build `NODE_PATH` so scripts can resolve lauf's dependencies (zod, etc.)\n * without requiring each package to declare them explicitly.\n *\n * @returns Colon-delimited `NODE_PATH` string\n * @private\n */\nfunction buildNodePath(): string {\n const paths = [\n path.join(LAUF_ROOT, 'node_modules'),\n path.join(getWorkspaceRoot(), 'node_modules'),\n ];\n const existing = process.env.NODE_PATH;\n if (existing) {\n return [...paths, existing].join(path.delimiter);\n }\n return paths.join(path.delimiter);\n}\n","import * as path from 'node:path';\n\nimport fg from 'fast-glob';\n\nimport { resolveWorkspacePackages } from './paths.ts';\nimport type { DiscoveredScript } from './types.ts';\nimport { getWorkspaceRoot } from './workspace.ts';\n\n/**\n * Validate that a glob pattern is safe for workspace-scoped discovery.\n *\n * Rejects patterns that start with `..` (parent traversal) or `/` (absolute path),\n * which could escape the workspace boundary.\n *\n * @param pattern - Glob pattern to validate\n * @returns `true` if the pattern is safe, `false` otherwise\n */\nfunction isValidPattern(pattern: string): boolean {\n // Reject null bytes (path poisoning)\n /* v8 ignore next 3 -- defensive security guard; callers never produce null bytes */\n if (pattern.includes('\\0')) {\n return false;\n }\n // Reject absolute paths (Unix `/` prefix and Windows drive letters like `C:\\...`)\n if (pattern.startsWith('..') || pattern.startsWith('/') || path.isAbsolute(pattern)) {\n return false;\n }\n // Reject patterns with embedded parent traversal (e.g. \"scripts/../../sensitive/*.ts\")\n const normalized = path.normalize(pattern);\n if (normalized.startsWith('..') || normalized.startsWith('/') || path.isAbsolute(normalized)) {\n return false;\n }\n return true;\n}\n\n/**\n * Filter discovered scripts to those whose resolved paths are within the workspace root.\n *\n * @param scripts - Scripts discovered by globbing\n * @param workspaceRoot - Absolute path to the workspace root\n * @returns Only scripts with paths inside the workspace root\n */\nfunction filterToWorkspace(\n scripts: readonly DiscoveredScript[],\n workspaceRoot: string,\n): readonly DiscoveredScript[] {\n const normalizedRoot = path.resolve(workspaceRoot);\n return scripts.filter((script) => {\n const resolved = path.resolve(script.path);\n return resolved === normalizedRoot || resolved.startsWith(`${normalizedRoot}${path.sep}`);\n });\n}\n\n/**\n * Options for script discovery.\n */\ninterface DiscoverOptions {\n /**\n * When provided, only discover scripts in packages whose directories\n * are within this path. Used by the `--all` flag to scope each config's\n * discovery to its own subtree.\n */\n readonly scopeDir?: string;\n}\n\n/**\n * Discover all lauf scripts across the monorepo.\n *\n * Scans each workspace package for files matching the given glob patterns\n * and returns qualified names in the format `<package-name>/<script-stem>`.\n *\n * When `options.scopeDir` is provided, only packages under that directory\n * are included in the discovery.\n *\n * @param patterns - Glob patterns relative to each package directory\n * @param options - Optional discovery options\n * @returns Readonly array of discovered scripts, sorted by name\n *\n * @example\n * ```ts\n * const scripts = discoverScripts(['scripts/*.ts'])\n * // [{ name: '@apps/api/generate-types', path: '/...', ... }, ...]\n * ```\n */\nexport function discoverScripts(\n patterns: string[],\n options?: DiscoverOptions,\n): readonly DiscoveredScript[] {\n const validPatterns = patterns.filter((p) => isValidPattern(p));\n\n if (validPatterns.length === 0) {\n return [];\n }\n\n const packages = resolveWorkspacePackages();\n const workspaceRoot = getWorkspaceRoot();\n const scopedPackages = filterPackagesByScope(packages, options);\n\n const scripts = scopedPackages\n .flatMap((pkg) => {\n const files = fg.sync(validPatterns, {\n cwd: pkg.dir,\n absolute: true,\n onlyFiles: true,\n });\n\n return files.map((filePath): DiscoveredScript => {\n const stem = stripScriptSuffix(path.basename(filePath, '.ts'));\n return {\n name: `${pkg.name}/${stem}`,\n path: filePath,\n packageDir: pkg.dir,\n packageName: pkg.name,\n };\n });\n })\n .toSorted((a, b) => a.name.localeCompare(b.name));\n\n return filterToWorkspace(scripts, workspaceRoot);\n}\n\n/**\n * Filter packages to those whose directories are within the scope directory.\n *\n * When no scopeDir is provided, returns all packages unchanged.\n */\nfunction filterPackagesByScope(\n packages: readonly { readonly name: string; readonly dir: string }[],\n options: DiscoverOptions | undefined,\n): readonly { readonly name: string; readonly dir: string }[] {\n if (!options || !options.scopeDir) {\n return packages;\n }\n const scope = path.resolve(options.scopeDir);\n return packages.filter((pkg) => {\n const resolved = path.resolve(pkg.dir);\n return resolved === scope || resolved.startsWith(`${scope}${path.sep}`);\n });\n}\n\nfunction stripScriptSuffix(stem: string): string {\n if (stem.endsWith('.laufen')) {\n return stem.slice(0, -'.laufen'.length);\n }\n if (stem.endsWith('.lauf')) {\n return stem.slice(0, -'.lauf'.length);\n }\n return stem;\n}\n\n/**\n * Find a single script by its qualified name.\n *\n * When a pre-discovered scripts array is provided, searches within it\n * instead of re-discovering all scripts. This avoids redundant globbing\n * when the caller already has the full list.\n *\n * @param scriptName - Qualified name (e.g. `@apps/sandbox/generate-types`)\n * @param patterns - Glob patterns relative to each package directory\n * @param scripts - Optional pre-discovered scripts array to search within\n * @returns The discovered script, or `undefined` if not found\n *\n * @example\n * ```ts\n * const allScripts = discoverScripts(['scripts/*.ts'])\n * const script = findScript('@apps/api/generate-types', ['scripts/*.ts'], allScripts)\n * if (script) {\n * console.log(script.path)\n * }\n * ```\n */\nexport function findScript(\n scriptName: string,\n patterns: string[],\n scripts?: readonly DiscoveredScript[],\n): DiscoveredScript | undefined {\n if (scripts !== undefined) {\n return scripts.find((s) => s.name === scriptName);\n }\n return discoverScripts(patterns).find((s) => s.name === scriptName);\n}\n","import { discoverScripts, findScript } from '../lib/discovery.ts';\nimport { fail, ok } from '../lib/result.ts';\nimport type { HandlerResult } from '../lib/result.ts';\nimport type { DiscoveredScript } from '../lib/types.ts';\nimport { promptForScript } from './prompt.ts';\n\n/**\n * Resolve which script to target — by name if provided, or via interactive prompt.\n *\n * @param scriptName - Qualified script name (e.g. `@apps/api/generate-types`), or undefined to prompt\n * @param patterns - Glob patterns relative to each package directory\n * @returns The resolved script, or a fail result if not found or cancelled\n */\nexport function resolveScript(\n scriptName: string | undefined,\n patterns: string[],\n): Promise<HandlerResult<DiscoveredScript>> {\n if (scriptName) {\n const script = findScript(scriptName, patterns);\n if (!script) {\n return Promise.resolve(\n fail({\n message: `Script not found: ${scriptName}`,\n hint: 'Run `lauf list` to see available scripts.',\n }),\n );\n }\n return Promise.resolve(ok(script));\n }\n\n const scripts = discoverScripts(patterns);\n return promptForScript(scripts);\n}\n","import { safeLoadLaufConfigWithMeta } from '../lib/config.ts';\nimport { defineHandler } from '../lib/handler.ts';\nimport { fail, ok } from '../lib/result.ts';\nimport { runScript } from '../runtime/runner.ts';\nimport { safeParseError } from '../utils/cli.ts';\nimport { resolveScript } from '../utils/resolve-script.ts';\n\n/**\n * Handler for the `lauf help [script]` CLI command.\n *\n * Loads config, resolves the target script (prompting if omitted),\n * then spawns the executor with LAUF_HELP=1 to display script help.\n */\nexport default defineHandler(async (ctx: { parameters: { script?: string } }) => {\n const [configError, loaded] = await safeLoadLaufConfigWithMeta(process.cwd());\n if (configError) {\n return fail({ message: `Failed to load lauf config: ${safeParseError(configError)}` });\n }\n\n const [scriptError, script] = await resolveScript(ctx.parameters.script, loaded.config.scripts);\n if (scriptError) {\n return fail(scriptError);\n }\n\n const result = await runScript(script, {}, { help: true, configDir: loaded.configDir });\n\n if (result.exitCode === 0) {\n return ok();\n }\n\n return fail({\n message: `Help failed for ${script.name}`,\n exitCode: result.exitCode,\n });\n});\n","/**\n * Template for `lauf.config.ts` — written by `lauf init`.\n */\n// oxlint-disable-next-line functional/functional-parameters\nexport function configTemplate(): string {\n return `import { defineConfig } from 'laufen'\n\nexport default defineConfig({\n scripts: ['scripts/*.ts'],\n})\n`;\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport * as p from '@clack/prompts';\nimport { attempt } from 'es-toolkit';\nimport pc from 'picocolors';\n\nimport { findConfigFile } from '../lib/config-discovery.ts';\nimport { defineHandler } from '../lib/handler.ts';\nimport { fail, ok } from '../lib/result.ts';\nimport { configTemplate } from '../templates/config.ts';\n\nconst MANIFEST_FILE = 'lauf.config.ts';\n\n/**\n * Handler for the `lauf init` CLI command.\n *\n * Initializes lauf in the current directory by writing a config file.\n * Checks for an existing reachable config via upward search first.\n */\nexport default defineHandler(() => {\n const cwd = process.cwd();\n\n // Check if there's already a reachable config\n const existing = findConfigFile(cwd);\n if (existing) {\n return fail({\n message: `Already initialized: config found at ${pc.dim(existing.configFile)}`,\n });\n }\n\n const filePath = path.join(cwd, MANIFEST_FILE);\n\n const [writeError] = attempt(() =>\n fs.writeFileSync(filePath, configTemplate(), { encoding: 'utf-8', flag: 'wx' }),\n );\n if (writeError) {\n if (writeError instanceof Error) {\n const nodeError = writeError as NodeJS.ErrnoException;\n if (nodeError.code === 'EEXIST') {\n return fail({ message: `Already initialized: ${MANIFEST_FILE} already exists` });\n }\n }\n return fail({ message: `Failed to write ${MANIFEST_FILE}: ${writeError}` });\n }\n\n p.log.success(`Initialized lauf at ${pc.cyan(MANIFEST_FILE)}`);\n\n return ok();\n});\n","import { groupBy } from 'es-toolkit';\nimport pc from 'picocolors';\n\nimport type { DiscoveredScript } from '../lib/types.ts';\n\n/**\n * Extract the script stem from a qualified name.\n * e.g. \"@scope/pkg/my-script\" → \"my-script\"\n */\nfunction scriptStem(qualifiedName: string): string {\n const lastSlash = qualifiedName.lastIndexOf('/');\n /* v8 ignore start -- discoverScripts always produces \"pkg/stem\" qualified names */\n if (lastSlash === -1) {\n return qualifiedName;\n }\n /* v8 ignore stop */\n return qualifiedName.slice(lastSlash + 1);\n}\n\nfunction treeConnector(isLast: boolean): string {\n if (isLast) {\n return '└── ';\n }\n return '├── ';\n}\n\nfunction treeChildPrefix(isLast: boolean): string {\n if (isLast) {\n return ' ';\n }\n return '│ ';\n}\n\nfunction formatTreeScriptLine(\n prefix: string,\n connector: string,\n stem: string,\n description: string,\n padWidth: number,\n): string {\n const name = pc.cyan(stem.padEnd(padWidth));\n if (description) {\n return `${prefix}${connector}${name}${pc.dim(description)}`;\n }\n return `${prefix}${connector}${name}`;\n}\n\n/**\n * Build a directory-tree-style string from grouped scripts.\n *\n * ```\n * ├── @apps/api\n * │ ├── build Build the project\n * │ └── test Run tests\n * └── @libs/core\n * └── generate Generate types\n * ```\n */\nexport function buildScriptTree(\n scripts: readonly DiscoveredScript[],\n descriptions: Record<string, string>,\n): string {\n const grouped = groupBy(scripts, (s) => s.packageName);\n const entries = Object.entries(grouped);\n const maxStemLen = Math.max(...scripts.map((s) => scriptStem(s.name).length));\n const padWidth = maxStemLen + 2;\n\n return entries\n .map(([packageName, pkgScripts], pkgIdx) => {\n const isLastPkg = pkgIdx === entries.length - 1;\n const pkgBranch = treeConnector(isLastPkg);\n const childPfx = treeChildPrefix(isLastPkg);\n\n const header = `${pkgBranch}${pc.bold(packageName)}`;\n const scriptLines = pkgScripts\n .map((script, scriptIdx) => {\n const isLastScript = scriptIdx === pkgScripts.length - 1;\n const scriptBranch = treeConnector(isLastScript);\n const stem = scriptStem(script.name);\n const desc = descriptions[script.path] || '';\n return formatTreeScriptLine(childPfx, scriptBranch, stem, desc, padWidth);\n })\n .join('\\n');\n\n return `${header}\\n${scriptLines}`;\n })\n .join('\\n');\n}\n","/* eslint-disable import/max-dependencies */\nimport { execFile } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { promisify } from 'node:util';\n\nimport * as p from '@clack/prompts';\nimport { attempt, attemptAsync, uniqBy } from 'es-toolkit';\nimport pc from 'picocolors';\nimport { z } from 'zod';\n\nimport type { LoadedConfig } from '../lib/config.ts';\nimport { loadAllLaufConfigs, safeLoadLaufConfigWithMeta } from '../lib/config.ts';\nimport { discoverScripts } from '../lib/discovery.ts';\nimport { defineHandler } from '../lib/handler.ts';\nimport { LAUF_ROOT, getWorkspaceRoot, resolveTsx } from '../lib/paths.ts';\nimport { fail, ok } from '../lib/result.ts';\nimport type { DiscoveredScript } from '../lib/types.ts';\nimport { safeParseError } from '../utils/cli.ts';\nimport { safeParseJSON } from '../utils/json.ts';\nimport { buildScriptTree } from '../utils/tree.ts';\n\nconst execFileAsync = promisify(execFile);\nconst METADATA_DIST_PATH = path.join(LAUF_ROOT, 'dist', 'runtime', 'metadata.mjs');\nconst METADATA_SRC_PATH = path.join(LAUF_ROOT, 'src', 'runtime', 'metadata.ts');\n\nconst listParams = z.object({\n flags: z.object({ all: z.boolean().optional() }),\n});\n\n/**\n * Handler for the `lauf list` CLI command.\n *\n * Discovers all lauf scripts across the monorepo and prints\n * a hierarchical tree grouped by package, with descriptions\n * extracted by importing each script via tsx.\n */\nexport default defineHandler({\n parameters: listParams,\n handler: (ctx) => {\n if (ctx.flags.all) {\n return listAllScripts();\n }\n return listScopedScripts();\n },\n});\n\n/**\n * List scripts from the closest config (default behavior).\n */\nasync function listScopedScripts() {\n const [configError, loaded] = await safeLoadLaufConfigWithMeta(process.cwd());\n if (configError) {\n return fail({ message: `Failed to load lauf config: ${safeParseError(configError)}` });\n }\n\n const scripts = discoverScripts(loaded.config.scripts);\n return displayScripts(scripts);\n}\n\n/**\n * List scripts from all configs within the search boundary (--all flag).\n */\nasync function listAllScripts() {\n const configs = await loadAllLaufConfigs(process.cwd());\n\n const allScripts = configs.flatMap((loaded: LoadedConfig) =>\n discoverScripts(loaded.config.scripts, { scopeDir: loaded.configDir }),\n );\n\n // Deduplicate by script path (closest config wins since configs are sorted shallowest-first)\n const unique = uniqBy(allScripts, (s) => s.path);\n\n return displayScripts(unique);\n}\n\n/**\n * Shared display logic for discovered scripts.\n *\n * Renders a directory-tree-style hierarchy grouped by package,\n * including scripts from all packages (root and workspace members).\n */\nasync function displayScripts(scripts: readonly DiscoveredScript[]) {\n if (scripts.length === 0) {\n p.log.warn('No scripts found.');\n p.log.message(pc.dim('Create one with: lauf create <name>'));\n return ok();\n }\n\n const descriptions = await loadDescriptions(scripts);\n const tree = buildScriptTree(scripts, descriptions);\n\n p.note(tree, `Found ${scripts.length} script(s)`);\n\n return ok();\n}\n\n/**\n * Resolve the metadata extractor path, preferring the built dist version\n * and falling back to the source .ts path if dist is unavailable.\n *\n * Returns undefined if neither exists.\n */\nfunction resolveMetadataPath(): string | undefined {\n const [distErr, distExists] = attempt(() => fs.existsSync(METADATA_DIST_PATH));\n if (!distErr && distExists) {\n return METADATA_DIST_PATH;\n }\n\n const [srcErr, srcExists] = attempt(() => fs.existsSync(METADATA_SRC_PATH));\n if (!srcErr && srcExists) {\n return METADATA_SRC_PATH;\n }\n\n return undefined;\n}\n\n/**\n * Build the NODE_PATH array for the metadata extractor subprocess.\n *\n * Includes the lauf and workspace node_modules directories,\n * plus the existing NODE_PATH if set.\n */\nfunction buildNodePaths(workspaceRoot: string): readonly string[] {\n const base = [path.join(LAUF_ROOT, 'node_modules'), path.join(workspaceRoot, 'node_modules')];\n const existing = process.env.NODE_PATH;\n if (existing) {\n return [...base, existing];\n }\n /* v8 ignore next 2 -- trivial else branch; NODE_PATH is almost always set in test env */\n return base;\n}\n\n/**\n * Build a minimal environment for the metadata extractor subprocess.\n *\n * Only exposes PATH, HOME, TERM, NODE_PATH, and any LAUF_* variables\n * to avoid leaking secrets from the parent environment.\n */\nfunction buildMinimalEnv(): Record<string, string | undefined> {\n const laufEntries = Object.entries(process.env).filter(([key]) => key.startsWith('LAUF_'));\n const base: Record<string, string | undefined> = {\n PATH: process.env.PATH,\n HOME: process.env.HOME,\n TERM: process.env.TERM,\n NODE_PATH: process.env.NODE_PATH,\n };\n return Object.assign(base, Object.fromEntries(laufEntries));\n}\n\n/**\n * Spawn the metadata extractor via tsx to import all scripts\n * and return their descriptions as a path → description record.\n *\n * Fails gracefully — returns an empty record on any error so\n * the list command still works, just without descriptions.\n */\n// oxlint-disable-next-line max-lines-per-function\nasync function loadDescriptions(\n scripts: readonly DiscoveredScript[],\n): Promise<Record<string, string>> {\n /* v8 ignore next 3 -- defensive guard; handler already returns early when scripts is empty */\n if (scripts.length === 0) {\n return {};\n }\n\n const metadataPath = resolveMetadataPath();\n if (!metadataPath) {\n p.log.warn(\n 'Script descriptions unavailable: metadata extractor not found. Run `pnpm build` to generate it.',\n );\n return {};\n }\n\n const [tsxError, tsxPath] = resolveTsx();\n if (tsxError) {\n p.log.warn(`Script descriptions unavailable: ${tsxError.message}`);\n return {};\n }\n\n // TypeScript doesn't narrow tuple index [1] after checking [0]; safe cast after guard above\n const resolvedTsxPath = tsxPath as string;\n\n const workspaceRoot = getWorkspaceRoot();\n const nodePaths = buildNodePaths(workspaceRoot);\n\n const [error, result] = await attemptAsync(() =>\n execFileAsync(resolvedTsxPath, [metadataPath], {\n env: {\n ...buildMinimalEnv(),\n NODE_PATH: nodePaths.join(path.delimiter),\n LAUF_SCRIPT_PATHS: JSON.stringify(scripts.map((s) => s.path)),\n LAUF_WORKSPACE_ROOT: workspaceRoot,\n },\n timeout: 15_000,\n }),\n );\n\n // es-toolkit's attemptAsync types require the null check for TS narrowing\n if (error || result === null) {\n p.log.warn('Description extraction timed out or failed. Listing scripts without descriptions.');\n return {};\n }\n\n const [parseError, parsed] = safeParseJSON(String(result.stdout));\n if (parseError || parsed === null || typeof parsed !== 'object') {\n p.log.warn('Description extraction failed: could not parse metadata output.');\n return {};\n }\n\n const descriptions = parsed as Record<string, string>;\n\n if (Object.keys(descriptions).length === 0 && scripts.length > 0) {\n p.log.warn('Description extraction returned no results. Descriptions may be unavailable.');\n }\n\n return descriptions;\n}\n","import * as p from '@clack/prompts';\n\nconst BLOCKED_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\n/**\n * Check whether a key is safe (not a prototype pollution vector).\n */\nfunction isSafeKey(key: string): boolean {\n return !BLOCKED_KEYS.has(key);\n}\n\n// oxlint-disable-next-line security/detect-unsafe-regex -- bounded decimal pattern, no backtracking risk\nconst STRICT_NUMBER_RE = /^-?\\d+(\\.\\d+)?$/;\n\n/**\n * Coerce a string value to a primitive type.\n *\n * Attempts boolean and strict decimal number coercion, falls back to string.\n * Rejects hex strings (0x...) and scientific notation (1e10) to avoid\n * unexpected conversions.\n *\n * @param value - Raw string value from CLI\n * @returns Coerced boolean, number, or original string\n * @private\n */\nfunction coerce(value: string): unknown {\n if (value === 'true') {\n return true;\n }\n if (value === 'false') {\n return false;\n }\n\n if (STRICT_NUMBER_RE.test(value)) {\n return Number(value);\n }\n\n return value;\n}\n\n/**\n * Parse raw CLI argv into a key-value record.\n *\n * Supports `--key=value`, `--key value`, and `--flag` (boolean true).\n * Uses recursion instead of stateful reduction. Warns when positional\n * arguments are found and filters out prototype pollution keys.\n *\n * @param argv - Raw argument strings\n * @returns Parsed key-value record\n * @private\n */\n// oxlint-disable-next-line max-lines-per-function\nexport function parseRawArgs(argv: readonly string[]): Record<string, unknown> {\n const parse = (\n remaining: readonly string[],\n acc: Record<string, unknown>,\n positionals: readonly string[],\n ): { readonly result: Record<string, unknown>; readonly positionals: readonly string[] } => {\n const arg = remaining[0];\n if (arg === undefined) {\n return { result: acc, positionals };\n }\n\n const rest = remaining.slice(1);\n\n if (!arg.startsWith('--')) {\n return parse(rest, acc, [...positionals, arg]);\n }\n\n const withoutDashes = arg.slice(2);\n const eqIdx = withoutDashes.indexOf('=');\n\n if (eqIdx !== -1) {\n // --key=value\n const key = withoutDashes.slice(0, eqIdx);\n const value = withoutDashes.slice(eqIdx + 1);\n if (isSafeKey(key)) {\n return parse(rest, { ...acc, [key]: coerce(value) }, positionals);\n }\n return parse(rest, acc, positionals);\n }\n\n const next = rest[0];\n if (next !== undefined && !next.startsWith('--')) {\n // --key value\n if (isSafeKey(withoutDashes)) {\n return parse(rest.slice(1), { ...acc, [withoutDashes]: coerce(next) }, positionals);\n }\n return parse(rest.slice(1), acc, positionals);\n }\n\n // --flag (boolean)\n if (isSafeKey(withoutDashes)) {\n return parse(rest, { ...acc, [withoutDashes]: true }, positionals);\n }\n return parse(rest, acc, positionals);\n };\n\n const initial: Record<string, unknown> = Object.create(null) as Record<string, unknown>;\n const { result, positionals } = parse(argv, initial, []);\n\n if (positionals.length > 0) {\n p.log.warn(\n `Positional arguments were ignored: ${positionals.join(', ')}. Use --key value syntax instead.`,\n );\n }\n\n return result;\n}\n\n/**\n * Extract raw argv entries after the given script name.\n *\n * Only searches after index 2 (skipping the node binary and script path)\n * to avoid matching wrong argv entries.\n *\n * @param name - Script name to search for in `process.argv`\n * @returns Argv entries following the script name, or empty array if not found\n * @private\n */\nexport function sliceArgvAfter(name: string): readonly string[] {\n // Slice from index 3 to skip: [0] node binary, [1] script path, [2] Clerc subcommand (\"run\")\n const scriptArgs = process.argv.slice(3);\n const idx = scriptArgs.findIndex((arg) => arg === name);\n if (idx === -1) {\n return [];\n }\n return scriptArgs.slice(idx + 1);\n}\n","// oxlint-disable import/max-dependencies\nimport * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport { z } from 'zod';\n\nimport { safeLoadLaufConfigWithMeta } from '../lib/config.ts';\nimport { defineHandler } from '../lib/handler.ts';\nimport { fail, ok } from '../lib/result.ts';\nimport type { DiscoveredScript, RunResult } from '../lib/types.ts';\nimport { runScript } from '../runtime/runner.ts';\nimport { parseRawArgs, sliceArgvAfter } from '../utils/argv.ts';\nimport { safeParseError } from '../utils/cli.ts';\nimport { resolveScript } from '../utils/resolve-script.ts';\n\nconst runParams = z.object({\n parameters: z.object({ script: z.string().min(1).optional() }),\n});\n\n/**\n * Handler for the `lauf run [script]` CLI command.\n *\n * Resolves the script by qualified name (or prompts for selection),\n * parses any trailing CLI flags into arguments, and spawns the script executor.\n */\nexport default defineHandler({\n parameters: runParams,\n handler: async (ctx) => {\n const [configError, loaded] = await safeLoadLaufConfigWithMeta(process.cwd());\n if (configError) {\n return fail({ message: `Failed to load lauf config: ${safeParseError(configError)}` });\n }\n\n const [scriptError, script] = await resolveScript(ctx.parameters.script, loaded.config.scripts);\n if (scriptError) {\n return fail(scriptError);\n }\n\n const rawArgv = resolveRawArgv(ctx.parameters.script);\n const isHelp = rawArgv.includes('--help') || rawArgv.includes('-h');\n\n if (isHelp) {\n const helpResult = await runScript(script, {}, { help: true, configDir: loaded.configDir });\n /* v8 ignore start -- help via runScript delegates to executor which handles its own errors */\n if (helpResult.exitCode === 0) {\n return ok();\n }\n return fail({ message: `Help failed for ${script.name}`, exitCode: helpResult.exitCode });\n }\n /* v8 ignore stop */\n\n const args = parseRawArgs(rawArgv);\n const result = await executeScript(script, args, loaded.configDir);\n\n if (result.exitCode === 0) {\n return ok();\n }\n\n return fail({\n message: `${pc.cyan(script.name)} exited with code ${result.exitCode}`,\n exitCode: result.exitCode,\n });\n },\n});\n\n/**\n * Extract raw argv entries following the given script name.\n * Returns an empty array when no script name was provided on the CLI.\n */\nfunction resolveRawArgv(scriptName: string | undefined): readonly string[] {\n if (scriptName) {\n return sliceArgvAfter(scriptName);\n }\n return [];\n}\n\n/**\n * Run a script, logging start and result messages.\n *\n * The handler no longer wraps execution in a spinner because\n * the child process may prompt for missing arguments, and the\n * spinner animation conflicts with interactive prompts.\n * Scripts can use `ctx.spinner` for progress indication during\n * their own execution after all prompts are resolved.\n */\nasync function executeScript(\n script: DiscoveredScript,\n args: Record<string, unknown>,\n configDir: string,\n): Promise<RunResult> {\n const label = pc.cyan(script.name);\n\n p.log.step(`Running ${label}`);\n const result = await runScript(script, args, { configDir });\n if (result.exitCode === 0) {\n p.log.success(`${label} completed successfully`);\n }\n return result;\n}\n","#!/usr/bin/env node\n// oxlint-disable import/max-dependencies\n\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport * as p from '@clack/prompts';\nimport { Clerc, helpPlugin, versionPlugin } from 'clerc';\nimport pc from 'picocolors';\n\nimport handleCreate from './handlers/create.ts';\nimport handleInfo from './handlers/info.ts';\nimport handleInit from './handlers/init.ts';\nimport handleList from './handlers/list.ts';\nimport handleRun from './handlers/run.ts';\nimport { errorHint, readPackageJSON, safeParseError } from './utils/cli.ts';\n\nconst pkgDir = path.dirname(path.dirname(fileURLToPath(import.meta.url)));\nconst [pkgError, pkg] = readPackageJSON(pkgDir);\n\nif (pkgError || pkg === null || !pkg.version) {\n p.log.error(\n 'Fatal error, unable to execute lauf, please log an issue on github: https://github.com/zrosenbauer/lauf/issues',\n );\n process.exit(1);\n}\n\nClerc.create()\n .name('lauf')\n .scriptName('lauf')\n .description('Typed script runner for monorepos')\n .version(pkg.version)\n .use(helpPlugin())\n .use(versionPlugin())\n .errorHandler((err: unknown) => {\n const message = safeParseError(err);\n p.log.error(message);\n const hint = errorHint(err);\n if (hint) {\n p.log.message(pc.dim(hint));\n }\n process.exit(1);\n })\n .command('init', 'Initialize lauf in the current package')\n .on('init', handleInit)\n .command('list', 'List all available scripts', {\n flags: {\n all: {\n type: Boolean,\n description: 'Discover scripts from all nested configs',\n alias: 'a',\n },\n },\n })\n .on('list', handleList)\n .command('info', 'Show info for a script', {\n parameters: ['[script]'],\n })\n .on('info', handleInfo)\n .command('run', 'Run a script', {\n parameters: ['[script]'],\n })\n .on('run', handleRun)\n .command('create', 'Create a new script', {\n parameters: ['[name]'],\n flags: {\n dir: {\n type: String,\n description: 'Target directory (relative to monorepo root)',\n },\n },\n })\n .on('create', handleCreate)\n .parse();\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["path","path","path","path","path","path","path","handleInit","handleList","handleInfo","handleRun","handleCreate"],"sources":["../src/lib/handler.ts","../src/lib/result.ts","../src/templates/script.ts","../src/utils/fs.ts","../src/utils/prompt.ts","../src/handlers/create.ts","../src/lib/workspace.ts","../src/lib/paths.ts","../src/runtime/runner.ts","../src/lib/discovery.ts","../src/utils/resolve-script.ts","../src/handlers/info.ts","../src/templates/config.ts","../src/handlers/init.ts","../src/utils/tree.ts","../src/handlers/list.ts","../src/utils/argv.ts","../src/handlers/run.ts","../src/index.ts"],"sourcesContent":["import * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport type { z } from 'zod';\n\nimport { formatArgErrors } from '../utils/cli.ts';\nimport type { HandlerResult } from './result.ts';\n\n/**\n * Object-form handler configuration that pairs a Zod schema for the\n * Clerc context with the handler function. The context is `safeParse`d\n * at runtime; validation failures exit cleanly with formatted errors.\n */\nexport interface HandlerConfig<S extends z.ZodType> {\n readonly parameters: S;\n readonly handler: (ctx: z.infer<S>) => HandlerResult | Promise<HandlerResult>;\n}\n\n/**\n * Define a Clerc-compatible command handler from a function that returns\n * `HandlerResult`. Centralizes error display and `process.exit` so\n * individual handlers never need to deal with side-effectful exits.\n */\nexport function defineHandler<Ctx>(\n fn: (ctx: Ctx) => HandlerResult | Promise<HandlerResult>,\n): (ctx: Ctx) => Promise<void>;\n\n/**\n * Define a Clerc-compatible command handler from a {@link HandlerConfig}\n * object. The Clerc context is validated against the provided Zod schema\n * before the handler is called.\n */\nexport function defineHandler<S extends z.ZodType>(\n config: HandlerConfig<S>,\n): (ctx: unknown) => Promise<void>;\n\nexport function defineHandler(\n fnOrConfig: ((ctx: never) => HandlerResult | Promise<HandlerResult>) | HandlerConfig<z.ZodType>,\n): (ctx: never) => Promise<void> {\n if (isHandlerConfig(fnOrConfig)) {\n return async (ctx: unknown): Promise<void> => {\n const parsed = fnOrConfig.parameters.safeParse(ctx);\n\n if (!parsed.success) {\n p.log.error(formatArgErrors(parsed.error.issues));\n process.exit(1);\n return;\n }\n\n await handleResult(fnOrConfig.handler(parsed.data));\n };\n }\n\n const fn = fnOrConfig;\n return async (ctx: never): Promise<void> => {\n await handleResult(fn(ctx));\n };\n}\n\n/**\n * Discriminate a plain handler function from a {@link HandlerConfig} object.\n */\nfunction isHandlerConfig(value: unknown): value is HandlerConfig<z.ZodType> {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n if (!('parameters' in value) || !('handler' in value)) {\n return false;\n }\n const obj = value as Record<string, unknown>;\n if (typeof obj.handler !== 'function') {\n return false;\n }\n return (\n typeof obj.parameters === 'object' && obj.parameters !== null && 'safeParse' in obj.parameters\n );\n}\n\n/**\n * Shared exit logic for resolved handler results.\n * Displays errors via `@clack/prompts` and calls `process.exit`.\n */\nasync function handleResult(result: HandlerResult | Promise<HandlerResult>): Promise<void> {\n const [error] = await Promise.resolve(result);\n\n if (error === null) {\n return;\n }\n\n if (error.exitCode === 0) {\n if (error.message) {\n p.log.info(error.message);\n }\n process.exit(0);\n return;\n }\n\n p.log.error(error.message);\n\n if (error.hint) {\n p.log.message(pc.dim(error.hint));\n }\n\n process.exit(error.exitCode ?? 1);\n}\n","/**\n * Structured error returned by handlers instead of throwing or calling process.exit.\n */\nexport interface HandlerError {\n readonly message: string;\n readonly hint?: string;\n readonly exitCode?: number;\n}\n\n/**\n * Generic discriminated tuple: success or failure.\n *\n * @typeParam T - The success value type\n * @typeParam E - The error type (defaults to `Error`)\n */\nexport type Result<T, E = Error> = readonly [E, null] | readonly [null, T];\n\n/**\n * Discriminated tuple: success or failure for handler operations.\n */\nexport type HandlerResult<T = void> = Result<T, HandlerError>;\n\n/**\n * Construct a success result.\n */\nexport function ok(): HandlerResult<void>;\nexport function ok<T>(value: T): HandlerResult<T>;\nexport function ok<T>(value?: T): HandlerResult<T> {\n return [null, value as T] as const;\n}\n\n/**\n * Construct a failure result.\n */\nexport function fail(error: HandlerError): HandlerResult<never> {\n return [error, null] as const;\n}\n","/**\n * Strip characters not in `[a-zA-Z0-9_-]` from a script name\n * to prevent template injection.\n *\n * @param name - Raw name input\n * @returns Sanitized name safe for template interpolation\n */\nexport function sanitizeName(name: string): string {\n return name.replaceAll(/[^a-zA-Z0-9_-]/g, '');\n}\n\n/**\n * Escape characters that could break a single-quoted string literal\n * (backslash and single quote).\n *\n * @param value - String to escape\n * @returns Escaped string safe for single-quoted template contexts\n */\nfunction escapeStringLiteral(value: string): string {\n return value.replaceAll('\\\\', '\\\\\\\\').replaceAll(\"'\", \"\\\\'\");\n}\n\n/**\n * Escape characters that could break a template literal (backtick and `${`).\n *\n * @param value - String to escape\n * @returns Escaped string safe for backtick template contexts\n */\nfunction escapeTemplateLiteral(value: string): string {\n return value.replaceAll('\\\\', '\\\\\\\\').replaceAll('`', '\\\\`').replaceAll('${', '\\\\${');\n}\n\n/**\n * Template for a new lauf script — written by `lauf create <name>`.\n *\n * @param name - Script name used in the template description\n * @returns Template string for the script file\n */\nexport function scriptTemplate(name: string): string {\n const safeName = sanitizeName(name);\n const singleQuoteSafe = escapeStringLiteral(safeName);\n const templateSafe = escapeTemplateLiteral(safeName);\n\n return `import { lauf, z } from 'laufen'\n\nexport default lauf({\n description: '${singleQuoteSafe} script',\n args: {\n verbose: z.boolean().default(false).describe('Enable verbose logging'),\n },\n async run(ctx) {\n if (ctx.args.verbose) {\n ctx.logger.info(\\`Running ${templateSafe} in \\${ctx.packageDir}\\`)\n }\n\n // TODO: implement ${safeName}\n ctx.logger.success('Hello from ${singleQuoteSafe}!')\n },\n})\n`;\n}\n","import * as fs from 'node:fs';\n\nimport { attempt } from 'es-toolkit';\n\n/**\n * Safely create directories recursively. Returns `[error, null]` or `[null, string | undefined]`.\n */\nexport function safeMkdirSync(dirPath: string): [unknown, null] | [null, string | undefined] {\n return attempt(() => fs.mkdirSync(dirPath, { recursive: true }));\n}\n","import * as p from '@clack/prompts';\n\nimport { fail, ok } from '../lib/result.ts';\nimport type { HandlerResult } from '../lib/result.ts';\nimport type { DiscoveredScript } from '../lib/types.ts';\n\n/**\n * Present an interactive select menu of discovered scripts.\n *\n * @param scripts - Available scripts to choose from\n * @returns The selected script, or a fail result on cancel/empty\n */\nexport async function promptForScript(\n scripts: readonly DiscoveredScript[],\n): Promise<HandlerResult<DiscoveredScript>> {\n if (scripts.length === 0) {\n return fail({\n message: 'No scripts found',\n hint: 'Run `lauf init` to get started.',\n });\n }\n\n const selected = await p.select({\n message: 'Select a script',\n options: scripts.map((s) => ({ value: s, label: s.name })),\n });\n\n if (p.isCancel(selected)) {\n p.cancel('Cancelled');\n return fail({ message: 'Cancelled', exitCode: 0 });\n }\n\n return ok(selected);\n}\n\n/**\n * Present an interactive text input prompt.\n *\n * @param message - Prompt message to display\n * @param placeholder - Optional placeholder text\n * @returns The entered string, or a fail result on cancel\n */\nexport async function promptForText(\n message: string,\n placeholder?: string,\n): Promise<HandlerResult<string>> {\n const value = await p.text({\n message,\n placeholder,\n });\n\n if (p.isCancel(value)) {\n p.cancel('Cancelled');\n return fail({ message: 'Cancelled', exitCode: 0 });\n }\n\n return ok(value);\n}\n","// oxlint-disable import/max-dependencies\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport * as p from '@clack/prompts';\nimport { attempt } from 'es-toolkit';\nimport pc from 'picocolors';\nimport { z } from 'zod';\n\nimport { safeLoadLaufConfigWithMeta } from '../lib/config.ts';\nimport { defineHandler } from '../lib/handler.ts';\nimport { fail, ok } from '../lib/result.ts';\nimport type { HandlerResult } from '../lib/result.ts';\nimport { scriptTemplate } from '../templates/script.ts';\nimport { readPackageJSON, safeParseError } from '../utils/cli.ts';\nimport { safeMkdirSync } from '../utils/fs.ts';\nimport { promptForText } from '../utils/prompt.ts';\n\nconst createParams = z.object({\n parameters: z.object({ name: z.string().min(1).optional() }),\n flags: z.object({ dir: z.string().optional() }),\n});\n\n/**\n * Handler for the `lauf create [name]` CLI command.\n *\n * Scaffolds a new lauf script file in the target directory\n * with a starter template including typed arguments.\n */\nexport default defineHandler({\n parameters: createParams,\n // oxlint-disable-next-line max-lines-per-function\n handler: async (ctx) => {\n const [nameError, name] = await resolveName(ctx.parameters.name);\n if (nameError) {\n return fail(nameError);\n }\n\n const { dir } = ctx.flags;\n\n const [configError, loaded] = await safeLoadLaufConfigWithMeta(process.cwd());\n if (configError) {\n return fail({ message: `Failed to load lauf config: ${safeParseError(configError)}` });\n }\n\n const targetDir = resolveTargetDir(dir, loaded.config.scripts, loaded.configDir);\n const normalizedTarget = path.normalize(targetDir);\n\n if (\n normalizedTarget !== loaded.configDir &&\n !normalizedTarget.startsWith(`${loaded.configDir}${path.sep}`)\n ) {\n return fail({\n message: `Target directory escapes config root: ${normalizedTarget}`,\n });\n }\n\n const stem = name.replace(/\\.lauf\\.ts$|\\.ts$/, '');\n const fileName = `${stem}.lauf.ts`;\n const filePath = path.join(targetDir, fileName);\n\n const resolvedFilePath = path.resolve(filePath);\n const normalizedTargetForFile = path.resolve(targetDir);\n /* v8 ignore next 5 -- defensive guard: unreachable with current name sanitisation */\n if (!resolvedFilePath.startsWith(`${normalizedTargetForFile}${path.sep}`)) {\n return fail({\n message: `File path escapes target directory: ${resolvedFilePath}`,\n });\n }\n\n const [mkdirError] = safeMkdirSync(targetDir);\n if (mkdirError) {\n return fail({ message: `Failed to create directory ${targetDir}: ${mkdirError}` });\n }\n\n const [writeError] = safeWriteFileExclusive(filePath, scriptTemplate(stem));\n if (writeError) {\n if (writeError instanceof Error) {\n const nodeError = writeError as NodeJS.ErrnoException;\n if (nodeError.code === 'EEXIST') {\n return fail({ message: `File already exists: ${filePath}` });\n }\n }\n return fail({ message: `Failed to write ${filePath}: ${writeError}` });\n }\n\n const [, pkg] = readPackageJSON(loaded.configDir);\n /* v8 ignore next -- fallback branch: readPackageJSON always returns a name in practice */\n const packageName = (pkg && pkg.name) || path.basename(loaded.configDir);\n const qualifiedName = `${packageName}/${stem}`;\n\n const relative = path.relative(loaded.configDir, filePath);\n p.log.success(`Created ${relative}`);\n p.log.message(pc.dim(`Run it with: lauf run ${qualifiedName}`));\n\n return ok();\n },\n});\n\n/**\n * Resolve the script name — from the CLI parameter or via interactive prompt.\n */\nfunction resolveName(name: string | undefined): Promise<HandlerResult<string>> {\n if (name) {\n return Promise.resolve(ok(name));\n }\n return promptForText('Enter a name for the new script', 'my-script');\n}\n\n/**\n * Resolve the target directory for a new script.\n *\n * When no --dir flag is provided, resolves relative to process.cwd().\n * When --dir is provided with an absolute path, uses it directly.\n * When --dir is provided with a relative path, resolves from the config directory.\n *\n * @param dir - Optional directory override\n * @param patterns - Script glob patterns from config\n * @param configDir - Directory containing the active config\n * @returns Absolute path to the target scripts directory\n * @private\n */\nfunction resolveTargetDir(dir: string | undefined, patterns: string[], configDir: string): string {\n if (dir) {\n if (path.isAbsolute(dir)) {\n return path.normalize(dir);\n }\n return path.resolve(configDir, dir);\n }\n const firstPattern = patterns[0];\n if (firstPattern) {\n return path.resolve(process.cwd(), path.dirname(firstPattern));\n }\n return path.resolve(process.cwd(), 'scripts');\n}\n\n/**\n * Atomically write a file, failing if it already exists (wx flag).\n * Prevents TOCTOU race between existence check and write.\n */\nfunction safeWriteFileExclusive(filePath: string, content: string): [unknown, null] | [null, void] {\n return attempt(() => fs.writeFileSync(filePath, content, { encoding: 'utf-8', flag: 'wx' }));\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { attempt } from 'es-toolkit';\nimport * as yaml from 'yaml';\n\nimport { safeParseJSON } from '../utils/json.ts';\n\nconst MAX_PARENT_ITERATIONS = 200;\n\ntype WorkspaceManager = 'pnpm' | 'npm' | 'yarn' | 'bun' | 'lerna' | 'single';\n\ninterface WorkspaceInfo {\n readonly manager: WorkspaceManager;\n readonly root: string;\n readonly globs: readonly string[];\n}\n\ninterface WorkspaceDetector {\n readonly manager: WorkspaceManager;\n readonly marker: string;\n readonly extractGlobs: (content: string) => readonly string[] | undefined;\n}\n\n/**\n * Safe YAML parse wrapper matching the `safeParseJSON` convention.\n *\n * @param content - Raw YAML string\n * @returns Tuple of [error, result]\n */\nfunction safeParseYaml(content: string): [Error, null] | [null, unknown] {\n return attempt(() => yaml.parse(content) as unknown);\n}\n\n/**\n * Validate that every element in an array is a string.\n *\n * @param arr - Array to validate\n * @returns `true` if all elements are strings\n */\nfunction isStringArray(arr: readonly unknown[]): arr is readonly string[] {\n return arr.every((x) => typeof x === 'string');\n}\n\n/**\n * Extract workspace globs from `pnpm-workspace.yaml`.\n *\n * @param content - Raw YAML content\n * @returns Array of glob patterns, or `undefined` if parsing fails\n */\nfunction extractPnpmGlobs(content: string): readonly string[] | undefined {\n const [err, config] = safeParseYaml(content);\n if (err) {\n return undefined;\n }\n if (\n config &&\n typeof config === 'object' &&\n 'packages' in config &&\n Array.isArray(config.packages) &&\n isStringArray(config.packages)\n ) {\n return config.packages;\n }\n return undefined;\n}\n\n/**\n * Extract workspace globs from `package.json`.\n *\n * Handles both `\"workspaces\": [...]` and `\"workspaces\": { \"packages\": [...] }` shapes.\n *\n * @param content - Raw JSON content\n * @returns Array of glob patterns, or `undefined` if no workspaces field\n */\nfunction extractPackageJsonGlobs(content: string): readonly string[] | undefined {\n const [err, parsed] = safeParseJSON(content);\n if (err || parsed === null) {\n return undefined;\n }\n\n /* v8 ignore next 3 -- defensive: safeParseJSON only returns objects/arrays for valid JSON */\n if (typeof parsed !== 'object') {\n return undefined;\n }\n\n const pkg = parsed as Record<string, unknown>;\n const workspaces = pkg.workspaces;\n\n if (Array.isArray(workspaces) && isStringArray(workspaces)) {\n return workspaces;\n }\n\n if (workspaces && typeof workspaces === 'object' && 'packages' in workspaces) {\n const inner = (workspaces as Record<string, unknown>).packages;\n if (Array.isArray(inner) && isStringArray(inner)) {\n return inner;\n }\n }\n\n return undefined;\n}\n\n/**\n * Extract workspace globs from `lerna.json`.\n *\n * @param content - Raw JSON content\n * @returns Array of glob patterns, or `undefined` if parsing fails\n */\nfunction extractLernaGlobs(content: string): readonly string[] | undefined {\n const [err, parsed] = safeParseJSON(content);\n if (err || !parsed || typeof parsed !== 'object') {\n return undefined;\n }\n const config = parsed as Record<string, unknown>;\n if (Array.isArray(config.packages) && isStringArray(config.packages)) {\n return config.packages;\n }\n return undefined;\n}\n\n/**\n * Ordered list of workspace detectors, checked in priority order.\n *\n * pnpm is checked first (dedicated workspace file), then package.json\n * workspaces (npm/yarn/bun), then lerna.\n */\nconst DETECTORS: readonly WorkspaceDetector[] = [\n { manager: 'pnpm', marker: 'pnpm-workspace.yaml', extractGlobs: extractPnpmGlobs },\n { manager: 'npm', marker: 'package.json', extractGlobs: extractPackageJsonGlobs },\n { manager: 'lerna', marker: 'lerna.json', extractGlobs: extractLernaGlobs },\n];\n\n/**\n * Refine manager name for package.json-based workspaces by checking lockfiles.\n *\n * @param dir - Directory to check for lockfiles\n * @returns Refined manager name\n */\nfunction refinePackageJsonManager(dir: string): WorkspaceManager {\n if (fs.existsSync(path.join(dir, 'yarn.lock'))) {\n return 'yarn';\n }\n if (fs.existsSync(path.join(dir, 'bun.lockb')) || fs.existsSync(path.join(dir, 'bun.lock'))) {\n return 'bun';\n }\n return 'npm';\n}\n\n/**\n * Resolve the manager for a given detector match.\n *\n * @param dir - Directory where the marker was found\n * @param detector - The matched detector\n * @returns Resolved workspace manager\n */\nfunction resolveManager(dir: string, detector: WorkspaceDetector): WorkspaceManager {\n if (detector.marker === 'package.json') {\n return refinePackageJsonManager(dir);\n }\n return detector.manager;\n}\n\n/**\n * Try a single detector against a directory.\n *\n * @param dir - Directory to scan\n * @param detector - Detector to try\n * @returns WorkspaceInfo if the detector matches, or `undefined`\n */\nfunction detectFromMarker(dir: string, detector: WorkspaceDetector): WorkspaceInfo | undefined {\n const markerPath = path.join(dir, detector.marker);\n if (!fs.existsSync(markerPath)) {\n return undefined;\n }\n const content = fs.readFileSync(markerPath, 'utf-8');\n const globs = detector.extractGlobs(content);\n if (globs === undefined) {\n return undefined;\n }\n const manager = resolveManager(dir, detector);\n return { manager, root: dir, globs };\n}\n\n/**\n * Try all detectors against a single directory.\n *\n * @param dir - Directory to scan\n * @returns WorkspaceInfo if any detector matches, or `undefined`\n */\nfunction tryDetectors(dir: string): WorkspaceInfo | undefined {\n const results = DETECTORS.map((detector) => detectFromMarker(dir, detector));\n return results.find((r): r is WorkspaceInfo => r !== null && r !== undefined);\n}\n\n/**\n * Walk up from `startDir` checking each directory for workspace markers.\n *\n * @param startDir - Starting directory\n * @returns WorkspaceInfo for the detected workspace, or a `'single'` fallback\n */\nfunction resolveWorkspace(startDir: string = process.cwd()): WorkspaceInfo {\n const walk = (dir: string, prevDir: string, iterations: number): WorkspaceInfo => {\n if (dir === prevDir || iterations >= MAX_PARENT_ITERATIONS) {\n return { manager: 'single', root: startDir, globs: [] };\n }\n\n const detected = tryDetectors(dir);\n if (detected) {\n return detected;\n }\n\n return walk(path.dirname(dir), dir, iterations + 1);\n };\n\n return walk(startDir, '', 0);\n}\n\n/**\n * Module-level cache keyed by cwd. The Map reference is const;\n * only its entries are mutated (acceptable for a cache).\n */\n// oxlint-disable-next-line prefer-const -- Map is const; entries are cache mutations\nconst workspaceCache = new Map<string, WorkspaceInfo>();\n\n/**\n * Return the full workspace info (manager, root, globs).\n *\n * Computes the workspace info on first access for the given cwd\n * and caches the result.\n */\nexport function getWorkspaceInfo(cwd: string = process.cwd()): WorkspaceInfo {\n const cached = workspaceCache.get(cwd);\n if (cached !== undefined) {\n return cached;\n }\n const info = resolveWorkspace(cwd);\n // oxlint-disable-next-line immutable-data -- cache mutation\n workspaceCache.set(cwd, info);\n return info;\n}\n\n/**\n * Return just the workspace root path.\n */\nexport function getWorkspaceRoot(cwd: string = process.cwd()): string {\n return getWorkspaceInfo(cwd).root;\n}\n\n/**\n * Reset the cached workspace info. Intended for testing only.\n */\nexport function resetWorkspaceCache(): void {\n // oxlint-disable-next-line immutable-data -- cache clear for testing\n workspaceCache.clear();\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport { attempt } from 'es-toolkit';\nimport fg from 'fast-glob';\n\nimport { safeParseJSON } from '../utils/json.ts';\nimport type { Result } from './result.ts';\nimport { getWorkspaceInfo, getWorkspaceRoot } from './workspace.ts';\n\nexport { getWorkspaceRoot };\n\n/**\n * Absolute path to the lauf package directory.\n */\nexport const LAUF_ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url)));\n\n/**\n * Resolve the path to the tsx binary in lauf's own node_modules.\n * Returns an error string if tsx is not found, directing the user\n * to run their package manager's install command.\n *\n * @returns Tuple of [errorMessage, null] or [null, absolutePath]\n */\nexport function resolveTsx(): Result<string> {\n const tsxPath = path.join(LAUF_ROOT, 'node_modules/.bin/tsx');\n if (fs.existsSync(tsxPath)) {\n return [null, tsxPath];\n }\n return [\n new Error(\n `tsx binary not found at ${tsxPath}. Run your package manager's install command (e.g. \"pnpm install\") to install dependencies.`,\n ),\n null,\n ];\n}\n\ninterface PackageInfo {\n readonly name: string;\n readonly dir: string;\n}\n\n/**\n * Resolve all workspace packages from detected workspace globs.\n *\n * Includes the workspace root itself if it has a valid `package.json`.\n *\n * @returns Array of package info objects with name and absolute directory path\n */\nexport function resolveWorkspacePackages(): readonly PackageInfo[] {\n const { root, globs } = getWorkspaceInfo();\n\n const dirs = fg.sync(\n globs.map((g) => g.replace(/\\/$/, '')),\n {\n cwd: root,\n onlyDirectories: true,\n absolute: true,\n },\n );\n\n const packages = dirs.flatMap((dir) => {\n const info = readPackageInfo(dir);\n if (info) {\n return [info];\n }\n return [];\n });\n\n const rootPkg = readPackageInfo(root);\n if (rootPkg) {\n return [{ ...rootPkg, name: '<root>' }, ...packages];\n }\n return packages;\n}\n\n/**\n * Read the package name from a directory's `package.json`.\n *\n * @param dir - Absolute path to the directory\n * @returns Package info if valid, or `undefined` if missing or malformed\n * @private\n */\nfunction readPackageInfo(dir: string): PackageInfo | undefined {\n const pkgJsonPath = path.join(dir, 'package.json');\n const [error, content] = attempt(() => fs.readFileSync(pkgJsonPath, 'utf-8'));\n if (error || content === null) {\n return undefined;\n }\n\n const [parseError, pkg] = safeParseJSON(content);\n if (parseError || pkg === null) {\n return undefined;\n }\n\n if (typeof pkg !== 'object' || !('name' in pkg) || typeof pkg.name !== 'string') {\n return undefined;\n }\n\n return { name: pkg.name, dir };\n}\n","import type { ChildProcess } from 'node:child_process';\nimport { spawn } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport * as p from '@clack/prompts';\nimport { attempt } from 'es-toolkit';\n\nimport { LAUF_ROOT, getWorkspaceRoot, resolveTsx } from '../lib/paths.ts';\nimport type { DiscoveredScript, RunResult } from '../lib/types.ts';\nimport { safeParseError } from '../utils/cli.ts';\n\nconst EXECUTOR_DIST_PATH = path.join(LAUF_ROOT, 'dist', 'runtime', 'executor.mjs');\nconst EXECUTOR_SRC_PATH = path.join(LAUF_ROOT, 'src', 'runtime', 'executor.ts');\n\n/**\n * Resolve the executor entry point, preferring the built dist version\n * and falling back to the source .ts path if dist is unavailable.\n *\n * Returns undefined if neither exists.\n */\nfunction resolveExecutorPath(): string | undefined {\n const [distErr, distExists] = attempt(() => fs.existsSync(EXECUTOR_DIST_PATH));\n if (!distErr && distExists) {\n return EXECUTOR_DIST_PATH;\n }\n\n const [srcErr, srcExists] = attempt(() => fs.existsSync(EXECUTOR_SRC_PATH));\n if (!srcErr && srcExists) {\n return EXECUTOR_SRC_PATH;\n }\n\n return undefined;\n}\n\n/**\n * Execute a discovered script by spawning tsx with the executor entry point.\n *\n * The script runs in a child process with context passed via environment\n * variables. The promise resolves when the child process exits.\n *\n * NOTE: The full parent process environment is intentionally inherited by\n * child scripts so that user-authored scripts can access env vars they\n * depend on (e.g. API keys, database URLs, CI variables). Filtering the\n * environment would break legitimate use cases. The metadata extraction\n * path (in list.ts) should apply its own filtering if needed.\n *\n * @param script - The discovered script to run\n * @param args - Parsed arguments to pass to the script\n * @param options - Optional flags (help mode, config directory)\n * @returns Promise resolving to the run result with exit code\n */\ninterface RunScriptOptions {\n readonly help?: boolean;\n readonly configDir?: string;\n}\n\n// oxlint-disable-next-line max-lines-per-function\nexport function runScript(\n script: DiscoveredScript,\n args: Record<string, unknown>,\n options?: RunScriptOptions,\n): Promise<RunResult> {\n const tsxResult = resolveTsx();\n if (tsxResult[0]) {\n p.log.error(tsxResult[0].message);\n return Promise.resolve({ exitCode: 1, script });\n }\n\n const executorPath = resolveExecutorPath();\n if (!executorPath) {\n p.log.error('Executor entry point not found. Run `pnpm build` to generate it.');\n return Promise.resolve({ exitCode: 1, script });\n }\n\n // TypeScript doesn't narrow tuple index [1] after checking [0]; safe cast after guard above\n const tsxPath = tsxResult[1] as string;\n const configDir = resolveConfigDir(options);\n const helpEnv = resolveHelpEnv(options);\n\n return new Promise((resolve) => {\n const child = spawn(tsxPath, [executorPath], {\n cwd: script.packageDir,\n stdio: 'inherit',\n env: {\n ...process.env,\n NODE_PATH: buildNodePath(),\n LAUF_SCRIPT_PATH: script.path,\n LAUF_ARGS: JSON.stringify(args),\n LAUF_WORKSPACE_ROOT: getWorkspaceRoot(),\n LAUF_CONFIG_DIR: configDir,\n LAUF_PACKAGE_DIR: script.packageDir,\n LAUF_SCRIPT_NAME: script.name,\n ...helpEnv,\n },\n });\n\n const signalCleanup = registerSignalForwarding(child);\n\n // AbortController signals settlement so only the first event\n // (close or error) resolves the promise. The second event is a no-op.\n const ac = new AbortController();\n\n child.once('close', (code) => {\n if (ac.signal.aborted) {\n return;\n }\n ac.abort();\n signalCleanup();\n resolve({\n exitCode: code ?? 1,\n script,\n });\n });\n\n child.once('error', (err) => {\n if (ac.signal.aborted) {\n return;\n }\n ac.abort();\n signalCleanup();\n p.log.error(`Failed to spawn script executor: ${safeParseError(err)}`);\n resolve({\n exitCode: 1,\n script,\n });\n });\n });\n}\n\n/**\n * Resolve the config directory from options, falling back to workspace root.\n */\nfunction resolveConfigDir(options: RunScriptOptions | undefined): string {\n if (options && options.configDir) {\n return options.configDir;\n }\n return getWorkspaceRoot();\n}\n\n/**\n * Resolve help-mode environment variables from options.\n */\nfunction resolveHelpEnv(options: RunScriptOptions | undefined): Record<string, string> {\n if (options && options.help) {\n return { LAUF_HELP: '1' };\n }\n return {};\n}\n\n/**\n * Register SIGINT and SIGTERM forwarding to the child process.\n * Returns a cleanup function that removes the signal handlers.\n *\n * @param child - The spawned child process\n * @returns Cleanup function to remove signal handlers\n * @private\n */\nfunction registerSignalForwarding(child: ChildProcess): () => void {\n const forwardSigint = (): void => {\n child.kill('SIGINT');\n };\n const forwardSigterm = (): void => {\n child.kill('SIGTERM');\n };\n\n process.on('SIGINT', forwardSigint);\n process.on('SIGTERM', forwardSigterm);\n\n return () => {\n process.removeListener('SIGINT', forwardSigint);\n process.removeListener('SIGTERM', forwardSigterm);\n };\n}\n\n/**\n * Build `NODE_PATH` so scripts can resolve lauf's dependencies (zod, etc.)\n * without requiring each package to declare them explicitly.\n *\n * @returns Colon-delimited `NODE_PATH` string\n * @private\n */\nfunction buildNodePath(): string {\n const paths = [\n path.join(LAUF_ROOT, 'node_modules'),\n path.join(getWorkspaceRoot(), 'node_modules'),\n ];\n const existing = process.env.NODE_PATH;\n if (existing) {\n return [...paths, existing].join(path.delimiter);\n }\n return paths.join(path.delimiter);\n}\n","import * as path from 'node:path';\n\nimport fg from 'fast-glob';\n\nimport { resolveWorkspacePackages } from './paths.ts';\nimport type { DiscoveredScript } from './types.ts';\nimport { getWorkspaceRoot } from './workspace.ts';\n\n/**\n * Validate that a glob pattern is safe for workspace-scoped discovery.\n *\n * Rejects patterns that start with `..` (parent traversal) or `/` (absolute path),\n * which could escape the workspace boundary.\n *\n * @param pattern - Glob pattern to validate\n * @returns `true` if the pattern is safe, `false` otherwise\n */\nfunction isValidPattern(pattern: string): boolean {\n // Reject null bytes (path poisoning)\n /* v8 ignore next 3 -- defensive security guard; callers never produce null bytes */\n if (pattern.includes('\\0')) {\n return false;\n }\n // Reject absolute paths (Unix `/` prefix and Windows drive letters like `C:\\...`)\n if (pattern.startsWith('..') || pattern.startsWith('/') || path.isAbsolute(pattern)) {\n return false;\n }\n // Reject patterns with embedded parent traversal (e.g. \"scripts/../../sensitive/*.ts\")\n const normalized = path.normalize(pattern);\n if (normalized.startsWith('..') || normalized.startsWith('/') || path.isAbsolute(normalized)) {\n return false;\n }\n return true;\n}\n\n/**\n * Filter discovered scripts to those whose resolved paths are within the workspace root.\n *\n * @param scripts - Scripts discovered by globbing\n * @param workspaceRoot - Absolute path to the workspace root\n * @returns Only scripts with paths inside the workspace root\n */\nfunction filterToWorkspace(\n scripts: readonly DiscoveredScript[],\n workspaceRoot: string,\n): readonly DiscoveredScript[] {\n const normalizedRoot = path.resolve(workspaceRoot);\n return scripts.filter((script) => {\n const resolved = path.resolve(script.path);\n return resolved === normalizedRoot || resolved.startsWith(`${normalizedRoot}${path.sep}`);\n });\n}\n\n/**\n * Options for script discovery.\n */\ninterface DiscoverOptions {\n /**\n * When provided, only discover scripts in packages whose directories\n * are within this path. Used by the `--all` flag to scope each config's\n * discovery to its own subtree.\n */\n readonly scopeDir?: string;\n}\n\n/**\n * Discover all lauf scripts across the monorepo.\n *\n * Scans each workspace package for files matching the given glob patterns\n * and returns qualified names in the format `<package-name>/<script-stem>`.\n *\n * When `options.scopeDir` is provided, only packages under that directory\n * are included in the discovery.\n *\n * @param patterns - Glob patterns relative to each package directory\n * @param options - Optional discovery options\n * @returns Readonly array of discovered scripts, sorted by name\n *\n * @example\n * ```ts\n * const scripts = discoverScripts(['scripts/*.ts'])\n * // [{ name: '@apps/api/generate-types', path: '/...', ... }, ...]\n * ```\n */\nexport function discoverScripts(\n patterns: string[],\n options?: DiscoverOptions,\n): readonly DiscoveredScript[] {\n const validPatterns = patterns.filter((p) => isValidPattern(p));\n\n if (validPatterns.length === 0) {\n return [];\n }\n\n const packages = resolveWorkspacePackages();\n const workspaceRoot = getWorkspaceRoot();\n const scopedPackages = filterPackagesByScope(packages, options);\n\n const scripts = scopedPackages\n .flatMap((pkg) => {\n const files = fg.sync(validPatterns, {\n cwd: pkg.dir,\n absolute: true,\n onlyFiles: true,\n });\n\n return files.map((filePath): DiscoveredScript => {\n const stem = stripScriptSuffix(path.basename(filePath, '.ts'));\n return {\n name: `${pkg.name}/${stem}`,\n path: filePath,\n packageDir: pkg.dir,\n packageName: pkg.name,\n };\n });\n })\n .toSorted((a, b) => a.name.localeCompare(b.name));\n\n return filterToWorkspace(scripts, workspaceRoot);\n}\n\n/**\n * Filter packages to those whose directories are within the scope directory.\n *\n * When no scopeDir is provided, returns all packages unchanged.\n */\nfunction filterPackagesByScope(\n packages: readonly { readonly name: string; readonly dir: string }[],\n options: DiscoverOptions | undefined,\n): readonly { readonly name: string; readonly dir: string }[] {\n if (!options || !options.scopeDir) {\n return packages;\n }\n const scope = path.resolve(options.scopeDir);\n return packages.filter((pkg) => {\n const resolved = path.resolve(pkg.dir);\n return resolved === scope || resolved.startsWith(`${scope}${path.sep}`);\n });\n}\n\nfunction stripScriptSuffix(stem: string): string {\n if (stem.endsWith('.laufen')) {\n return stem.slice(0, -'.laufen'.length);\n }\n if (stem.endsWith('.lauf')) {\n return stem.slice(0, -'.lauf'.length);\n }\n return stem;\n}\n\n/**\n * Find a single script by its qualified name.\n *\n * When a pre-discovered scripts array is provided, searches within it\n * instead of re-discovering all scripts. This avoids redundant globbing\n * when the caller already has the full list.\n *\n * @param scriptName - Qualified name (e.g. `@apps/sandbox/generate-types`)\n * @param patterns - Glob patterns relative to each package directory\n * @param scripts - Optional pre-discovered scripts array to search within\n * @returns The discovered script, or `undefined` if not found\n *\n * @example\n * ```ts\n * const allScripts = discoverScripts(['scripts/*.ts'])\n * const script = findScript('@apps/api/generate-types', ['scripts/*.ts'], allScripts)\n * if (script) {\n * console.log(script.path)\n * }\n * ```\n */\nexport function findScript(\n scriptName: string,\n patterns: string[],\n scripts?: readonly DiscoveredScript[],\n): DiscoveredScript | undefined {\n if (scripts !== undefined) {\n return scripts.find((s) => s.name === scriptName);\n }\n return discoverScripts(patterns).find((s) => s.name === scriptName);\n}\n","import { discoverScripts, findScript } from '../lib/discovery.ts';\nimport { fail, ok } from '../lib/result.ts';\nimport type { HandlerResult } from '../lib/result.ts';\nimport type { DiscoveredScript } from '../lib/types.ts';\nimport { promptForScript } from './prompt.ts';\n\n/**\n * Resolve which script to target — by name if provided, or via interactive prompt.\n *\n * @param scriptName - Qualified script name (e.g. `@apps/api/generate-types`), or undefined to prompt\n * @param patterns - Glob patterns relative to each package directory\n * @returns The resolved script, or a fail result if not found or cancelled\n */\nexport function resolveScript(\n scriptName: string | undefined,\n patterns: string[],\n): Promise<HandlerResult<DiscoveredScript>> {\n if (scriptName) {\n const script = findScript(scriptName, patterns);\n if (!script) {\n return Promise.resolve(\n fail({\n message: `Script not found: ${scriptName}`,\n hint: 'Run `lauf list` to see available scripts.',\n }),\n );\n }\n return Promise.resolve(ok(script));\n }\n\n const scripts = discoverScripts(patterns);\n return promptForScript(scripts);\n}\n","import { safeLoadLaufConfigWithMeta } from '../lib/config.ts';\nimport { defineHandler } from '../lib/handler.ts';\nimport { fail, ok } from '../lib/result.ts';\nimport { runScript } from '../runtime/runner.ts';\nimport { safeParseError } from '../utils/cli.ts';\nimport { resolveScript } from '../utils/resolve-script.ts';\n\n/**\n * Handler for the `lauf help [script]` CLI command.\n *\n * Loads config, resolves the target script (prompting if omitted),\n * then spawns the executor with LAUF_HELP=1 to display script help.\n */\nexport default defineHandler(async (ctx: { parameters: { script?: string } }) => {\n const [configError, loaded] = await safeLoadLaufConfigWithMeta(process.cwd());\n if (configError) {\n return fail({ message: `Failed to load lauf config: ${safeParseError(configError)}` });\n }\n\n const [scriptError, script] = await resolveScript(ctx.parameters.script, loaded.config.scripts);\n if (scriptError) {\n return fail(scriptError);\n }\n\n const result = await runScript(script, {}, { help: true, configDir: loaded.configDir });\n\n if (result.exitCode === 0) {\n return ok();\n }\n\n return fail({\n message: `Help failed for ${script.name}`,\n exitCode: result.exitCode,\n });\n});\n","/**\n * Template for `lauf.config.ts` — written by `lauf init`.\n */\n// oxlint-disable-next-line functional/functional-parameters\nexport function configTemplate(): string {\n return `import { defineConfig } from 'laufen'\n\nexport default defineConfig({\n scripts: ['scripts/*.ts'],\n})\n`;\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport * as p from '@clack/prompts';\nimport { attempt } from 'es-toolkit';\nimport pc from 'picocolors';\n\nimport { findConfigFile } from '../lib/config-discovery.ts';\nimport { defineHandler } from '../lib/handler.ts';\nimport { fail, ok } from '../lib/result.ts';\nimport { configTemplate } from '../templates/config.ts';\n\nconst MANIFEST_FILE = 'lauf.config.ts';\n\n/**\n * Handler for the `lauf init` CLI command.\n *\n * Initializes lauf in the current directory by writing a config file.\n * Checks for an existing reachable config via upward search first.\n */\nexport default defineHandler(() => {\n const cwd = process.cwd();\n\n // Check if there's already a reachable config\n const existing = findConfigFile(cwd);\n if (existing) {\n return fail({\n message: `Already initialized: config found at ${pc.dim(existing.configFile)}`,\n });\n }\n\n const filePath = path.join(cwd, MANIFEST_FILE);\n\n const [writeError] = attempt(() =>\n fs.writeFileSync(filePath, configTemplate(), { encoding: 'utf-8', flag: 'wx' }),\n );\n if (writeError) {\n if (writeError instanceof Error) {\n const nodeError = writeError as NodeJS.ErrnoException;\n if (nodeError.code === 'EEXIST') {\n return fail({ message: `Already initialized: ${MANIFEST_FILE} already exists` });\n }\n }\n return fail({ message: `Failed to write ${MANIFEST_FILE}: ${writeError}` });\n }\n\n p.log.success(`Initialized lauf at ${pc.cyan(MANIFEST_FILE)}`);\n\n return ok();\n});\n","import { groupBy } from 'es-toolkit';\nimport pc from 'picocolors';\n\nimport type { DiscoveredScript } from '../lib/types.ts';\n\n/**\n * Extract the script stem from a qualified name.\n * e.g. \"@scope/pkg/my-script\" → \"my-script\"\n */\nfunction scriptStem(qualifiedName: string): string {\n const lastSlash = qualifiedName.lastIndexOf('/');\n /* v8 ignore start -- discoverScripts always produces \"pkg/stem\" qualified names */\n if (lastSlash === -1) {\n return qualifiedName;\n }\n /* v8 ignore stop */\n return qualifiedName.slice(lastSlash + 1);\n}\n\nfunction treeConnector(isLast: boolean): string {\n if (isLast) {\n return '└── ';\n }\n return '├── ';\n}\n\nfunction treeChildPrefix(isLast: boolean): string {\n if (isLast) {\n return ' ';\n }\n return '│ ';\n}\n\nfunction formatTreeScriptLine(\n prefix: string,\n connector: string,\n stem: string,\n description: string,\n padWidth: number,\n): string {\n const name = pc.cyan(stem.padEnd(padWidth));\n if (description) {\n return `${prefix}${connector}${name}${pc.dim(description)}`;\n }\n return `${prefix}${connector}${name}`;\n}\n\n/**\n * Build a directory-tree-style string from grouped scripts.\n *\n * ```\n * ├── @apps/api\n * │ ├── build Build the project\n * │ └── test Run tests\n * └── @libs/core\n * └── generate Generate types\n * ```\n */\nexport function buildScriptTree(\n scripts: readonly DiscoveredScript[],\n descriptions: Record<string, string>,\n): string {\n const grouped = groupBy(scripts, (s) => s.packageName);\n const entries = Object.entries(grouped);\n const maxStemLen = Math.max(...scripts.map((s) => scriptStem(s.name).length));\n const padWidth = maxStemLen + 2;\n\n return entries\n .map(([packageName, pkgScripts], pkgIdx) => {\n const isLastPkg = pkgIdx === entries.length - 1;\n const pkgBranch = treeConnector(isLastPkg);\n const childPfx = treeChildPrefix(isLastPkg);\n\n const header = `${pkgBranch}${pc.bold(packageName)}`;\n const scriptLines = pkgScripts\n .map((script, scriptIdx) => {\n const isLastScript = scriptIdx === pkgScripts.length - 1;\n const scriptBranch = treeConnector(isLastScript);\n const stem = scriptStem(script.name);\n const desc = descriptions[script.path] || '';\n return formatTreeScriptLine(childPfx, scriptBranch, stem, desc, padWidth);\n })\n .join('\\n');\n\n return `${header}\\n${scriptLines}`;\n })\n .join('\\n');\n}\n","/* eslint-disable import/max-dependencies */\nimport { execFile } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { promisify } from 'node:util';\n\nimport * as p from '@clack/prompts';\nimport { attempt, attemptAsync, uniqBy } from 'es-toolkit';\nimport pc from 'picocolors';\nimport { z } from 'zod';\n\nimport type { LoadedConfig } from '../lib/config.ts';\nimport { loadAllLaufConfigs, safeLoadLaufConfigWithMeta } from '../lib/config.ts';\nimport { discoverScripts } from '../lib/discovery.ts';\nimport { defineHandler } from '../lib/handler.ts';\nimport { LAUF_ROOT, getWorkspaceRoot, resolveTsx } from '../lib/paths.ts';\nimport { fail, ok } from '../lib/result.ts';\nimport type { DiscoveredScript } from '../lib/types.ts';\nimport { safeParseError } from '../utils/cli.ts';\nimport { safeParseJSON } from '../utils/json.ts';\nimport { buildScriptTree } from '../utils/tree.ts';\n\nconst execFileAsync = promisify(execFile);\nconst METADATA_DIST_PATH = path.join(LAUF_ROOT, 'dist', 'runtime', 'metadata.mjs');\nconst METADATA_SRC_PATH = path.join(LAUF_ROOT, 'src', 'runtime', 'metadata.ts');\n\nconst listParams = z.object({\n flags: z.object({ all: z.boolean().optional() }),\n});\n\n/**\n * Handler for the `lauf list` CLI command.\n *\n * Discovers all lauf scripts across the monorepo and prints\n * a hierarchical tree grouped by package, with descriptions\n * extracted by importing each script via tsx.\n */\nexport default defineHandler({\n parameters: listParams,\n handler: (ctx) => {\n if (ctx.flags.all) {\n return listAllScripts();\n }\n return listScopedScripts();\n },\n});\n\n/**\n * List scripts from the closest config (default behavior).\n */\nasync function listScopedScripts() {\n const [configError, loaded] = await safeLoadLaufConfigWithMeta(process.cwd());\n if (configError) {\n return fail({ message: `Failed to load lauf config: ${safeParseError(configError)}` });\n }\n\n const scripts = discoverScripts(loaded.config.scripts);\n return displayScripts(scripts);\n}\n\n/**\n * List scripts from all configs within the search boundary (--all flag).\n */\nasync function listAllScripts() {\n const configs = await loadAllLaufConfigs(process.cwd());\n\n const allScripts = configs.flatMap((loaded: LoadedConfig) =>\n discoverScripts(loaded.config.scripts, { scopeDir: loaded.configDir }),\n );\n\n // Deduplicate by script path (closest config wins since configs are sorted shallowest-first)\n const unique = uniqBy(allScripts, (s) => s.path);\n\n return displayScripts(unique);\n}\n\n/**\n * Shared display logic for discovered scripts.\n *\n * Renders a directory-tree-style hierarchy grouped by package,\n * including scripts from all packages (root and workspace members).\n */\nasync function displayScripts(scripts: readonly DiscoveredScript[]) {\n if (scripts.length === 0) {\n p.log.warn('No scripts found.');\n p.log.message(pc.dim('Create one with: lauf create <name>'));\n return ok();\n }\n\n const descriptions = await loadDescriptions(scripts);\n const tree = buildScriptTree(scripts, descriptions);\n\n p.note(tree, `Found ${scripts.length} script(s)`);\n\n return ok();\n}\n\n/**\n * Resolve the metadata extractor path, preferring the built dist version\n * and falling back to the source .ts path if dist is unavailable.\n *\n * Returns undefined if neither exists.\n */\nfunction resolveMetadataPath(): string | undefined {\n const [distErr, distExists] = attempt(() => fs.existsSync(METADATA_DIST_PATH));\n if (!distErr && distExists) {\n return METADATA_DIST_PATH;\n }\n\n const [srcErr, srcExists] = attempt(() => fs.existsSync(METADATA_SRC_PATH));\n if (!srcErr && srcExists) {\n return METADATA_SRC_PATH;\n }\n\n return undefined;\n}\n\n/**\n * Build the NODE_PATH array for the metadata extractor subprocess.\n *\n * Includes the lauf and workspace node_modules directories,\n * plus the existing NODE_PATH if set.\n */\nfunction buildNodePaths(workspaceRoot: string): readonly string[] {\n const base = [path.join(LAUF_ROOT, 'node_modules'), path.join(workspaceRoot, 'node_modules')];\n const existing = process.env.NODE_PATH;\n if (existing) {\n return [...base, existing];\n }\n /* v8 ignore next 2 -- trivial else branch; NODE_PATH is almost always set in test env */\n return base;\n}\n\n/**\n * Build a minimal environment for the metadata extractor subprocess.\n *\n * Only exposes PATH, HOME, TERM, NODE_PATH, and any LAUF_* variables\n * to avoid leaking secrets from the parent environment.\n */\nfunction buildMinimalEnv(): Record<string, string | undefined> {\n const laufEntries = Object.entries(process.env).filter(([key]) => key.startsWith('LAUF_'));\n const base: Record<string, string | undefined> = {\n PATH: process.env.PATH,\n HOME: process.env.HOME,\n TERM: process.env.TERM,\n NODE_PATH: process.env.NODE_PATH,\n };\n return Object.assign(base, Object.fromEntries(laufEntries));\n}\n\n/**\n * Spawn the metadata extractor via tsx to import all scripts\n * and return their descriptions as a path → description record.\n *\n * Fails gracefully — returns an empty record on any error so\n * the list command still works, just without descriptions.\n */\n// oxlint-disable-next-line max-lines-per-function\nasync function loadDescriptions(\n scripts: readonly DiscoveredScript[],\n): Promise<Record<string, string>> {\n /* v8 ignore next 3 -- defensive guard; handler already returns early when scripts is empty */\n if (scripts.length === 0) {\n return {};\n }\n\n const metadataPath = resolveMetadataPath();\n if (!metadataPath) {\n p.log.warn(\n 'Script descriptions unavailable: metadata extractor not found. Run `pnpm build` to generate it.',\n );\n return {};\n }\n\n const [tsxError, tsxPath] = resolveTsx();\n if (tsxError) {\n p.log.warn(`Script descriptions unavailable: ${tsxError.message}`);\n return {};\n }\n\n // TypeScript doesn't narrow tuple index [1] after checking [0]; safe cast after guard above\n const resolvedTsxPath = tsxPath as string;\n\n const workspaceRoot = getWorkspaceRoot();\n const nodePaths = buildNodePaths(workspaceRoot);\n\n const [error, result] = await attemptAsync(() =>\n execFileAsync(resolvedTsxPath, [metadataPath], {\n env: {\n ...buildMinimalEnv(),\n NODE_PATH: nodePaths.join(path.delimiter),\n LAUF_SCRIPT_PATHS: JSON.stringify(scripts.map((s) => s.path)),\n LAUF_WORKSPACE_ROOT: workspaceRoot,\n },\n timeout: 15_000,\n }),\n );\n\n // es-toolkit's attemptAsync types require the null check for TS narrowing\n if (error || result === null) {\n p.log.warn('Description extraction timed out or failed. Listing scripts without descriptions.');\n return {};\n }\n\n const [parseError, parsed] = safeParseJSON(String(result.stdout));\n if (parseError || parsed === null || typeof parsed !== 'object') {\n p.log.warn('Description extraction failed: could not parse metadata output.');\n return {};\n }\n\n const descriptions = parsed as Record<string, string>;\n\n if (Object.keys(descriptions).length === 0 && scripts.length > 0) {\n p.log.warn('Description extraction returned no results. Descriptions may be unavailable.');\n }\n\n return descriptions;\n}\n","import * as p from '@clack/prompts';\n\nconst BLOCKED_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\n/**\n * Check whether a key is safe (not a prototype pollution vector).\n */\nfunction isSafeKey(key: string): boolean {\n return !BLOCKED_KEYS.has(key);\n}\n\n// oxlint-disable-next-line security/detect-unsafe-regex -- bounded decimal pattern, no backtracking risk\nconst STRICT_NUMBER_RE = /^-?\\d+(\\.\\d+)?$/;\n\n/**\n * Coerce a string value to a primitive type.\n *\n * Attempts boolean and strict decimal number coercion, falls back to string.\n * Rejects hex strings (0x...) and scientific notation (1e10) to avoid\n * unexpected conversions.\n *\n * @param value - Raw string value from CLI\n * @returns Coerced boolean, number, or original string\n * @private\n */\nfunction coerce(value: string): unknown {\n if (value === 'true') {\n return true;\n }\n if (value === 'false') {\n return false;\n }\n\n if (STRICT_NUMBER_RE.test(value)) {\n return Number(value);\n }\n\n return value;\n}\n\n/**\n * Parse raw CLI argv into a key-value record.\n *\n * Supports `--key=value`, `--key value`, and `--flag` (boolean true).\n * Uses recursion instead of stateful reduction. Warns when positional\n * arguments are found and filters out prototype pollution keys.\n *\n * @param argv - Raw argument strings\n * @returns Parsed key-value record\n * @private\n */\n// oxlint-disable-next-line max-lines-per-function\nexport function parseRawArgs(argv: readonly string[]): Record<string, unknown> {\n const parse = (\n remaining: readonly string[],\n acc: Record<string, unknown>,\n positionals: readonly string[],\n ): { readonly result: Record<string, unknown>; readonly positionals: readonly string[] } => {\n const arg = remaining[0];\n if (arg === undefined) {\n return { result: acc, positionals };\n }\n\n const rest = remaining.slice(1);\n\n if (!arg.startsWith('--')) {\n return parse(rest, acc, [...positionals, arg]);\n }\n\n const withoutDashes = arg.slice(2);\n const eqIdx = withoutDashes.indexOf('=');\n\n if (eqIdx !== -1) {\n // --key=value\n const key = withoutDashes.slice(0, eqIdx);\n const value = withoutDashes.slice(eqIdx + 1);\n if (isSafeKey(key)) {\n return parse(rest, { ...acc, [key]: coerce(value) }, positionals);\n }\n return parse(rest, acc, positionals);\n }\n\n const next = rest[0];\n if (next !== undefined && !next.startsWith('--')) {\n // --key value\n if (isSafeKey(withoutDashes)) {\n return parse(rest.slice(1), { ...acc, [withoutDashes]: coerce(next) }, positionals);\n }\n return parse(rest.slice(1), acc, positionals);\n }\n\n // --flag (boolean)\n if (isSafeKey(withoutDashes)) {\n return parse(rest, { ...acc, [withoutDashes]: true }, positionals);\n }\n return parse(rest, acc, positionals);\n };\n\n const initial: Record<string, unknown> = Object.create(null) as Record<string, unknown>;\n const { result, positionals } = parse(argv, initial, []);\n\n if (positionals.length > 0) {\n p.log.warn(\n `Positional arguments were ignored: ${positionals.join(', ')}. Use --key value syntax instead.`,\n );\n }\n\n return result;\n}\n\n/**\n * Extract raw argv entries after the given script name.\n *\n * Only searches after index 2 (skipping the node binary and script path)\n * to avoid matching wrong argv entries.\n *\n * @param name - Script name to search for in `process.argv`\n * @returns Argv entries following the script name, or empty array if not found\n * @private\n */\nexport function sliceArgvAfter(name: string): readonly string[] {\n // Slice from index 3 to skip: [0] node binary, [1] script path, [2] Clerc subcommand (\"run\")\n const scriptArgs = process.argv.slice(3);\n const idx = scriptArgs.findIndex((arg) => arg === name);\n if (idx === -1) {\n return [];\n }\n return scriptArgs.slice(idx + 1);\n}\n","// oxlint-disable import/max-dependencies\nimport * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport { z } from 'zod';\n\nimport { safeLoadLaufConfigWithMeta } from '../lib/config.ts';\nimport { defineHandler } from '../lib/handler.ts';\nimport { fail, ok } from '../lib/result.ts';\nimport type { DiscoveredScript, RunResult } from '../lib/types.ts';\nimport { runScript } from '../runtime/runner.ts';\nimport { parseRawArgs, sliceArgvAfter } from '../utils/argv.ts';\nimport { safeParseError } from '../utils/cli.ts';\nimport { resolveScript } from '../utils/resolve-script.ts';\n\nconst runParams = z.object({\n parameters: z.object({ script: z.string().min(1).optional() }),\n});\n\n/**\n * Handler for the `lauf run [script]` CLI command.\n *\n * Resolves the script by qualified name (or prompts for selection),\n * parses any trailing CLI flags into arguments, and spawns the script executor.\n */\nexport default defineHandler({\n parameters: runParams,\n handler: async (ctx) => {\n const [configError, loaded] = await safeLoadLaufConfigWithMeta(process.cwd());\n if (configError) {\n return fail({ message: `Failed to load lauf config: ${safeParseError(configError)}` });\n }\n\n const [scriptError, script] = await resolveScript(ctx.parameters.script, loaded.config.scripts);\n if (scriptError) {\n return fail(scriptError);\n }\n\n const rawArgv = resolveRawArgv(ctx.parameters.script);\n const isHelp = rawArgv.includes('--help') || rawArgv.includes('-h');\n\n if (isHelp) {\n const helpResult = await runScript(script, {}, { help: true, configDir: loaded.configDir });\n /* v8 ignore start -- help via runScript delegates to executor which handles its own errors */\n if (helpResult.exitCode === 0) {\n return ok();\n }\n return fail({ message: `Help failed for ${script.name}`, exitCode: helpResult.exitCode });\n }\n /* v8 ignore stop */\n\n const args = parseRawArgs(rawArgv);\n const result = await executeScript(script, args, loaded.configDir);\n\n if (result.exitCode === 0) {\n return ok();\n }\n\n return fail({\n message: `${pc.cyan(script.name)} exited with code ${result.exitCode}`,\n exitCode: result.exitCode,\n });\n },\n});\n\n/**\n * Extract raw argv entries following the given script name.\n * Returns an empty array when no script name was provided on the CLI.\n */\nfunction resolveRawArgv(scriptName: string | undefined): readonly string[] {\n if (scriptName) {\n return sliceArgvAfter(scriptName);\n }\n return [];\n}\n\n/**\n * Run a script, logging start and result messages.\n *\n * The handler no longer wraps execution in a spinner because\n * the child process may prompt for missing arguments, and the\n * spinner animation conflicts with interactive prompts.\n * Scripts can use `ctx.spinner` for progress indication during\n * their own execution after all prompts are resolved.\n */\nasync function executeScript(\n script: DiscoveredScript,\n args: Record<string, unknown>,\n configDir: string,\n): Promise<RunResult> {\n const label = pc.cyan(script.name);\n\n p.log.step(`Running ${label}`);\n const result = await runScript(script, args, { configDir });\n if (result.exitCode === 0) {\n p.log.success(`${label} completed successfully`);\n }\n return result;\n}\n","#!/usr/bin/env node\n// oxlint-disable import/max-dependencies\n\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport * as p from '@clack/prompts';\nimport { Clerc, helpPlugin, versionPlugin } from 'clerc';\nimport pc from 'picocolors';\n\nimport handleCreate from './handlers/create.ts';\nimport handleInfo from './handlers/info.ts';\nimport handleInit from './handlers/init.ts';\nimport handleList from './handlers/list.ts';\nimport handleRun from './handlers/run.ts';\nimport { errorHint, readPackageJSON, safeParseError } from './utils/cli.ts';\n\nconst pkgDir = path.dirname(path.dirname(fileURLToPath(import.meta.url)));\nconst [pkgError, pkg] = readPackageJSON(pkgDir);\n\nif (pkgError || pkg === null || !pkg.version) {\n p.log.error(\n 'Fatal error, unable to execute lauf, please log an issue on github: https://github.com/zrosenbauer/lauf/issues',\n );\n process.exit(1);\n}\n\nClerc.create()\n .name('lauf')\n .scriptName('lauf')\n .description('Typed script runner for monorepos')\n .version(pkg.version)\n .use(helpPlugin())\n .use(versionPlugin())\n .errorHandler((err: unknown) => {\n const message = safeParseError(err);\n p.log.error(message);\n const hint = errorHint(err);\n if (hint) {\n p.log.message(pc.dim(hint));\n }\n process.exit(1);\n })\n .command('init', 'Initialize lauf in the current package')\n .on('init', handleInit)\n .command('list', 'List all available scripts', {\n flags: {\n all: {\n type: Boolean,\n description: 'Discover scripts from all nested configs',\n alias: 'a',\n },\n },\n })\n .on('list', handleList)\n .command('info', 'Show info for a script', {\n parameters: ['[script]'],\n })\n .on('info', handleInfo)\n .command('run', 'Run a script', {\n parameters: ['[script]'],\n })\n .on('run', handleRun)\n .command('create', 'Create a new script', {\n parameters: ['[name]'],\n flags: {\n dir: {\n type: String,\n description: 'Target directory (relative to monorepo root)',\n },\n },\n })\n .on('create', handleCreate)\n .parse();\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "laufen",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Typed script runner for monorepos — discover, validate, and execute TypeScript scripts with Zod-powered arguments",
5
5
  "keywords": [
6
6
  "bun",