clispark 1.14.0 → 1.15.0

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/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/wizard.ts","../src/languages/packs/node-oclif.ts","../src/package-root.ts","../src/update/adapters/node-oclif.ts","../src/update/reconcile.ts","../src/languages/registry-checkers/npm.ts","../src/languages/index.ts","../src/scaffold.ts","../src/update/manifest.ts","../src/errors.ts","../src/logger.ts","../src/update/update.ts","../src/update/releasenotes.ts","../src/whoami.ts","../src/confetti.ts"],"sourcesContent":["// src/cli.ts\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport { runWizard } from './wizard';\nimport { scaffoldProject } from './scaffold';\nimport { withLogging } from './logger';\nimport { formatUpdateSummary, updateProject } from './update/update';\nimport { fetchReleaseNotes, formatReleaseNotes } from './update/releasenotes';\nimport { getGeneratorVersion, requireManifest } from './update/manifest';\nimport { LANGUAGE_PACKS } from './languages';\nimport type { LanguagePack } from './languages/pack';\nimport { UserError } from './errors';\nimport { getWhoamiOutput, type WhoamiMode } from './whoami';\nimport { printConfetti } from './confetti';\n\nconst program = new Command();\n\nprogram\n .name('clispark')\n .description('Interactive scaffolding tool for new CLI projects')\n .option('--no-confetti', 'Skip the confetti after a successful run')\n .configureHelp({ showGlobalOptions: true })\n .version(getGeneratorVersion());\n\nfunction resolvePack(language: string): LanguagePack {\n const pack = LANGUAGE_PACKS[language];\n if (!pack) {\n throw new UserError(`Unknown language \"${language}\" — is your clispark installation out of date?`);\n }\n return pack;\n}\n\nprogram.action((options: { confetti?: boolean }) =>\n withLogging('scaffold', async (logger) => {\n const answers = await runWizard();\n const targetDir = path.join(process.cwd(), answers.projectName);\n const pack = resolvePack(answers.language);\n\n logger.info({ projectName: answers.projectName, targetDir, language: pack.id }, 'scaffold started');\n await scaffoldProject(\n {\n projectName: answers.projectName,\n targetDir,\n registryUrl: answers.registryUrl,\n publishIntent: answers.publishIntent,\n },\n pack,\n );\n logger.info({ projectName: answers.projectName }, 'scaffold completed');\n\n console.log(`\\nDone! Your new CLI project is ready at ${targetDir}`);\n if (options.confetti !== false) printConfetti();\n })(),\n);\n\nprogram\n .command('update')\n .description('Update generator-managed core files and dependencies to the latest clispark version')\n .action((_options: unknown, command: Command) =>\n withLogging('update', async (logger) => {\n const targetDir = process.cwd();\n const manifest = await requireManifest(targetDir);\n const language = manifest.language ?? 'node';\n const pack = resolvePack(language);\n logger.info({ targetDir, language }, 'update started');\n const result = await updateProject(targetDir, pack.updateAdapter, pack.templateDir, language);\n logger.info({ status: result.status }, 'update completed');\n console.log(formatUpdateSummary(result));\n const { confetti } = command.optsWithGlobals<{ confetti?: boolean }>();\n if (confetti !== false && result.status === 'updated') printConfetti();\n })(),\n );\n\nprogram\n .command('releasenotes')\n .description(\"Show what changed between this project's generator version and the latest clispark version\")\n .action(\n withLogging('releasenotes', async (logger) => {\n const targetDir = process.cwd();\n logger.info({ targetDir }, 'releasenotes started');\n const result = await fetchReleaseNotes(targetDir);\n logger.info({ status: result.status }, 'releasenotes completed');\n console.log(formatReleaseNotes(result));\n }),\n );\n\nfunction resolveWhoamiMode(options: { joke?: boolean; fact?: boolean }): WhoamiMode {\n if (options.joke && options.fact) {\n throw new UserError('Use either --joke or --fact, not both.');\n }\n if (options.joke) return 'joke';\n if (options.fact) return 'fact';\n return 'random';\n}\n\nprogram\n .command('whoami')\n .description('A little something extra')\n .option('--joke', 'Always show a joke')\n .option('--fact', 'Always show a fun fact about this machine')\n .action((options: { joke?: boolean; fact?: boolean }) =>\n withLogging('whoami', async (logger) => {\n const mode = resolveWhoamiMode(options);\n logger.info({ mode }, 'whoami started');\n console.log(await getWhoamiOutput(fetch, undefined, undefined, mode));\n logger.info({}, 'whoami completed');\n })(),\n );\n\nprogram.parseAsync(process.argv).catch((error: unknown) => {\n console.error(error);\n process.exit(1);\n});\n","// src/wizard.ts\nimport { intro, outro, text, select, log, isCancel, cancel } from '@clack/prompts';\nimport { LANGUAGE_PACKS } from './languages';\nimport type { LanguagePack } from './languages/pack';\nimport type { NameCheckResult } from './languages/registry-checker';\nimport type { Profile, WizardAnswers } from './types';\n\nexport interface WizardDeps {\n languagePacks: Record<string, LanguagePack>;\n}\n\nconst defaultDeps: WizardDeps = {\n languagePacks: LANGUAGE_PACKS,\n};\n\nfunction exitIfCancelled(value: unknown): void {\n if (isCancel(value)) {\n cancel('Operation cancelled.');\n process.exit(1);\n }\n}\n\nexport async function runWizard(deps: WizardDeps = defaultDeps): Promise<WizardAnswers> {\n intro('clispark — scaffold a new CLI project');\n\n const packs = Object.values(deps.languagePacks);\n const languageValue = await select({\n message: 'Which language?',\n options: packs.map((pack) => ({ value: pack.id, label: pack.displayName })),\n });\n exitIfCancelled(languageValue);\n const pack = deps.languagePacks[languageValue as string];\n\n const nameValue = await text({\n message: 'Project name',\n validate: pack.validateProjectName,\n });\n exitIfCancelled(nameValue);\n let projectName = nameValue as string;\n\n const profileValue = await select({\n message: 'Is this a work or private project?',\n options: [\n { value: 'private', label: 'Private' },\n { value: 'work', label: 'Work' },\n ],\n });\n exitIfCancelled(profileValue);\n const profile = profileValue as Profile;\n\n let registryUrl = pack.registry.defaultUrl;\n if (profile === 'work') {\n const registryValue = await text({\n message: pack.registry.promptLabel,\n placeholder: pack.registry.defaultUrl,\n defaultValue: pack.registry.defaultUrl,\n });\n exitIfCancelled(registryValue);\n registryUrl = (registryValue as string) || pack.registry.defaultUrl;\n }\n\n const publishIntentValue = await select({\n message: 'Do you plan to publish this?',\n options: [\n { value: false, label: 'No' },\n { value: true, label: 'Yes' },\n ],\n initialValue: false,\n });\n exitIfCancelled(publishIntentValue);\n const publishIntent = publishIntentValue as boolean;\n\n let nameAvailability: NameCheckResult = 'skipped';\n\n if (publishIntent) {\n nameAvailability = await pack.registry.checkNameAvailability(projectName, registryUrl);\n\n while (nameAvailability === 'taken') {\n log.warn(`\"${projectName}\" is already taken on ${registryUrl}. Please choose a different name.`);\n\n const retryValue = await text({\n message: 'Project name',\n validate: pack.validateProjectName,\n });\n exitIfCancelled(retryValue);\n projectName = retryValue as string;\n\n nameAvailability = await pack.registry.checkNameAvailability(projectName, registryUrl);\n }\n\n if (nameAvailability === 'unverified') {\n log.warn(`Could not verify availability of \"${projectName}\" on ${registryUrl}. Continuing anyway.`);\n }\n }\n\n outro(`Ready to scaffold \"${projectName}\".`);\n\n return { language: pack.id, projectName, profile, registryUrl, publishIntent, nameAvailability };\n}\n","// src/languages/packs/node-oclif.ts\nimport path from 'node:path';\nimport { findPackageRoot } from '../../package-root';\nimport type { LanguagePack } from '../pack';\nimport { nodeOclifAdapter } from '../../update/adapters/node-oclif';\nimport { npmRegistryChecker, NPM_DEFAULT_REGISTRY_URL } from '../registry-checkers/npm';\n\nfunction validateProjectName(value: string): string | undefined {\n if (!value || value.trim().length === 0) return 'Project name is required.';\n if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) {\n return 'Use lowercase letters and numbers, with single hyphens between words (no leading, trailing, or repeated hyphens).';\n }\n return undefined;\n}\n\nexport const nodeOclifPack: LanguagePack = {\n id: 'node',\n displayName: 'Node.js / TypeScript (oclif)',\n templateDir: path.join(findPackageRoot(), 'templates', 'node'),\n scaffoldCommands: [\n { command: 'npm', args: ['install'] },\n { command: 'npm', args: ['run', 'build'] },\n ],\n validateProjectName,\n updateAdapter: nodeOclifAdapter,\n registry: {\n defaultUrl: NPM_DEFAULT_REGISTRY_URL,\n promptLabel: 'Custom npm registry URL (leave empty for npmjs.org)',\n checkNameAvailability: npmRegistryChecker.checkNameAvailability,\n applyPrivateIntent: npmRegistryChecker.applyPrivateIntent,\n },\n};\n","import { existsSync, readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Finds clispark's own package root by walking up from this module's\n * location. A fixed relative path can't work here: this module's depth\n * below the package root differs between running from source (tests) and\n * running as part of the bundled `dist/cli.js` (tsup flattens everything\n * into one file, so `import.meta.url` no longer reflects the original\n * per-module nesting) — but the walk-up strategy is depth-independent\n * either way, since it doesn't matter where exactly the walk starts.\n */\nexport function findPackageRoot(): string {\n let dir = path.dirname(fileURLToPath(import.meta.url));\n while (true) {\n const pkgPath = path.join(dir, 'package.json');\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as { name?: string };\n if (pkg.name === 'clispark') return dir;\n }\n const parentDir = path.dirname(dir);\n if (parentDir === dir) {\n throw new Error(\"Could not locate clispark's own package.json.\");\n }\n dir = parentDir;\n }\n}\n","// src/update/adapters/node-oclif.ts\nimport { readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { Manifest } from '../manifest';\nimport type { CoreFieldsExtraction, ManifestFileMergeResult, UpdateAdapter } from '../adapter';\nimport { deepEquals, reconcileEntry, stringEquals, type FieldOutcome } from '../reconcile';\n\nexport const CORE_FILE_PATHS = [\n 'bin/run.ts',\n 'src/index.ts',\n 'src/base-command.ts',\n 'src/logger.ts',\n 'tsup.config.ts',\n 'vitest.config.ts',\n 'tsconfig.json',\n 'ARCHITECTURE.md',\n '.gitignore',\n] as const;\n\nexport const CORE_SCRIPT_NAMES = ['build', 'postbuild', 'pretest', 'test', 'typecheck'] as const;\n\nexport interface PackageJsonShape {\n name: string;\n version: string;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n scripts?: Record<string, string>;\n engines?: Record<string, string>;\n oclif?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\nfunction currentDependencyValue(\n pkg: PackageJsonShape,\n name: string,\n): { section: 'dependencies' | 'devDependencies'; value: string } | undefined {\n if (pkg.dependencies?.[name] !== undefined) return { section: 'dependencies', value: pkg.dependencies[name] };\n if (pkg.devDependencies?.[name] !== undefined) {\n return { section: 'devDependencies', value: pkg.devDependencies[name] };\n }\n return undefined;\n}\n\nfunction extractCoreFields(pkg: PackageJsonShape): CoreFieldsExtraction {\n const coreDependencies = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };\n\n const coreScripts: Record<string, string> = {};\n for (const name of CORE_SCRIPT_NAMES) {\n const value = pkg.scripts?.[name];\n if (value !== undefined) coreScripts[name] = value;\n }\n\n return {\n coreDependencies,\n coreScripts,\n coreFields: {\n engines: pkg.engines ?? {},\n oclif: pkg.oclif ?? {},\n },\n };\n}\n\nfunction mergePackageJson(\n currentPkg: PackageJsonShape,\n oldManifest: Manifest,\n newTemplatePkg: PackageJsonShape,\n): ManifestFileMergeResult {\n const updatedFile: PackageJsonShape = { ...currentPkg };\n let changed = false;\n\n const dependencies: FieldOutcome[] = [];\n const coreDependencies: Record<string, string> = {};\n const dependencyNames = new Set([\n ...Object.keys(newTemplatePkg.dependencies ?? {}),\n ...Object.keys(newTemplatePkg.devDependencies ?? {}),\n ]);\n\n for (const name of dependencyNames) {\n const inNewDependencies = newTemplatePkg.dependencies?.[name] !== undefined;\n const newValue = (inNewDependencies ? newTemplatePkg.dependencies : newTemplatePkg.devDependencies)![name];\n const current = currentDependencyValue(currentPkg, name);\n const oldValue = oldManifest.coreDependencies[name];\n\n const result = reconcileEntry(current?.value, oldValue, newValue, stringEquals);\n dependencies.push({ key: name, outcome: result.outcome });\n coreDependencies[name] = result.value;\n\n if (result.outcome !== 'skipped' && result.value !== current?.value) {\n changed = true;\n const section = current?.section ?? (inNewDependencies ? 'dependencies' : 'devDependencies');\n updatedFile[section] = { ...updatedFile[section], [name]: result.value };\n }\n }\n\n const scripts: FieldOutcome[] = [];\n const coreScripts: Record<string, string> = {};\n\n for (const name of CORE_SCRIPT_NAMES) {\n const newValue = newTemplatePkg.scripts?.[name];\n if (newValue === undefined) continue;\n const currentValue = currentPkg.scripts?.[name];\n const oldValue = oldManifest.coreScripts[name];\n\n const result = reconcileEntry(currentValue, oldValue, newValue, stringEquals);\n scripts.push({ key: name, outcome: result.outcome });\n coreScripts[name] = result.value;\n\n if (result.outcome !== 'skipped' && result.value !== currentValue) {\n changed = true;\n updatedFile.scripts = { ...updatedFile.scripts, [name]: result.value };\n }\n }\n\n const oldCoreFields = oldManifest.coreFields as {\n engines?: Record<string, string>;\n oclif?: Record<string, unknown>;\n };\n const fields: FieldOutcome[] = [];\n let enginesValue: Record<string, string> = oldCoreFields.engines ?? {};\n let oclifValue: Record<string, unknown> = oldCoreFields.oclif ?? {};\n\n if (newTemplatePkg.engines !== undefined) {\n const enginesResult = reconcileEntry(\n currentPkg.engines,\n oldCoreFields.engines,\n newTemplatePkg.engines,\n deepEquals,\n );\n fields.push({ key: 'engines', outcome: enginesResult.outcome });\n enginesValue = enginesResult.value;\n if (enginesResult.outcome !== 'skipped' && !deepEquals(enginesResult.value, currentPkg.engines)) {\n changed = true;\n updatedFile.engines = enginesResult.value;\n }\n }\n\n if (newTemplatePkg.oclif !== undefined) {\n const oclifResult = reconcileEntry(currentPkg.oclif, oldCoreFields.oclif, newTemplatePkg.oclif, deepEquals);\n fields.push({ key: 'oclif', outcome: oclifResult.outcome });\n oclifValue = oclifResult.value;\n if (oclifResult.outcome !== 'skipped' && !deepEquals(oclifResult.value, currentPkg.oclif)) {\n changed = true;\n updatedFile.oclif = oclifResult.value;\n }\n }\n\n return {\n updatedFile,\n changed,\n dependencies,\n scripts,\n fields,\n coreDependencies,\n coreScripts,\n coreFields: { engines: enginesValue, oclif: oclifValue },\n };\n}\n\nexport const nodeOclifAdapter: UpdateAdapter = {\n coreFilePaths: CORE_FILE_PATHS,\n\n templateSourcePath(relativePath) {\n return relativePath === '.gitignore' ? 'gitignore' : relativePath;\n },\n\n manifestFileName: 'package.json',\n\n async readManifestFile(dir) {\n const content = await readFile(path.join(dir, 'package.json'), 'utf8');\n return JSON.parse(content) as PackageJsonShape;\n },\n\n async writeManifestFile(dir, content) {\n await writeFile(path.join(dir, 'package.json'), JSON.stringify(content, null, 2) + '\\n');\n },\n\n parseManifestFile(rawContent) {\n return JSON.parse(rawContent) as PackageJsonShape;\n },\n\n readProjectName(manifestFile) {\n return (manifestFile as PackageJsonShape).name;\n },\n\n extractCoreFields(manifestFile) {\n return extractCoreFields(manifestFile as PackageJsonShape);\n },\n\n mergeManifestFile(current, oldManifest, newTemplate) {\n return mergePackageJson(current as PackageJsonShape, oldManifest, newTemplate as PackageJsonShape);\n },\n};\n","export type ReconcileOutcome = 'added' | 'replaced' | 'skipped';\n\n/** A named field/dependency/script whose reconciliation outcome should be reported to the user (e.g. \"pino: replaced\"). */\nexport interface FieldOutcome {\n key: string;\n outcome: ReconcileOutcome;\n}\n\nexport interface ReconcileResult<T> {\n outcome: ReconcileOutcome;\n value: T;\n}\n\nexport function reconcileEntry<T>(\n currentLiveValue: T | undefined,\n oldManifestValue: T | undefined,\n newTemplateValue: T,\n isEqual: (a: T, b: T) => boolean,\n): ReconcileResult<T> {\n if (currentLiveValue === undefined) {\n return { outcome: 'added', value: newTemplateValue };\n }\n if (oldManifestValue !== undefined && isEqual(currentLiveValue, oldManifestValue)) {\n return { outcome: 'replaced', value: newTemplateValue };\n }\n return { outcome: 'skipped', value: oldManifestValue ?? currentLiveValue };\n}\n\nexport function stringEquals(a: string, b: string): boolean {\n return a === b;\n}\n\nexport function deepEquals(a: unknown, b: unknown): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\n","// src/languages/registry-checkers/npm.ts\nimport { readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { NameCheckResult, RegistryChecker } from '../registry-checker';\n\nexport const NPM_DEFAULT_REGISTRY_URL = 'https://registry.npmjs.org';\n\nconst FETCH_TIMEOUT_MS = 5000;\n\nasync function checkNameAvailability(name: string, registryUrl: string): Promise<NameCheckResult> {\n const url = `${registryUrl.replace(/\\/$/, '')}/${encodeURIComponent(name)}`;\n\n try {\n const response = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });\n if (response.status === 404) return 'available';\n if (response.status === 200) return 'taken';\n return 'unverified';\n } catch {\n return 'unverified';\n }\n}\n\nasync function applyPrivateIntent(targetDir: string): Promise<void> {\n const packageJsonPath = path.join(targetDir, 'package.json');\n const pkg = JSON.parse(await readFile(packageJsonPath, 'utf8')) as Record<string, unknown>;\n pkg.private = true;\n await writeFile(packageJsonPath, JSON.stringify(pkg, null, 2) + '\\n');\n}\n\nexport const npmRegistryChecker: RegistryChecker = {\n checkNameAvailability,\n applyPrivateIntent,\n};\n","// src/languages/index.ts\nimport type { LanguagePack } from './pack';\nimport { nodeOclifPack } from './packs/node-oclif';\n\nexport const LANGUAGE_PACKS: Record<string, LanguagePack> = {\n [nodeOclifPack.id]: nodeOclifPack,\n};\n\nexport function getPackById(id: string): LanguagePack | undefined {\n return LANGUAGE_PACKS[id];\n}\n","// src/scaffold.ts\nimport { cp, readdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport spawn from 'cross-spawn';\nimport type { LanguagePack } from './languages/pack';\nimport { buildManifest, getGeneratorVersion, writeManifest } from './update/manifest';\nimport { UserError } from './errors';\n\nexport interface ScaffoldOptions {\n projectName: string;\n targetDir: string;\n registryUrl?: string;\n publishIntent?: boolean;\n}\n\nasync function assertTargetDirIsUsable(targetDir: string): Promise<void> {\n let entries: string[];\n try {\n entries = await readdir(targetDir);\n } catch {\n return;\n }\n if (entries.length > 0) {\n throw new UserError(`Directory \"${targetDir}\" already exists and is not empty.`);\n }\n}\n\nexport function applyPlaceholders(content: string, projectName: string): string {\n return content.replaceAll('{{projectName}}', projectName);\n}\n\nasync function collectFiles(dir: string): Promise<string[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const files = await Promise.all(\n entries.map((entry) => {\n const fullPath = path.join(dir, entry.name);\n return entry.isDirectory() ? collectFiles(fullPath) : Promise.resolve([fullPath]);\n }),\n );\n return files.flat();\n}\n\n// Scans every copied file rather than a hardcoded list, so a new template file\n// that needs {{projectName}} substituted can't silently be forgotten here.\nasync function replacePlaceholdersInTree(targetDir: string, projectName: string): Promise<void> {\n const files = await collectFiles(targetDir);\n await Promise.all(\n files.map(async (filePath) => {\n const content = await readFile(filePath, 'utf8');\n if (content.includes('{{projectName}}')) {\n await writeFile(filePath, applyPlaceholders(content, projectName));\n }\n }),\n );\n}\n\nexport async function copyTemplate(options: ScaffoldOptions, pack: LanguagePack): Promise<void> {\n const { projectName, targetDir, registryUrl, publishIntent } = options;\n\n await assertTargetDirIsUsable(targetDir);\n await cp(pack.templateDir, targetDir, { recursive: true });\n\n await rename(path.join(targetDir, 'gitignore'), path.join(targetDir, '.gitignore'));\n\n await replacePlaceholdersInTree(targetDir, projectName);\n\n if (publishIntent === false) {\n await pack.registry.applyPrivateIntent(targetDir);\n }\n\n if (registryUrl && registryUrl !== pack.registry.defaultUrl) {\n await writeFile(path.join(targetDir, '.npmrc'), `registry=${registryUrl}\\n`);\n }\n}\n\nexport interface ScaffoldDeps {\n runCommand: (command: string, args: string[], cwd: string) => Promise<void>;\n}\n\nasync function defaultRunCommand(command: string, args: string[], cwd: string): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const child = spawn(command, args, { cwd, stdio: 'inherit' });\n child.on('error', reject);\n child.on('close', (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(`Command \"${command} ${args.join(' ')}\" exited with code ${code}`));\n }\n });\n });\n}\n\nconst defaultScaffoldDeps: ScaffoldDeps = { runCommand: defaultRunCommand };\n\nexport async function scaffoldProject(\n options: ScaffoldOptions,\n pack: LanguagePack,\n deps: ScaffoldDeps = defaultScaffoldDeps,\n): Promise<void> {\n await copyTemplate(options, pack);\n\n const { targetDir } = options;\n const manifest = await buildManifest(targetDir, getGeneratorVersion(), pack.id, pack.updateAdapter);\n await writeManifest(targetDir, manifest);\n\n await deps.runCommand('git', ['init'], targetDir);\n await deps.runCommand('git', ['add', '-A'], targetDir);\n await deps.runCommand('git', ['commit', '-m', 'chore: initial scaffold from clispark'], targetDir);\n\n for (const scaffoldCommand of pack.scaffoldCommands) {\n await deps.runCommand(scaffoldCommand.command, scaffoldCommand.args, targetDir);\n }\n}\n","// src/update/manifest.ts\nimport { createHash } from 'node:crypto';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { UserError } from '../errors';\nimport type { UpdateAdapter } from './adapter';\n\nexport interface Manifest {\n generatorVersion: string;\n language: string;\n coreFiles: Record<string, string>;\n coreDependencies: Record<string, string>;\n coreScripts: Record<string, string>;\n coreFields: Record<string, unknown>;\n}\n\nexport function hashContent(content: string): string {\n return createHash('sha256').update(content).digest('hex');\n}\n\nexport async function hashCoreFiles(dir: string, adapter: UpdateAdapter): Promise<Record<string, string>> {\n const entries = await Promise.all(\n adapter.coreFilePaths.map(async (relativePath) => {\n const content = await readFile(path.join(dir, relativePath), 'utf8');\n return [relativePath, hashContent(content)] as const;\n }),\n );\n return Object.fromEntries(entries);\n}\n\nexport async function buildManifest(\n targetDir: string,\n generatorVersion: string,\n language: string,\n adapter: UpdateAdapter,\n): Promise<Manifest> {\n const coreFiles = await hashCoreFiles(targetDir, adapter);\n const manifestFile = await adapter.readManifestFile(targetDir);\n const { coreDependencies, coreScripts, coreFields } = adapter.extractCoreFields(manifestFile);\n return { generatorVersion, language, coreFiles, coreDependencies, coreScripts, coreFields };\n}\n\nexport const MANIFEST_RELATIVE_PATH = path.join('.clispark', 'manifest.json');\n\nexport async function writeManifest(targetDir: string, manifest: Manifest): Promise<void> {\n const manifestPath = path.join(targetDir, MANIFEST_RELATIVE_PATH);\n await mkdir(path.dirname(manifestPath), { recursive: true });\n await writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\\n');\n}\n\nexport async function readManifest(targetDir: string): Promise<Manifest | undefined> {\n try {\n const content = await readFile(path.join(targetDir, MANIFEST_RELATIVE_PATH), 'utf8');\n return JSON.parse(content) as Manifest;\n } catch {\n return undefined;\n }\n}\n\nexport async function requireManifest(targetDir: string): Promise<Manifest> {\n const manifest = await readManifest(targetDir);\n if (!manifest) {\n throw new UserError(\n 'No .clispark/manifest.json found — this project predates update support, or is not a clispark project.',\n );\n }\n return manifest;\n}\n\n/**\n * Finds clispark's own package.json by walking up from this file's location.\n * A fixed relative path (`../package.json`) can't work here: this module's\n * depth below the package root differs between running from source (tests)\n * and running as part of the bundled `dist/cli.js` (tsup flattens everything\n * into one file, so `import.meta.url` no longer reflects the original\n * per-module nesting).\n */\nexport function getGeneratorVersion(): string {\n let dir = path.dirname(fileURLToPath(import.meta.url));\n while (true) {\n const pkgPath = path.join(dir, 'package.json');\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as { name?: string; version: string };\n if (pkg.name === 'clispark') return pkg.version;\n }\n const parentDir = path.dirname(dir);\n if (parentDir === dir) {\n throw new Error(\"Could not locate clispark's own package.json.\");\n }\n dir = parentDir;\n }\n}\n","// src/errors.ts\n\n/** An expected, user-fixable failure (bad input, unmet precondition) — distinct from an unexpected crash. */\nexport class UserError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'UserError';\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport { mkdirSync, readdirSync, statSync, unlinkSync, writeFileSync } from 'node:fs';\nimport path from 'node:path';\nimport envPaths from 'env-paths';\nimport pino, { type Logger } from 'pino';\n\nconst paths = envPaths('clispark', { suffix: '' });\n\n/** Runs fn, silently swallowing any error — for best-effort work that must never abort the caller. */\nexport function safely(fn: () => void): void {\n try {\n fn();\n } catch {\n // best-effort; a failure here must never affect the surrounding operation\n }\n}\n\nexport interface LoggerHandle {\n logger: Logger;\n logFilePath: string;\n}\n\nfunction buildLogFileName(commandName: string): string {\n const timestamp = new Date().toISOString().replaceAll(/[:.]/g, '-');\n const suffix = randomBytes(3).toString('hex');\n return `${commandName}-${timestamp}-${suffix}.log`;\n}\n\nfunction getRetentionDays(): number {\n const parsed = Number(process.env.LOG_RETENTION_DAYS);\n return Number.isFinite(parsed) ? parsed : 14;\n}\n\n// Common secret-shaped field names, redacted at the top level and one level of\n// nesting (`*.<key>`). Edit this list directly if your own logging needs more.\nexport const SENSITIVE_LOG_KEYS = [\n 'password',\n 'secret',\n 'token',\n 'apiKey',\n 'accessToken',\n 'refreshToken',\n 'clientSecret',\n 'authorization',\n];\n\nfunction buildRedactPaths(keys: string[]): string[] {\n return keys.flatMap((key) => [key, `*.${key}`]);\n}\n\nconst SWEEP_MARKER_FILE = '.last-sweep';\nconst SWEEP_THROTTLE_MS = 24 * 60 * 60 * 1000; // once a day is enough given day-granularity retention\n\nfunction sweepOldLogs(logDir: string): void {\n safely(() => {\n const markerPath = path.join(logDir, SWEEP_MARKER_FILE);\n let shouldSweep = true;\n try {\n shouldSweep = Date.now() - statSync(markerPath).mtimeMs >= SWEEP_THROTTLE_MS;\n } catch {\n // no marker yet (first run in this directory) - sweep now\n }\n if (!shouldSweep) return;\n\n const cutoffMs = Date.now() - getRetentionDays() * 24 * 60 * 60 * 1000;\n for (const file of readdirSync(logDir)) {\n if (file === SWEEP_MARKER_FILE) continue;\n const filePath = path.join(logDir, file);\n if (statSync(filePath).mtimeMs < cutoffMs) {\n unlinkSync(filePath);\n }\n }\n writeFileSync(markerPath, '');\n });\n}\n\nexport function createLogger(commandName: string, logDir: string = paths.log): LoggerHandle {\n mkdirSync(logDir, { recursive: true });\n sweepOldLogs(logDir);\n\n const logFilePath = path.join(logDir, buildLogFileName(commandName));\n const fileDestination = pino.destination({ dest: logFilePath, sync: true, mode: 0o600 });\n const destination = process.env.DEBUG\n ? pino.multistream([{ stream: fileDestination }, { stream: process.stdout }])\n : fileDestination;\n const logger = pino({ redact: buildRedactPaths([...SENSITIVE_LOG_KEYS, 'registryUrl']) }, destination);\n\n return { logger, logFilePath };\n}\n\nexport function withLogging(\n commandName: string,\n action: (logger: Logger) => Promise<void>,\n logDir: string = paths.log,\n loggerFactory: typeof createLogger = createLogger,\n): () => Promise<void> {\n return async () => {\n let handle: LoggerHandle;\n try {\n handle = loggerFactory(commandName, logDir);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\\n✖ ${message}`);\n process.exit(1);\n return;\n }\n\n const { logger, logFilePath } = handle;\n safely(() => logger.info({ command: commandName }, 'started'));\n\n try {\n await action(logger);\n safely(() => logger.info({ command: commandName }, 'completed'));\n if (process.env.DEBUG) {\n console.log(`Details: ${logFilePath}`);\n }\n } catch (error) {\n safely(() => logger.error({ command: commandName, err: error }, 'failed'));\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\\n✖ ${message}`);\n console.error(`Details: ${logFilePath}`);\n process.exit(1);\n }\n };\n}\n","// src/update/update.ts\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport spawn from 'cross-spawn';\nimport { applyPlaceholders } from '../scaffold';\nimport { getGeneratorVersion, hashContent, requireManifest, writeManifest, type Manifest } from './manifest';\nimport { reconcileEntry, stringEquals, type FieldOutcome } from './reconcile';\nimport { UserError } from '../errors';\nimport { compareVersions } from './releasenotes';\nimport type { UpdateAdapter } from './adapter';\n\nexport interface UpdateDeps {\n runCommand: (command: string, args: string[], cwd: string) => Promise<void>;\n captureCommand: (command: string, args: string[], cwd: string) => Promise<string>;\n}\n\nasync function defaultRunCommand(command: string, args: string[], cwd: string): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const child = spawn(command, args, { cwd, stdio: 'inherit' });\n child.on('error', reject);\n child.on('close', (code) => {\n if (code === 0) resolve();\n else reject(new Error(`Command \"${command} ${args.join(' ')}\" exited with code ${code}`));\n });\n });\n}\n\nasync function defaultCaptureCommand(command: string, args: string[], cwd: string): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n const child = spawn(command, args, { cwd });\n let stdout = '';\n child.stdout?.on('data', (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n child.on('error', reject);\n child.on('close', (code) => {\n if (code === 0) resolve(stdout);\n else reject(new Error(`Command \"${command} ${args.join(' ')}\" exited with code ${code}`));\n });\n });\n}\n\nconst defaultUpdateDeps: UpdateDeps = { runCommand: defaultRunCommand, captureCommand: defaultCaptureCommand };\n\nexport interface FileOutcomeEntry {\n path: string;\n outcome: 'added' | 'replaced' | 'skipped' | 'no-longer-core';\n}\n\nexport interface UpdateResult {\n status: 'up-to-date' | 'no-changes' | 'updated';\n fromVersion: string;\n toVersion: string;\n files: FileOutcomeEntry[];\n dependencies: FieldOutcome[];\n scripts: FieldOutcome[];\n fields: FieldOutcome[];\n}\n\nexport async function updateProject(\n targetDir: string,\n adapter: UpdateAdapter,\n templateDir: string,\n language: string,\n deps: UpdateDeps = defaultUpdateDeps,\n): Promise<UpdateResult> {\n const status = (await deps.captureCommand('git', ['status', '--porcelain'], targetDir)).trim();\n if (status.length > 0) {\n throw new UserError('Working tree is not clean. Commit or stash your changes before running update.');\n }\n\n const oldManifest = await requireManifest(targetDir);\n const toVersion = getGeneratorVersion();\n const fromVersion = oldManifest.generatorVersion;\n\n if (compareVersions(fromVersion, toVersion) >= 0) {\n return { status: 'up-to-date', fromVersion, toVersion, files: [], dependencies: [], scripts: [], fields: [] };\n }\n\n const currentManifestFile = await adapter.readManifestFile(targetDir);\n const projectName = adapter.readProjectName(currentManifestFile);\n\n const newTemplateRaw = applyPlaceholders(\n await readFile(path.join(templateDir, adapter.manifestFileName), 'utf8'),\n projectName,\n );\n const newTemplateManifestFile = adapter.parseManifestFile(newTemplateRaw);\n\n const files: FileOutcomeEntry[] = [];\n const newCoreFiles: Record<string, string> = {};\n const fileWrites: { targetPath: string; content: string }[] = [];\n\n // Reads + hashes run in parallel; results are then applied in adapter.coreFilePaths\n // order below so files/fileWrites stay deterministic regardless of I/O timing.\n const perFileResults = await Promise.all(\n adapter.coreFilePaths.map(async (relativePath) => {\n const newContent = applyPlaceholders(\n await readFile(path.join(templateDir, adapter.templateSourcePath(relativePath)), 'utf8'),\n projectName,\n );\n const newHash = hashContent(newContent);\n\n let currentHash: string | undefined;\n try {\n currentHash = hashContent(await readFile(path.join(targetDir, relativePath), 'utf8'));\n } catch {\n currentHash = undefined;\n }\n\n const result = reconcileEntry(currentHash, oldManifest.coreFiles[relativePath], newHash, stringEquals);\n return { relativePath, newContent, result };\n }),\n );\n\n for (const { relativePath, newContent, result } of perFileResults) {\n files.push({ path: relativePath, outcome: result.outcome });\n newCoreFiles[relativePath] = result.value;\n\n if (result.outcome === 'added' || result.outcome === 'replaced') {\n fileWrites.push({ targetPath: path.join(targetDir, relativePath), content: newContent });\n }\n }\n\n for (const relativePath of Object.keys(oldManifest.coreFiles)) {\n if (adapter.coreFilePaths.includes(relativePath)) continue;\n try {\n await readFile(path.join(targetDir, relativePath), 'utf8');\n files.push({ path: relativePath, outcome: 'no-longer-core' });\n } catch {\n // already gone locally, nothing to report\n }\n }\n\n const fileMerge = adapter.mergeManifestFile(currentManifestFile, oldManifest, newTemplateManifestFile);\n\n const hasFileChanges = files.some(\n (f) => f.outcome === 'added' || f.outcome === 'replaced' || f.outcome === 'no-longer-core',\n );\n const hasChanges = hasFileChanges || fileMerge.changed;\n\n if (!hasChanges) {\n return {\n status: 'no-changes',\n fromVersion,\n toVersion,\n files,\n dependencies: fileMerge.dependencies,\n scripts: fileMerge.scripts,\n fields: fileMerge.fields,\n };\n }\n\n for (const write of fileWrites) {\n await mkdir(path.dirname(write.targetPath), { recursive: true });\n await writeFile(write.targetPath, write.content);\n }\n\n if (fileMerge.changed) {\n await adapter.writeManifestFile(targetDir, fileMerge.updatedFile);\n }\n\n const newManifest: Manifest = {\n generatorVersion: toVersion,\n language,\n coreFiles: newCoreFiles,\n coreDependencies: fileMerge.coreDependencies,\n coreScripts: fileMerge.coreScripts,\n coreFields: fileMerge.coreFields,\n };\n await writeManifest(targetDir, newManifest);\n\n await deps.runCommand('git', ['add', '-A'], targetDir);\n await deps.runCommand('git', ['commit', '-m', `chore: update clispark core to v${toVersion}`], targetDir);\n\n return {\n status: 'updated',\n fromVersion,\n toVersion,\n files,\n dependencies: fileMerge.dependencies,\n scripts: fileMerge.scripts,\n fields: fileMerge.fields,\n };\n}\n\nexport function formatUpdateSummary(result: UpdateResult): string {\n if (result.status === 'up-to-date') {\n return `Already up to date (v${result.toVersion}).`;\n }\n\n const added = result.files.filter((f) => f.outcome === 'added');\n const replaced = result.files.filter((f) => f.outcome === 'replaced');\n const skipped = result.files.filter((f) => f.outcome === 'skipped');\n const noLongerCore = result.files.filter((f) => f.outcome === 'no-longer-core');\n\n const lines: string[] = [];\n if (result.status === 'no-changes') {\n lines.push(\n noLongerCore.length\n ? `No changes applied: nothing to update since v${result.fromVersion}.`\n : `No changes applied: every core file/field has been modified locally since v${result.fromVersion}.`,\n );\n } else {\n lines.push(`Updated core from v${result.fromVersion} to v${result.toVersion}.`);\n }\n\n if (added.length) lines.push(` New: ${added.map((f) => f.path).join(', ')}`);\n if (replaced.length) lines.push(` Updated: ${replaced.map((f) => f.path).join(', ')}`);\n if (skipped.length) lines.push(` Skipped (locally modified): ${skipped.map((f) => f.path).join(', ')}`);\n if (noLongerCore.length) {\n lines.push(\n ` No longer part of the core, safe to remove manually: ${noLongerCore.map((f) => f.path).join(', ')}`,\n );\n }\n\n const fieldOutcomes = [...result.dependencies, ...result.scripts, ...result.fields].filter(\n (o) => o.outcome !== 'skipped',\n );\n if (fieldOutcomes.length) {\n lines.push(` Manifest: ${fieldOutcomes.map((o) => `${o.key} (${o.outcome})`).join(', ')}`);\n }\n\n if (result.status === 'updated') {\n lines.push('Run \"clispark releasenotes\" to see what changed.');\n }\n\n return lines.join('\\n');\n}\n","// src/update/releasenotes.ts\nimport { getGeneratorVersion, requireManifest } from './manifest';\n\nexport interface GitHubRelease {\n tag_name: string;\n name: string;\n body: string;\n}\n\nfunction stripV(version: string): string {\n return version.startsWith('v') ? version.slice(1) : version;\n}\n\nexport function compareVersions(a: string, b: string): number {\n const partsA = stripV(a).split('.').map(Number);\n const partsB = stripV(b).split('.').map(Number);\n const length = Math.max(partsA.length, partsB.length);\n for (let i = 0; i < length; i++) {\n const diff = (partsA[i] ?? 0) - (partsB[i] ?? 0);\n if (diff !== 0) return diff;\n }\n return 0;\n}\n\nexport interface ReleaseNotesResult {\n status: 'up-to-date' | 'releases-found';\n fromVersion: string;\n toVersion: string;\n releases: GitHubRelease[];\n}\n\nconst RELEASES_URL = 'https://api.github.com/repos/martinwichner/clispark/releases';\nconst FETCH_TIMEOUT_MS = 5000;\n\nexport async function fetchReleaseNotes(\n targetDir: string,\n fetchFn: typeof fetch = fetch,\n): Promise<ReleaseNotesResult> {\n const manifest = await requireManifest(targetDir);\n const fromVersion = manifest.generatorVersion;\n const toVersion = getGeneratorVersion();\n\n if (compareVersions(fromVersion, toVersion) >= 0) {\n return { status: 'up-to-date', fromVersion, toVersion, releases: [] };\n }\n\n const response = await fetchFn(RELEASES_URL, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });\n if (!response.ok) {\n throw new Error(`Failed to fetch release notes: GitHub API responded with ${response.status}`);\n }\n const allReleases = (await response.json()) as GitHubRelease[];\n\n const releases = allReleases\n .filter((r) => compareVersions(r.tag_name, fromVersion) > 0 && compareVersions(r.tag_name, toVersion) <= 0)\n .sort((a, b) => compareVersions(b.tag_name, a.tag_name));\n\n return { status: 'releases-found', fromVersion, toVersion, releases };\n}\n\nexport function formatReleaseNotes(result: ReleaseNotesResult): string {\n if (result.status === 'up-to-date') {\n return `You're on the latest clispark version (v${result.toVersion}), nothing to show.`;\n }\n if (result.releases.length === 0) {\n return `No published releases found between v${result.fromVersion} and v${result.toVersion}.`;\n }\n return result.releases.map((r) => `## ${r.tag_name}\\n\\n${r.body}`).join('\\n\\n');\n}\n","// src/whoami.ts\nimport os from 'node:os';\n\nconst JOKE_API_URL = 'https://v2.jokeapi.dev/joke/Programming,Miscellaneous';\nconst FETCH_TIMEOUT_MS = 3000;\n\n/** Languages JokeAPI (https://jokeapi.dev/) currently supports, per its /languages endpoint. */\nconst SUPPORTED_JOKE_LANGUAGES = ['cs', 'de', 'en', 'es', 'fr', 'pt'];\n\nexport const LOGO = String.raw`\n ⚡ clispark\n`;\n\nexport const FALLBACK_QUOTES = [\n \"Green unit tests don't mean it works. Real end-to-end verification does.\",\n 'A default value can silently reintroduce coupling you just removed.',\n \"The bug you can't find locally is waiting for you in CI.\",\n \"YAGNI until you actually need it — then it's just an adapter away.\",\n 'Every folder move has a bundling-depth bug waiting to happen.',\n 'The cheapest fix is the one you verify before you ship it.',\n 'TDD: write the failing test first, or explain later why it passed by accident.',\n \"If it's not tested against a real filesystem, it's a theory, not a fact.\",\n];\n\ninterface JokeApiSingle {\n error: false;\n type: 'single';\n joke: string;\n}\n\ninterface JokeApiTwoPart {\n error: false;\n type: 'twopart';\n setup: string;\n delivery: string;\n}\n\ninterface JokeApiError {\n error: true;\n}\n\ntype JokeApiResponse = JokeApiSingle | JokeApiTwoPart | JokeApiError;\n\n/** Maps a BCP-47 locale (e.g. \"de-DE\") to a JokeAPI language code, defaulting to \"en\" when unsupported. */\nexport function detectJokeLanguage(\n locale: string = Intl.DateTimeFormat().resolvedOptions().locale,\n): string {\n const primary = locale.split('-')[0].toLowerCase();\n return SUPPORTED_JOKE_LANGUAGES.includes(primary) ? primary : 'en';\n}\n\nfunction pickFallbackQuote(): string {\n return FALLBACK_QUOTES[Math.floor(Math.random() * FALLBACK_QUOTES.length)];\n}\n\nexport interface OsFacts {\n uptime: () => number;\n hostname: () => string;\n cpus: () => os.CpuInfo[];\n totalmem: () => number;\n platform: () => NodeJS.Platform;\n release: () => string;\n arch: () => string;\n}\n\nconst defaultOsFacts: OsFacts = {\n uptime: () => os.uptime(),\n hostname: () => os.hostname(),\n cpus: () => os.cpus(),\n totalmem: () => os.totalmem(),\n platform: () => os.platform(),\n release: () => os.release(),\n arch: () => os.arch(),\n};\n\nfunction formatUptime(seconds: number): string {\n const days = Math.floor(seconds / 86400);\n const hours = Math.floor((seconds % 86400) / 3600);\n const minutes = Math.floor((seconds % 3600) / 60);\n const parts: string[] = [];\n if (days > 0) parts.push(`${days}d`);\n if (days > 0 || hours > 0) parts.push(`${hours}h`);\n parts.push(`${minutes}m`);\n return parts.join(' ');\n}\n\nconst FACT_GENERATORS: Array<(facts: OsFacts) => string> = [\n (facts) => `This machine has been up for ${formatUptime(facts.uptime())}.`,\n (facts) => `Hostname: ${facts.hostname()}`,\n (facts) => {\n const cpus = facts.cpus();\n return `CPU: ${cpus[0]?.model.trim() ?? 'unknown'} (${cpus.length} logical cores)`;\n },\n (facts) => `RAM: ${(facts.totalmem() / 1024 ** 3).toFixed(1)} GB total`,\n (facts) => `OS: ${facts.platform()} ${facts.release()} (${facts.arch()})`,\n];\n\nexport function getRandomFunFact(osFacts: OsFacts = defaultOsFacts, randomFn: () => number = Math.random): string {\n const index = Math.floor(randomFn() * FACT_GENERATORS.length);\n return FACT_GENERATORS[index](osFacts);\n}\n\nasync function fetchJoke(language: string, fetchFn: typeof fetch): Promise<string | undefined> {\n try {\n const url = `${JOKE_API_URL}?lang=${language}&safe-mode`;\n const response = await fetchFn(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });\n if (!response.ok) return undefined;\n\n const data = (await response.json()) as JokeApiResponse;\n if (data.error) return undefined;\n return data.type === 'single' ? data.joke : `${data.setup}\\n${data.delivery}`;\n } catch {\n return undefined;\n }\n}\n\nexport type WhoamiMode = 'joke' | 'fact' | 'random';\n\nexport async function getWhoamiOutput(\n fetchFn: typeof fetch = fetch,\n osFacts: OsFacts = defaultOsFacts,\n randomFn: () => number = Math.random,\n mode: WhoamiMode = 'random',\n): Promise<string> {\n const showFact = mode === 'fact' || (mode === 'random' && randomFn() < 0.5);\n if (showFact) {\n return `${LOGO}\\n${getRandomFunFact(osFacts, randomFn)}\\n`;\n }\n\n const language = detectJokeLanguage();\n const quote = (await fetchJoke(language, fetchFn)) ?? pickFallbackQuote();\n return `${LOGO}\\n${quote}\\n`;\n}\n","// src/confetti.ts\n\nconst CONFETTI_VARIANTS = [\n '✨ 🎉 ✨ 🎊 ✨ 🎉 ✨\\n 🎈 🎊 🎈\\n✨ 🎉 ✨ 🎉',\n '🎊 ✨ 🎈 ✨ 🎊\\n 🎉 🎉 🎉\\n✨ 🎈 ✨ 🎈 ✨',\n '🎉✨🎊✨🎉✨🎊✨🎉\\n 🎈 🎈\\n✨🎊✨🎉✨🎊✨🎉✨',\n];\n\nexport function getConfetti(randomFn: () => number = Math.random): string {\n const index = Math.floor(randomFn() * CONFETTI_VARIANTS.length);\n return CONFETTI_VARIANTS[index];\n}\n\nexport function printConfetti(randomFn: () => number = Math.random): void {\n console.log(`\\n${getConfetti(randomFn)}\\n`);\n}\n"],"mappings":"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","names":["path","path","path","path","readFile","writeFile","path","path","pack","readFile","writeFile","path","existsSync","readFileSync","readFile","writeFile","path","fileURLToPath","readFile","path","writeFile","fileURLToPath","existsSync","readFileSync","path","readFile","writeFile","path","mkdir","readFile","writeFile","path","spawn","FETCH_TIMEOUT_MS","defaultRunCommand","spawn","readFile","path","mkdir","writeFile","FETCH_TIMEOUT_MS","path"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/wizard.ts","../src/languages/packs/node-oclif.ts","../src/package-root.ts","../src/update/adapters/node-oclif.ts","../src/update/reconcile.ts","../src/languages/registry-checkers/npm.ts","../src/languages/packs/dotnet.ts","../src/update/adapters/dotnet.ts","../src/languages/registry-checkers/nuget.ts","../src/languages/index.ts","../src/scaffold.ts","../src/update/manifest.ts","../src/errors.ts","../src/logger.ts","../src/update/update.ts","../src/update/releasenotes.ts","../src/whoami.ts","../src/confetti.ts"],"sourcesContent":["// src/cli.ts\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport { runWizard } from './wizard';\nimport { scaffoldProject } from './scaffold';\nimport { withLogging } from './logger';\nimport { formatUpdateSummary, updateProject } from './update/update';\nimport { fetchReleaseNotes, formatReleaseNotes } from './update/releasenotes';\nimport { getGeneratorVersion, requireManifest } from './update/manifest';\nimport { LANGUAGE_PACKS } from './languages';\nimport type { LanguagePack } from './languages/pack';\nimport { UserError } from './errors';\nimport { getWhoamiOutput, type WhoamiMode } from './whoami';\nimport { printConfetti } from './confetti';\n\nconst program = new Command();\n\nprogram\n .name('clispark')\n .description('Interactive scaffolding tool for new CLI projects')\n .option('--no-confetti', 'Skip the confetti after a successful run')\n .configureHelp({ showGlobalOptions: true })\n .version(getGeneratorVersion());\n\nfunction resolvePack(language: string): LanguagePack {\n const pack = LANGUAGE_PACKS[language];\n if (!pack) {\n throw new UserError(`Unknown language \"${language}\" — is your clispark installation out of date?`);\n }\n return pack;\n}\n\nprogram.action((options: { confetti?: boolean }) =>\n withLogging('scaffold', async (logger) => {\n const answers = await runWizard();\n const targetDir = path.join(process.cwd(), answers.projectName);\n const pack = resolvePack(answers.language);\n\n logger.info({ projectName: answers.projectName, targetDir, language: pack.id }, 'scaffold started');\n await scaffoldProject(\n {\n projectName: answers.projectName,\n targetDir,\n registryUrl: answers.registryUrl,\n publishIntent: answers.publishIntent,\n },\n pack,\n );\n logger.info({ projectName: answers.projectName }, 'scaffold completed');\n\n console.log(`\\nDone! Your new CLI project is ready at ${targetDir}`);\n if (options.confetti !== false) printConfetti();\n })(),\n);\n\nprogram\n .command('update')\n .description('Update generator-managed core files and dependencies to the latest clispark version')\n .action((_options: unknown, command: Command) =>\n withLogging('update', async (logger) => {\n const targetDir = process.cwd();\n const manifest = await requireManifest(targetDir);\n const language = manifest.language ?? 'node';\n const pack = resolvePack(language);\n logger.info({ targetDir, language }, 'update started');\n const result = await updateProject(targetDir, pack.updateAdapter, pack.templateDir, language);\n logger.info({ status: result.status }, 'update completed');\n console.log(formatUpdateSummary(result));\n const { confetti } = command.optsWithGlobals<{ confetti?: boolean }>();\n if (confetti !== false && result.status === 'updated') printConfetti();\n })(),\n );\n\nprogram\n .command('releasenotes')\n .description(\"Show what changed between this project's generator version and the latest clispark version\")\n .action(\n withLogging('releasenotes', async (logger) => {\n const targetDir = process.cwd();\n logger.info({ targetDir }, 'releasenotes started');\n const result = await fetchReleaseNotes(targetDir);\n logger.info({ status: result.status }, 'releasenotes completed');\n console.log(formatReleaseNotes(result));\n }),\n );\n\nfunction resolveWhoamiMode(options: { joke?: boolean; fact?: boolean }): WhoamiMode {\n if (options.joke && options.fact) {\n throw new UserError('Use either --joke or --fact, not both.');\n }\n if (options.joke) return 'joke';\n if (options.fact) return 'fact';\n return 'random';\n}\n\nprogram\n .command('whoami')\n .description('A little something extra')\n .option('--joke', 'Always show a joke')\n .option('--fact', 'Always show a fun fact about this machine')\n .action((options: { joke?: boolean; fact?: boolean }) =>\n withLogging('whoami', async (logger) => {\n const mode = resolveWhoamiMode(options);\n logger.info({ mode }, 'whoami started');\n console.log(await getWhoamiOutput(fetch, undefined, undefined, mode));\n logger.info({}, 'whoami completed');\n })(),\n );\n\nprogram.parseAsync(process.argv).catch((error: unknown) => {\n console.error(error);\n process.exit(1);\n});\n","// src/wizard.ts\nimport { intro, outro, text, select, log, isCancel, cancel } from '@clack/prompts';\nimport { LANGUAGE_PACKS } from './languages';\nimport type { LanguagePack } from './languages/pack';\nimport type { NameCheckResult } from './languages/registry-checker';\nimport type { Profile, WizardAnswers } from './types';\n\nexport interface WizardDeps {\n languagePacks: Record<string, LanguagePack>;\n}\n\nconst defaultDeps: WizardDeps = {\n languagePacks: LANGUAGE_PACKS,\n};\n\nfunction exitIfCancelled(value: unknown): void {\n if (isCancel(value)) {\n cancel('Operation cancelled.');\n process.exit(1);\n }\n}\n\nexport async function runWizard(deps: WizardDeps = defaultDeps): Promise<WizardAnswers> {\n intro('clispark — scaffold a new CLI project');\n\n const packs = Object.values(deps.languagePacks);\n const languageValue = await select({\n message: 'Which language?',\n options: packs.map((pack) => ({ value: pack.id, label: pack.displayName })),\n });\n exitIfCancelled(languageValue);\n const pack = deps.languagePacks[languageValue as string];\n\n const nameValue = await text({\n message: 'Project name',\n validate: pack.validateProjectName,\n });\n exitIfCancelled(nameValue);\n let projectName = nameValue as string;\n\n const profileValue = await select({\n message: 'Is this a work or private project?',\n options: [\n { value: 'private', label: 'Private' },\n { value: 'work', label: 'Work' },\n ],\n });\n exitIfCancelled(profileValue);\n const profile = profileValue as Profile;\n\n let registryUrl = pack.registry.defaultUrl;\n if (profile === 'work') {\n const registryValue = await text({\n message: pack.registry.promptLabel,\n placeholder: pack.registry.defaultUrl,\n defaultValue: pack.registry.defaultUrl,\n });\n exitIfCancelled(registryValue);\n registryUrl = (registryValue as string) || pack.registry.defaultUrl;\n }\n\n const publishIntentValue = await select({\n message: 'Do you plan to publish this?',\n options: [\n { value: false, label: 'No' },\n { value: true, label: 'Yes' },\n ],\n initialValue: false,\n });\n exitIfCancelled(publishIntentValue);\n const publishIntent = publishIntentValue as boolean;\n\n let nameAvailability: NameCheckResult = 'skipped';\n\n if (publishIntent) {\n nameAvailability = await pack.registry.checkNameAvailability(projectName, registryUrl);\n\n while (nameAvailability === 'taken') {\n log.warn(`\"${projectName}\" is already taken on ${registryUrl}. Please choose a different name.`);\n\n const retryValue = await text({\n message: 'Project name',\n validate: pack.validateProjectName,\n });\n exitIfCancelled(retryValue);\n projectName = retryValue as string;\n\n nameAvailability = await pack.registry.checkNameAvailability(projectName, registryUrl);\n }\n\n if (nameAvailability === 'unverified') {\n log.warn(`Could not verify availability of \"${projectName}\" on ${registryUrl}. Continuing anyway.`);\n }\n }\n\n outro(`Ready to scaffold \"${projectName}\".`);\n\n return { language: pack.id, projectName, profile, registryUrl, publishIntent, nameAvailability };\n}\n","// src/languages/packs/node-oclif.ts\nimport path from 'node:path';\nimport { findPackageRoot } from '../../package-root';\nimport type { LanguagePack } from '../pack';\nimport { nodeOclifAdapter } from '../../update/adapters/node-oclif';\nimport { npmRegistryChecker, NPM_DEFAULT_REGISTRY_URL } from '../registry-checkers/npm';\n\nfunction validateProjectName(value: string): string | undefined {\n if (!value || value.trim().length === 0) return 'Project name is required.';\n if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) {\n return 'Use lowercase letters and numbers, with single hyphens between words (no leading, trailing, or repeated hyphens).';\n }\n return undefined;\n}\n\nexport const nodeOclifPack: LanguagePack = {\n id: 'node',\n displayName: 'Node.js / TypeScript (oclif)',\n templateDir: path.join(findPackageRoot(), 'templates', 'node'),\n scaffoldCommands: [\n { command: 'npm', args: ['install'] },\n { command: 'npm', args: ['run', 'build'] },\n ],\n validateProjectName,\n updateAdapter: nodeOclifAdapter,\n registry: {\n defaultUrl: NPM_DEFAULT_REGISTRY_URL,\n promptLabel: 'Custom npm registry URL (leave empty for npmjs.org)',\n checkNameAvailability: npmRegistryChecker.checkNameAvailability,\n applyPrivateIntent: npmRegistryChecker.applyPrivateIntent,\n applyRegistryUrl: npmRegistryChecker.applyRegistryUrl,\n },\n};\n","import { existsSync, readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Finds clispark's own package root by walking up from this module's\n * location. A fixed relative path can't work here: this module's depth\n * below the package root differs between running from source (tests) and\n * running as part of the bundled `dist/cli.js` (tsup flattens everything\n * into one file, so `import.meta.url` no longer reflects the original\n * per-module nesting) — but the walk-up strategy is depth-independent\n * either way, since it doesn't matter where exactly the walk starts.\n */\nexport function findPackageRoot(): string {\n let dir = path.dirname(fileURLToPath(import.meta.url));\n while (true) {\n const pkgPath = path.join(dir, 'package.json');\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as { name?: string };\n if (pkg.name === 'clispark') return dir;\n }\n const parentDir = path.dirname(dir);\n if (parentDir === dir) {\n throw new Error(\"Could not locate clispark's own package.json.\");\n }\n dir = parentDir;\n }\n}\n","// src/update/adapters/node-oclif.ts\nimport { readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { Manifest } from '../manifest';\nimport type { CoreFieldsExtraction, ManifestFileMergeResult, UpdateAdapter } from '../adapter';\nimport { deepEquals, reconcileEntry, stringEquals, type FieldOutcome } from '../reconcile';\n\nexport const CORE_FILE_PATHS = [\n 'bin/run.ts',\n 'src/index.ts',\n 'src/base-command.ts',\n 'src/logger.ts',\n 'tsup.config.ts',\n 'vitest.config.ts',\n 'tsconfig.json',\n 'ARCHITECTURE.md',\n '.gitignore',\n] as const;\n\nexport const CORE_SCRIPT_NAMES = ['build', 'postbuild', 'pretest', 'test', 'typecheck'] as const;\n\nexport interface PackageJsonShape {\n name: string;\n version: string;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n scripts?: Record<string, string>;\n engines?: Record<string, string>;\n oclif?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\nfunction currentDependencyValue(\n pkg: PackageJsonShape,\n name: string,\n): { section: 'dependencies' | 'devDependencies'; value: string } | undefined {\n if (pkg.dependencies?.[name] !== undefined) return { section: 'dependencies', value: pkg.dependencies[name] };\n if (pkg.devDependencies?.[name] !== undefined) {\n return { section: 'devDependencies', value: pkg.devDependencies[name] };\n }\n return undefined;\n}\n\nfunction extractCoreFields(pkg: PackageJsonShape): CoreFieldsExtraction {\n const coreDependencies = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };\n\n const coreScripts: Record<string, string> = {};\n for (const name of CORE_SCRIPT_NAMES) {\n const value = pkg.scripts?.[name];\n if (value !== undefined) coreScripts[name] = value;\n }\n\n return {\n coreDependencies,\n coreScripts,\n coreFields: {\n engines: pkg.engines ?? {},\n oclif: pkg.oclif ?? {},\n },\n };\n}\n\nfunction mergePackageJson(\n currentPkg: PackageJsonShape,\n oldManifest: Manifest,\n newTemplatePkg: PackageJsonShape,\n): ManifestFileMergeResult {\n const updatedFile: PackageJsonShape = { ...currentPkg };\n let changed = false;\n\n const dependencies: FieldOutcome[] = [];\n const coreDependencies: Record<string, string> = {};\n const dependencyNames = new Set([\n ...Object.keys(newTemplatePkg.dependencies ?? {}),\n ...Object.keys(newTemplatePkg.devDependencies ?? {}),\n ]);\n\n for (const name of dependencyNames) {\n const inNewDependencies = newTemplatePkg.dependencies?.[name] !== undefined;\n const newValue = (inNewDependencies ? newTemplatePkg.dependencies : newTemplatePkg.devDependencies)![name];\n const current = currentDependencyValue(currentPkg, name);\n const oldValue = oldManifest.coreDependencies[name];\n\n const result = reconcileEntry(current?.value, oldValue, newValue, stringEquals);\n dependencies.push({ key: name, outcome: result.outcome });\n coreDependencies[name] = result.value;\n\n if (result.outcome !== 'skipped' && result.value !== current?.value) {\n changed = true;\n const section = current?.section ?? (inNewDependencies ? 'dependencies' : 'devDependencies');\n updatedFile[section] = { ...updatedFile[section], [name]: result.value };\n }\n }\n\n const scripts: FieldOutcome[] = [];\n const coreScripts: Record<string, string> = {};\n\n for (const name of CORE_SCRIPT_NAMES) {\n const newValue = newTemplatePkg.scripts?.[name];\n if (newValue === undefined) continue;\n const currentValue = currentPkg.scripts?.[name];\n const oldValue = oldManifest.coreScripts[name];\n\n const result = reconcileEntry(currentValue, oldValue, newValue, stringEquals);\n scripts.push({ key: name, outcome: result.outcome });\n coreScripts[name] = result.value;\n\n if (result.outcome !== 'skipped' && result.value !== currentValue) {\n changed = true;\n updatedFile.scripts = { ...updatedFile.scripts, [name]: result.value };\n }\n }\n\n const oldCoreFields = oldManifest.coreFields as {\n engines?: Record<string, string>;\n oclif?: Record<string, unknown>;\n };\n const fields: FieldOutcome[] = [];\n let enginesValue: Record<string, string> = oldCoreFields.engines ?? {};\n let oclifValue: Record<string, unknown> = oldCoreFields.oclif ?? {};\n\n if (newTemplatePkg.engines !== undefined) {\n const enginesResult = reconcileEntry(\n currentPkg.engines,\n oldCoreFields.engines,\n newTemplatePkg.engines,\n deepEquals,\n );\n fields.push({ key: 'engines', outcome: enginesResult.outcome });\n enginesValue = enginesResult.value;\n if (enginesResult.outcome !== 'skipped' && !deepEquals(enginesResult.value, currentPkg.engines)) {\n changed = true;\n updatedFile.engines = enginesResult.value;\n }\n }\n\n if (newTemplatePkg.oclif !== undefined) {\n const oclifResult = reconcileEntry(currentPkg.oclif, oldCoreFields.oclif, newTemplatePkg.oclif, deepEquals);\n fields.push({ key: 'oclif', outcome: oclifResult.outcome });\n oclifValue = oclifResult.value;\n if (oclifResult.outcome !== 'skipped' && !deepEquals(oclifResult.value, currentPkg.oclif)) {\n changed = true;\n updatedFile.oclif = oclifResult.value;\n }\n }\n\n return {\n updatedFile,\n changed,\n dependencies,\n scripts,\n fields,\n coreDependencies,\n coreScripts,\n coreFields: { engines: enginesValue, oclif: oclifValue },\n };\n}\n\nexport const nodeOclifAdapter: UpdateAdapter = {\n coreFilePaths: CORE_FILE_PATHS,\n\n templateSourcePath(relativePath) {\n return relativePath === '.gitignore' ? 'gitignore' : relativePath;\n },\n\n manifestFileName: 'package.json',\n\n async readManifestFile(dir) {\n const content = await readFile(path.join(dir, 'package.json'), 'utf8');\n return JSON.parse(content) as PackageJsonShape;\n },\n\n async writeManifestFile(dir, content) {\n await writeFile(path.join(dir, 'package.json'), JSON.stringify(content, null, 2) + '\\n');\n },\n\n parseManifestFile(rawContent) {\n return JSON.parse(rawContent) as PackageJsonShape;\n },\n\n readProjectName(manifestFile) {\n return (manifestFile as PackageJsonShape).name;\n },\n\n extractCoreFields(manifestFile) {\n return extractCoreFields(manifestFile as PackageJsonShape);\n },\n\n mergeManifestFile(current, oldManifest, newTemplate) {\n return mergePackageJson(current as PackageJsonShape, oldManifest, newTemplate as PackageJsonShape);\n },\n};\n","export type ReconcileOutcome = 'added' | 'replaced' | 'skipped';\n\n/** A named field/dependency/script whose reconciliation outcome should be reported to the user (e.g. \"pino: replaced\"). */\nexport interface FieldOutcome {\n key: string;\n outcome: ReconcileOutcome;\n}\n\nexport interface ReconcileResult<T> {\n outcome: ReconcileOutcome;\n value: T;\n}\n\nexport function reconcileEntry<T>(\n currentLiveValue: T | undefined,\n oldManifestValue: T | undefined,\n newTemplateValue: T,\n isEqual: (a: T, b: T) => boolean,\n): ReconcileResult<T> {\n if (currentLiveValue === undefined) {\n return { outcome: 'added', value: newTemplateValue };\n }\n if (oldManifestValue !== undefined && isEqual(currentLiveValue, oldManifestValue)) {\n return { outcome: 'replaced', value: newTemplateValue };\n }\n return { outcome: 'skipped', value: oldManifestValue ?? currentLiveValue };\n}\n\nexport function stringEquals(a: string, b: string): boolean {\n return a === b;\n}\n\nexport function deepEquals(a: unknown, b: unknown): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\n","// src/languages/registry-checkers/npm.ts\nimport { readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { NameCheckResult, RegistryChecker } from '../registry-checker';\n\nexport const NPM_DEFAULT_REGISTRY_URL = 'https://registry.npmjs.org';\n\nconst FETCH_TIMEOUT_MS = 5000;\n\nasync function checkNameAvailability(name: string, registryUrl: string): Promise<NameCheckResult> {\n const url = `${registryUrl.replace(/\\/$/, '')}/${encodeURIComponent(name)}`;\n\n try {\n const response = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });\n if (response.status === 404) return 'available';\n if (response.status === 200) return 'taken';\n return 'unverified';\n } catch {\n return 'unverified';\n }\n}\n\nasync function applyPrivateIntent(targetDir: string): Promise<void> {\n const packageJsonPath = path.join(targetDir, 'package.json');\n const pkg = JSON.parse(await readFile(packageJsonPath, 'utf8')) as Record<string, unknown>;\n pkg.private = true;\n await writeFile(packageJsonPath, JSON.stringify(pkg, null, 2) + '\\n');\n}\n\nasync function applyRegistryUrl(targetDir: string, registryUrl: string): Promise<void> {\n await writeFile(path.join(targetDir, '.npmrc'), `registry=${registryUrl}\\n`);\n}\n\nexport const npmRegistryChecker: RegistryChecker = {\n checkNameAvailability,\n applyPrivateIntent,\n applyRegistryUrl,\n};\n","// src/languages/packs/dotnet.ts\nimport path from 'node:path';\nimport { findPackageRoot } from '../../package-root';\nimport type { LanguagePack } from '../pack';\nimport { dotnetAdapter } from '../../update/adapters/dotnet';\nimport { nugetRegistryChecker, NUGET_DEFAULT_REGISTRY_URL } from '../registry-checkers/nuget';\n\nfunction validateProjectName(value: string): string | undefined {\n if (!value || value.trim().length === 0) return 'Project name is required.';\n if (!/^[A-Z][A-Za-z0-9]*$/.test(value)) {\n return 'Use PascalCase, starting with an uppercase letter (e.g. MyTool).';\n }\n return undefined;\n}\n\nexport const dotnetPack: LanguagePack = {\n id: 'dotnet',\n displayName: '.NET / C# (System.CommandLine)',\n templateDir: path.join(findPackageRoot(), 'templates', 'dotnet'),\n scaffoldCommands: [\n { command: 'dotnet', args: ['restore'] },\n { command: 'dotnet', args: ['build'] },\n ],\n validateProjectName,\n updateAdapter: dotnetAdapter,\n registry: {\n defaultUrl: NUGET_DEFAULT_REGISTRY_URL,\n promptLabel: 'Custom NuGet feed URL (leave empty for nuget.org)',\n checkNameAvailability: nugetRegistryChecker.checkNameAvailability,\n applyPrivateIntent: nugetRegistryChecker.applyPrivateIntent,\n applyRegistryUrl: nugetRegistryChecker.applyRegistryUrl,\n },\n};\n","// src/update/adapters/dotnet.ts\nimport { readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { Manifest } from '../manifest';\nimport type { CoreFieldsExtraction, ManifestFileMergeResult, UpdateAdapter } from '../adapter';\nimport { reconcileEntry, stringEquals, type FieldOutcome } from '../reconcile';\n\nexport const CORE_FILE_PATHS = [\n 'Cli.slnx',\n 'src/Program.cs',\n 'src/ICliCommand.cs',\n 'src/CommandPathAttribute.cs',\n 'src/CommandDiscovery.cs',\n 'src/CliUserException.cs',\n 'src/Logging/CliLoggerFactory.cs',\n 'src/Logging/SensitivePropertyEnricher.cs',\n 'tests/Cli.Tests.csproj',\n 'ARCHITECTURE.md',\n '.gitignore',\n] as const;\n\nexport interface DotnetManifestFile {\n raw: string;\n version: string;\n targetFramework: string;\n packageId: string;\n toolCommandName: string;\n packageReferences: Record<string, string>;\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction extractTag(content: string, tag: string): string {\n const match = content.match(new RegExp(`<${tag}>([^<]*)</${tag}>`));\n if (!match) throw new Error(`.csproj is missing a <${tag}> tag`);\n return match[1];\n}\n\nfunction extractPackageReferences(content: string): Record<string, string> {\n const refs: Record<string, string> = {};\n const re = /<PackageReference\\s+Include=\"([^\"]+)\"\\s+Version=\"([^\"]+)\"\\s*\\/>/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(content))) {\n refs[m[1]] = m[2];\n }\n return refs;\n}\n\nfunction setTag(content: string, tag: string, value: string): string {\n const escapedTag = escapeRegExp(tag);\n return content.replace(new RegExp(`(<${escapedTag}>)[^<]*(</${escapedTag}>)`), `$1${value}$2`);\n}\n\nfunction setPackageReferenceVersion(content: string, name: string, version: string): string {\n const re = new RegExp(`(<PackageReference\\\\s+Include=\"${escapeRegExp(name)}\"\\\\s+Version=\")[^\"]+(\")`);\n return content.replace(re, `$1${version}$2`);\n}\n\nfunction addPackageReference(content: string, name: string, version: string): string {\n // Reuses the indentation of the last existing <PackageReference> line\n // immediately preceding </ItemGroup>, so the new line matches siblings exactly.\n const re = /^([ \\t]*)<PackageReference[^\\n]*\\/>\\n(?=[ \\t]*<\\/ItemGroup>)/m;\n return content.replace(re, (match, indent: string) => `${match}${indent}<PackageReference Include=\"${name}\" Version=\"${version}\" />\\n`);\n}\n\nfunction parseManifestFile(rawContent: string): DotnetManifestFile {\n return {\n raw: rawContent,\n version: extractTag(rawContent, 'Version'),\n targetFramework: extractTag(rawContent, 'TargetFramework'),\n packageId: extractTag(rawContent, 'PackageId'),\n toolCommandName: extractTag(rawContent, 'ToolCommandName'),\n packageReferences: extractPackageReferences(rawContent),\n };\n}\n\nfunction extractCoreFields(manifestFile: DotnetManifestFile): CoreFieldsExtraction {\n return {\n coreDependencies: manifestFile.packageReferences,\n coreScripts: {},\n coreFields: { TargetFramework: manifestFile.targetFramework },\n };\n}\n\nfunction mergeManifestFile(\n current: DotnetManifestFile,\n oldManifest: Manifest,\n newTemplate: DotnetManifestFile,\n): ManifestFileMergeResult {\n let raw = current.raw;\n let changed = false;\n\n const dependencies: FieldOutcome[] = [];\n const coreDependencies: Record<string, string> = {};\n\n for (const name of Object.keys(newTemplate.packageReferences)) {\n const newValue = newTemplate.packageReferences[name];\n const currentValue = current.packageReferences[name];\n const oldValue = oldManifest.coreDependencies[name];\n\n const result = reconcileEntry(currentValue, oldValue, newValue, stringEquals);\n dependencies.push({ key: name, outcome: result.outcome });\n coreDependencies[name] = result.value;\n\n if (result.outcome === 'added') {\n changed = true;\n raw = addPackageReference(raw, name, result.value);\n } else if (result.outcome !== 'skipped' && result.value !== currentValue) {\n changed = true;\n raw = setPackageReferenceVersion(raw, name, result.value);\n }\n }\n\n const oldCoreFields = oldManifest.coreFields as { TargetFramework?: string };\n const fields: FieldOutcome[] = [];\n\n const targetFrameworkResult = reconcileEntry(\n current.targetFramework,\n oldCoreFields.TargetFramework,\n newTemplate.targetFramework,\n stringEquals,\n );\n fields.push({ key: 'TargetFramework', outcome: targetFrameworkResult.outcome });\n if (targetFrameworkResult.outcome !== 'skipped' && targetFrameworkResult.value !== current.targetFramework) {\n changed = true;\n raw = setTag(raw, 'TargetFramework', targetFrameworkResult.value);\n }\n\n return {\n updatedFile: { ...current, raw },\n changed,\n dependencies,\n scripts: [],\n fields,\n coreDependencies,\n coreScripts: {},\n coreFields: { TargetFramework: targetFrameworkResult.value },\n };\n}\n\nexport const dotnetAdapter: UpdateAdapter = {\n coreFilePaths: CORE_FILE_PATHS,\n\n templateSourcePath(relativePath) {\n return relativePath === '.gitignore' ? 'gitignore' : relativePath;\n },\n\n manifestFileName: 'src/Cli.csproj',\n\n async readManifestFile(dir) {\n const content = await readFile(path.join(dir, 'src', 'Cli.csproj'), 'utf8');\n return parseManifestFile(content);\n },\n\n async writeManifestFile(dir, content) {\n await writeFile(path.join(dir, 'src', 'Cli.csproj'), (content as DotnetManifestFile).raw);\n },\n\n parseManifestFile,\n\n readProjectName(manifestFile) {\n return (manifestFile as DotnetManifestFile).packageId;\n },\n\n extractCoreFields(manifestFile) {\n return extractCoreFields(manifestFile as DotnetManifestFile);\n },\n\n mergeManifestFile(current, oldManifest, newTemplate) {\n return mergeManifestFile(current as DotnetManifestFile, oldManifest, newTemplate as DotnetManifestFile);\n },\n};\n","// src/languages/registry-checkers/nuget.ts\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { NameCheckResult, RegistryChecker } from '../registry-checker';\n\nexport const NUGET_DEFAULT_REGISTRY_URL = 'https://api.nuget.org/v3/index.json';\n\nconst NUGET_FLATCONTAINER_BASE = 'https://api.nuget.org/v3-flatcontainer';\nconst FETCH_TIMEOUT_MS = 5000;\n\nasync function checkNameAvailability(name: string): Promise<NameCheckResult> {\n const url = `${NUGET_FLATCONTAINER_BASE}/${encodeURIComponent(name.toLowerCase())}/index.json`;\n\n try {\n const response = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });\n if (response.status === 404) return 'available';\n if (response.status === 200) return 'taken';\n return 'unverified';\n } catch {\n return 'unverified';\n }\n}\n\nasync function applyPrivateIntent(targetDir: string): Promise<void> {\n const csprojPath = path.join(targetDir, 'src', 'Cli.csproj');\n const content = await readFile(csprojPath, 'utf8');\n const updated = content.replace(\n /(<PropertyGroup>\\s*\\n)/,\n '$1 <IsPackable>false</IsPackable>\\n',\n );\n await writeFile(csprojPath, updated);\n}\n\nasync function applyRegistryUrl(targetDir: string, registryUrl: string): Promise<void> {\n const config = [\n '<?xml version=\"1.0\" encoding=\"utf-8\"?>',\n '<configuration>',\n ' <packageSources>',\n ' <clear />',\n ` <add key=\"custom\" value=\"${registryUrl}\" />`,\n ' </packageSources>',\n '</configuration>',\n '',\n ].join('\\n');\n await mkdir(targetDir, { recursive: true });\n await writeFile(path.join(targetDir, 'NuGet.config'), config);\n}\n\nexport const nugetRegistryChecker: RegistryChecker = {\n checkNameAvailability,\n applyPrivateIntent,\n applyRegistryUrl,\n};\n","// src/languages/index.ts\nimport type { LanguagePack } from './pack';\nimport { nodeOclifPack } from './packs/node-oclif';\nimport { dotnetPack } from './packs/dotnet';\n\nexport const LANGUAGE_PACKS: Record<string, LanguagePack> = {\n [nodeOclifPack.id]: nodeOclifPack,\n [dotnetPack.id]: dotnetPack,\n};\n\nexport function getPackById(id: string): LanguagePack | undefined {\n return LANGUAGE_PACKS[id];\n}\n","// src/scaffold.ts\nimport { cp, readdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport spawn from 'cross-spawn';\nimport type { LanguagePack } from './languages/pack';\nimport { buildManifest, getGeneratorVersion, writeManifest } from './update/manifest';\nimport { UserError } from './errors';\n\nexport interface ScaffoldOptions {\n projectName: string;\n targetDir: string;\n registryUrl?: string;\n publishIntent?: boolean;\n}\n\nasync function assertTargetDirIsUsable(targetDir: string): Promise<void> {\n let entries: string[];\n try {\n entries = await readdir(targetDir);\n } catch {\n return;\n }\n if (entries.length > 0) {\n throw new UserError(`Directory \"${targetDir}\" already exists and is not empty.`);\n }\n}\n\nexport function applyPlaceholders(content: string, projectName: string): string {\n return content.replaceAll('{{projectName}}', projectName);\n}\n\nasync function collectFiles(dir: string): Promise<string[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const files = await Promise.all(\n entries.map((entry) => {\n const fullPath = path.join(dir, entry.name);\n return entry.isDirectory() ? collectFiles(fullPath) : Promise.resolve([fullPath]);\n }),\n );\n return files.flat();\n}\n\n// Scans every copied file rather than a hardcoded list, so a new template file\n// that needs {{projectName}} substituted can't silently be forgotten here.\nasync function replacePlaceholdersInTree(targetDir: string, projectName: string): Promise<void> {\n const files = await collectFiles(targetDir);\n await Promise.all(\n files.map(async (filePath) => {\n const content = await readFile(filePath, 'utf8');\n if (content.includes('{{projectName}}')) {\n await writeFile(filePath, applyPlaceholders(content, projectName));\n }\n }),\n );\n}\n\nexport async function copyTemplate(options: ScaffoldOptions, pack: LanguagePack): Promise<void> {\n const { projectName, targetDir, registryUrl, publishIntent } = options;\n\n await assertTargetDirIsUsable(targetDir);\n await cp(pack.templateDir, targetDir, { recursive: true });\n\n await rename(path.join(targetDir, 'gitignore'), path.join(targetDir, '.gitignore'));\n\n await replacePlaceholdersInTree(targetDir, projectName);\n\n if (publishIntent === false) {\n await pack.registry.applyPrivateIntent(targetDir);\n }\n\n if (registryUrl && registryUrl !== pack.registry.defaultUrl) {\n await pack.registry.applyRegistryUrl(targetDir, registryUrl);\n }\n}\n\nexport interface ScaffoldDeps {\n runCommand: (command: string, args: string[], cwd: string) => Promise<void>;\n}\n\nasync function defaultRunCommand(command: string, args: string[], cwd: string): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const child = spawn(command, args, { cwd, stdio: 'inherit' });\n child.on('error', reject);\n child.on('close', (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(`Command \"${command} ${args.join(' ')}\" exited with code ${code}`));\n }\n });\n });\n}\n\nconst defaultScaffoldDeps: ScaffoldDeps = { runCommand: defaultRunCommand };\n\nexport async function scaffoldProject(\n options: ScaffoldOptions,\n pack: LanguagePack,\n deps: ScaffoldDeps = defaultScaffoldDeps,\n): Promise<void> {\n await copyTemplate(options, pack);\n\n const { targetDir } = options;\n const manifest = await buildManifest(targetDir, getGeneratorVersion(), pack.id, pack.updateAdapter);\n await writeManifest(targetDir, manifest);\n\n await deps.runCommand('git', ['init'], targetDir);\n await deps.runCommand('git', ['add', '-A'], targetDir);\n await deps.runCommand('git', ['commit', '-m', 'chore: initial scaffold from clispark'], targetDir);\n\n for (const scaffoldCommand of pack.scaffoldCommands) {\n await deps.runCommand(scaffoldCommand.command, scaffoldCommand.args, targetDir);\n }\n}\n","// src/update/manifest.ts\nimport { createHash } from 'node:crypto';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { UserError } from '../errors';\nimport type { UpdateAdapter } from './adapter';\n\nexport interface Manifest {\n generatorVersion: string;\n language: string;\n coreFiles: Record<string, string>;\n coreDependencies: Record<string, string>;\n coreScripts: Record<string, string>;\n coreFields: Record<string, unknown>;\n}\n\nexport function hashContent(content: string): string {\n return createHash('sha256').update(content).digest('hex');\n}\n\nexport async function hashCoreFiles(dir: string, adapter: UpdateAdapter): Promise<Record<string, string>> {\n const entries = await Promise.all(\n adapter.coreFilePaths.map(async (relativePath) => {\n const content = await readFile(path.join(dir, relativePath), 'utf8');\n return [relativePath, hashContent(content)] as const;\n }),\n );\n return Object.fromEntries(entries);\n}\n\nexport async function buildManifest(\n targetDir: string,\n generatorVersion: string,\n language: string,\n adapter: UpdateAdapter,\n): Promise<Manifest> {\n const coreFiles = await hashCoreFiles(targetDir, adapter);\n const manifestFile = await adapter.readManifestFile(targetDir);\n const { coreDependencies, coreScripts, coreFields } = adapter.extractCoreFields(manifestFile);\n return { generatorVersion, language, coreFiles, coreDependencies, coreScripts, coreFields };\n}\n\nexport const MANIFEST_RELATIVE_PATH = path.join('.clispark', 'manifest.json');\n\nexport async function writeManifest(targetDir: string, manifest: Manifest): Promise<void> {\n const manifestPath = path.join(targetDir, MANIFEST_RELATIVE_PATH);\n await mkdir(path.dirname(manifestPath), { recursive: true });\n await writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\\n');\n}\n\nexport async function readManifest(targetDir: string): Promise<Manifest | undefined> {\n try {\n const content = await readFile(path.join(targetDir, MANIFEST_RELATIVE_PATH), 'utf8');\n return JSON.parse(content) as Manifest;\n } catch {\n return undefined;\n }\n}\n\nexport async function requireManifest(targetDir: string): Promise<Manifest> {\n const manifest = await readManifest(targetDir);\n if (!manifest) {\n throw new UserError(\n 'No .clispark/manifest.json found — this project predates update support, or is not a clispark project.',\n );\n }\n return manifest;\n}\n\n/**\n * Finds clispark's own package.json by walking up from this file's location.\n * A fixed relative path (`../package.json`) can't work here: this module's\n * depth below the package root differs between running from source (tests)\n * and running as part of the bundled `dist/cli.js` (tsup flattens everything\n * into one file, so `import.meta.url` no longer reflects the original\n * per-module nesting).\n */\nexport function getGeneratorVersion(): string {\n let dir = path.dirname(fileURLToPath(import.meta.url));\n while (true) {\n const pkgPath = path.join(dir, 'package.json');\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as { name?: string; version: string };\n if (pkg.name === 'clispark') return pkg.version;\n }\n const parentDir = path.dirname(dir);\n if (parentDir === dir) {\n throw new Error(\"Could not locate clispark's own package.json.\");\n }\n dir = parentDir;\n }\n}\n","// src/errors.ts\n\n/** An expected, user-fixable failure (bad input, unmet precondition) — distinct from an unexpected crash. */\nexport class UserError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'UserError';\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport { mkdirSync, readdirSync, statSync, unlinkSync, writeFileSync } from 'node:fs';\nimport path from 'node:path';\nimport envPaths from 'env-paths';\nimport pino, { type Logger } from 'pino';\n\nconst paths = envPaths('clispark', { suffix: '' });\n\n/** Runs fn, silently swallowing any error — for best-effort work that must never abort the caller. */\nexport function safely(fn: () => void): void {\n try {\n fn();\n } catch {\n // best-effort; a failure here must never affect the surrounding operation\n }\n}\n\nexport interface LoggerHandle {\n logger: Logger;\n logFilePath: string;\n}\n\nfunction buildLogFileName(commandName: string): string {\n const timestamp = new Date().toISOString().replaceAll(/[:.]/g, '-');\n const suffix = randomBytes(3).toString('hex');\n return `${commandName}-${timestamp}-${suffix}.log`;\n}\n\nfunction getRetentionDays(): number {\n const parsed = Number(process.env.LOG_RETENTION_DAYS);\n return Number.isFinite(parsed) ? parsed : 14;\n}\n\n// Common secret-shaped field names, redacted at the top level and one level of\n// nesting (`*.<key>`). Edit this list directly if your own logging needs more.\nexport const SENSITIVE_LOG_KEYS = [\n 'password',\n 'secret',\n 'token',\n 'apiKey',\n 'accessToken',\n 'refreshToken',\n 'clientSecret',\n 'authorization',\n];\n\nfunction buildRedactPaths(keys: string[]): string[] {\n return keys.flatMap((key) => [key, `*.${key}`]);\n}\n\nconst SWEEP_MARKER_FILE = '.last-sweep';\nconst SWEEP_THROTTLE_MS = 24 * 60 * 60 * 1000; // once a day is enough given day-granularity retention\n\nfunction sweepOldLogs(logDir: string): void {\n safely(() => {\n const markerPath = path.join(logDir, SWEEP_MARKER_FILE);\n let shouldSweep = true;\n try {\n shouldSweep = Date.now() - statSync(markerPath).mtimeMs >= SWEEP_THROTTLE_MS;\n } catch {\n // no marker yet (first run in this directory) - sweep now\n }\n if (!shouldSweep) return;\n\n const cutoffMs = Date.now() - getRetentionDays() * 24 * 60 * 60 * 1000;\n for (const file of readdirSync(logDir)) {\n if (file === SWEEP_MARKER_FILE) continue;\n const filePath = path.join(logDir, file);\n if (statSync(filePath).mtimeMs < cutoffMs) {\n unlinkSync(filePath);\n }\n }\n writeFileSync(markerPath, '');\n });\n}\n\nexport function createLogger(commandName: string, logDir: string = paths.log): LoggerHandle {\n mkdirSync(logDir, { recursive: true });\n sweepOldLogs(logDir);\n\n const logFilePath = path.join(logDir, buildLogFileName(commandName));\n const fileDestination = pino.destination({ dest: logFilePath, sync: true, mode: 0o600 });\n const destination = process.env.DEBUG\n ? pino.multistream([{ stream: fileDestination }, { stream: process.stdout }])\n : fileDestination;\n const logger = pino({ redact: buildRedactPaths([...SENSITIVE_LOG_KEYS, 'registryUrl']) }, destination);\n\n return { logger, logFilePath };\n}\n\nexport function withLogging(\n commandName: string,\n action: (logger: Logger) => Promise<void>,\n logDir: string = paths.log,\n loggerFactory: typeof createLogger = createLogger,\n): () => Promise<void> {\n return async () => {\n let handle: LoggerHandle;\n try {\n handle = loggerFactory(commandName, logDir);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\\n✖ ${message}`);\n process.exit(1);\n return;\n }\n\n const { logger, logFilePath } = handle;\n safely(() => logger.info({ command: commandName }, 'started'));\n\n try {\n await action(logger);\n safely(() => logger.info({ command: commandName }, 'completed'));\n if (process.env.DEBUG) {\n console.log(`Details: ${logFilePath}`);\n }\n } catch (error) {\n safely(() => logger.error({ command: commandName, err: error }, 'failed'));\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\\n✖ ${message}`);\n console.error(`Details: ${logFilePath}`);\n process.exit(1);\n }\n };\n}\n","// src/update/update.ts\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport spawn from 'cross-spawn';\nimport { applyPlaceholders } from '../scaffold';\nimport { getGeneratorVersion, hashContent, requireManifest, writeManifest, type Manifest } from './manifest';\nimport { reconcileEntry, stringEquals, type FieldOutcome } from './reconcile';\nimport { UserError } from '../errors';\nimport { compareVersions } from './releasenotes';\nimport type { UpdateAdapter } from './adapter';\n\nexport interface UpdateDeps {\n runCommand: (command: string, args: string[], cwd: string) => Promise<void>;\n captureCommand: (command: string, args: string[], cwd: string) => Promise<string>;\n}\n\nasync function defaultRunCommand(command: string, args: string[], cwd: string): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const child = spawn(command, args, { cwd, stdio: 'inherit' });\n child.on('error', reject);\n child.on('close', (code) => {\n if (code === 0) resolve();\n else reject(new Error(`Command \"${command} ${args.join(' ')}\" exited with code ${code}`));\n });\n });\n}\n\nasync function defaultCaptureCommand(command: string, args: string[], cwd: string): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n const child = spawn(command, args, { cwd });\n let stdout = '';\n child.stdout?.on('data', (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n child.on('error', reject);\n child.on('close', (code) => {\n if (code === 0) resolve(stdout);\n else reject(new Error(`Command \"${command} ${args.join(' ')}\" exited with code ${code}`));\n });\n });\n}\n\nconst defaultUpdateDeps: UpdateDeps = { runCommand: defaultRunCommand, captureCommand: defaultCaptureCommand };\n\nexport interface FileOutcomeEntry {\n path: string;\n outcome: 'added' | 'replaced' | 'skipped' | 'no-longer-core';\n}\n\nexport interface UpdateResult {\n status: 'up-to-date' | 'no-changes' | 'updated';\n fromVersion: string;\n toVersion: string;\n files: FileOutcomeEntry[];\n dependencies: FieldOutcome[];\n scripts: FieldOutcome[];\n fields: FieldOutcome[];\n}\n\nexport async function updateProject(\n targetDir: string,\n adapter: UpdateAdapter,\n templateDir: string,\n language: string,\n deps: UpdateDeps = defaultUpdateDeps,\n): Promise<UpdateResult> {\n const status = (await deps.captureCommand('git', ['status', '--porcelain'], targetDir)).trim();\n if (status.length > 0) {\n throw new UserError('Working tree is not clean. Commit or stash your changes before running update.');\n }\n\n const oldManifest = await requireManifest(targetDir);\n const toVersion = getGeneratorVersion();\n const fromVersion = oldManifest.generatorVersion;\n\n if (compareVersions(fromVersion, toVersion) >= 0) {\n return { status: 'up-to-date', fromVersion, toVersion, files: [], dependencies: [], scripts: [], fields: [] };\n }\n\n const currentManifestFile = await adapter.readManifestFile(targetDir);\n const projectName = adapter.readProjectName(currentManifestFile);\n\n const newTemplateRaw = applyPlaceholders(\n await readFile(path.join(templateDir, adapter.manifestFileName), 'utf8'),\n projectName,\n );\n const newTemplateManifestFile = adapter.parseManifestFile(newTemplateRaw);\n\n const files: FileOutcomeEntry[] = [];\n const newCoreFiles: Record<string, string> = {};\n const fileWrites: { targetPath: string; content: string }[] = [];\n\n // Reads + hashes run in parallel; results are then applied in adapter.coreFilePaths\n // order below so files/fileWrites stay deterministic regardless of I/O timing.\n const perFileResults = await Promise.all(\n adapter.coreFilePaths.map(async (relativePath) => {\n const newContent = applyPlaceholders(\n await readFile(path.join(templateDir, adapter.templateSourcePath(relativePath)), 'utf8'),\n projectName,\n );\n const newHash = hashContent(newContent);\n\n let currentHash: string | undefined;\n try {\n currentHash = hashContent(await readFile(path.join(targetDir, relativePath), 'utf8'));\n } catch {\n currentHash = undefined;\n }\n\n const result = reconcileEntry(currentHash, oldManifest.coreFiles[relativePath], newHash, stringEquals);\n return { relativePath, newContent, result };\n }),\n );\n\n for (const { relativePath, newContent, result } of perFileResults) {\n files.push({ path: relativePath, outcome: result.outcome });\n newCoreFiles[relativePath] = result.value;\n\n if (result.outcome === 'added' || result.outcome === 'replaced') {\n fileWrites.push({ targetPath: path.join(targetDir, relativePath), content: newContent });\n }\n }\n\n for (const relativePath of Object.keys(oldManifest.coreFiles)) {\n if (adapter.coreFilePaths.includes(relativePath)) continue;\n try {\n await readFile(path.join(targetDir, relativePath), 'utf8');\n files.push({ path: relativePath, outcome: 'no-longer-core' });\n } catch {\n // already gone locally, nothing to report\n }\n }\n\n const fileMerge = adapter.mergeManifestFile(currentManifestFile, oldManifest, newTemplateManifestFile);\n\n const hasFileChanges = files.some(\n (f) => f.outcome === 'added' || f.outcome === 'replaced' || f.outcome === 'no-longer-core',\n );\n const hasChanges = hasFileChanges || fileMerge.changed;\n\n if (!hasChanges) {\n return {\n status: 'no-changes',\n fromVersion,\n toVersion,\n files,\n dependencies: fileMerge.dependencies,\n scripts: fileMerge.scripts,\n fields: fileMerge.fields,\n };\n }\n\n for (const write of fileWrites) {\n await mkdir(path.dirname(write.targetPath), { recursive: true });\n await writeFile(write.targetPath, write.content);\n }\n\n if (fileMerge.changed) {\n await adapter.writeManifestFile(targetDir, fileMerge.updatedFile);\n }\n\n const newManifest: Manifest = {\n generatorVersion: toVersion,\n language,\n coreFiles: newCoreFiles,\n coreDependencies: fileMerge.coreDependencies,\n coreScripts: fileMerge.coreScripts,\n coreFields: fileMerge.coreFields,\n };\n await writeManifest(targetDir, newManifest);\n\n await deps.runCommand('git', ['add', '-A'], targetDir);\n await deps.runCommand('git', ['commit', '-m', `chore: update clispark core to v${toVersion}`], targetDir);\n\n return {\n status: 'updated',\n fromVersion,\n toVersion,\n files,\n dependencies: fileMerge.dependencies,\n scripts: fileMerge.scripts,\n fields: fileMerge.fields,\n };\n}\n\nexport function formatUpdateSummary(result: UpdateResult): string {\n if (result.status === 'up-to-date') {\n return `Already up to date (v${result.toVersion}).`;\n }\n\n const added = result.files.filter((f) => f.outcome === 'added');\n const replaced = result.files.filter((f) => f.outcome === 'replaced');\n const skipped = result.files.filter((f) => f.outcome === 'skipped');\n const noLongerCore = result.files.filter((f) => f.outcome === 'no-longer-core');\n\n const lines: string[] = [];\n if (result.status === 'no-changes') {\n lines.push(\n noLongerCore.length\n ? `No changes applied: nothing to update since v${result.fromVersion}.`\n : `No changes applied: every core file/field has been modified locally since v${result.fromVersion}.`,\n );\n } else {\n lines.push(`Updated core from v${result.fromVersion} to v${result.toVersion}.`);\n }\n\n if (added.length) lines.push(` New: ${added.map((f) => f.path).join(', ')}`);\n if (replaced.length) lines.push(` Updated: ${replaced.map((f) => f.path).join(', ')}`);\n if (skipped.length) lines.push(` Skipped (locally modified): ${skipped.map((f) => f.path).join(', ')}`);\n if (noLongerCore.length) {\n lines.push(\n ` No longer part of the core, safe to remove manually: ${noLongerCore.map((f) => f.path).join(', ')}`,\n );\n }\n\n const fieldOutcomes = [...result.dependencies, ...result.scripts, ...result.fields].filter(\n (o) => o.outcome !== 'skipped',\n );\n if (fieldOutcomes.length) {\n lines.push(` Manifest: ${fieldOutcomes.map((o) => `${o.key} (${o.outcome})`).join(', ')}`);\n }\n\n if (result.status === 'updated') {\n lines.push('Run \"clispark releasenotes\" to see what changed.');\n }\n\n return lines.join('\\n');\n}\n","// src/update/releasenotes.ts\nimport { getGeneratorVersion, requireManifest } from './manifest';\n\nexport interface GitHubRelease {\n tag_name: string;\n name: string;\n body: string;\n}\n\nfunction stripV(version: string): string {\n return version.startsWith('v') ? version.slice(1) : version;\n}\n\nexport function compareVersions(a: string, b: string): number {\n const partsA = stripV(a).split('.').map(Number);\n const partsB = stripV(b).split('.').map(Number);\n const length = Math.max(partsA.length, partsB.length);\n for (let i = 0; i < length; i++) {\n const diff = (partsA[i] ?? 0) - (partsB[i] ?? 0);\n if (diff !== 0) return diff;\n }\n return 0;\n}\n\nexport interface ReleaseNotesResult {\n status: 'up-to-date' | 'releases-found';\n fromVersion: string;\n toVersion: string;\n releases: GitHubRelease[];\n}\n\nconst RELEASES_URL = 'https://api.github.com/repos/martinwichner/clispark/releases';\nconst FETCH_TIMEOUT_MS = 5000;\n\nexport async function fetchReleaseNotes(\n targetDir: string,\n fetchFn: typeof fetch = fetch,\n): Promise<ReleaseNotesResult> {\n const manifest = await requireManifest(targetDir);\n const fromVersion = manifest.generatorVersion;\n const toVersion = getGeneratorVersion();\n\n if (compareVersions(fromVersion, toVersion) >= 0) {\n return { status: 'up-to-date', fromVersion, toVersion, releases: [] };\n }\n\n const response = await fetchFn(RELEASES_URL, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });\n if (!response.ok) {\n throw new Error(`Failed to fetch release notes: GitHub API responded with ${response.status}`);\n }\n const allReleases = (await response.json()) as GitHubRelease[];\n\n const releases = allReleases\n .filter((r) => compareVersions(r.tag_name, fromVersion) > 0 && compareVersions(r.tag_name, toVersion) <= 0)\n .sort((a, b) => compareVersions(b.tag_name, a.tag_name));\n\n return { status: 'releases-found', fromVersion, toVersion, releases };\n}\n\nexport function formatReleaseNotes(result: ReleaseNotesResult): string {\n if (result.status === 'up-to-date') {\n return `You're on the latest clispark version (v${result.toVersion}), nothing to show.`;\n }\n if (result.releases.length === 0) {\n return `No published releases found between v${result.fromVersion} and v${result.toVersion}.`;\n }\n return result.releases.map((r) => `## ${r.tag_name}\\n\\n${r.body}`).join('\\n\\n');\n}\n","// src/whoami.ts\nimport os from 'node:os';\n\nconst JOKE_API_URL = 'https://v2.jokeapi.dev/joke/Programming,Miscellaneous';\nconst FETCH_TIMEOUT_MS = 3000;\n\n/** Languages JokeAPI (https://jokeapi.dev/) currently supports, per its /languages endpoint. */\nconst SUPPORTED_JOKE_LANGUAGES = ['cs', 'de', 'en', 'es', 'fr', 'pt'];\n\nexport const LOGO = String.raw`\n ⚡ clispark\n`;\n\nexport const FALLBACK_QUOTES = [\n \"Green unit tests don't mean it works. Real end-to-end verification does.\",\n 'A default value can silently reintroduce coupling you just removed.',\n \"The bug you can't find locally is waiting for you in CI.\",\n \"YAGNI until you actually need it — then it's just an adapter away.\",\n 'Every folder move has a bundling-depth bug waiting to happen.',\n 'The cheapest fix is the one you verify before you ship it.',\n 'TDD: write the failing test first, or explain later why it passed by accident.',\n \"If it's not tested against a real filesystem, it's a theory, not a fact.\",\n];\n\ninterface JokeApiSingle {\n error: false;\n type: 'single';\n joke: string;\n}\n\ninterface JokeApiTwoPart {\n error: false;\n type: 'twopart';\n setup: string;\n delivery: string;\n}\n\ninterface JokeApiError {\n error: true;\n}\n\ntype JokeApiResponse = JokeApiSingle | JokeApiTwoPart | JokeApiError;\n\n/** Maps a BCP-47 locale (e.g. \"de-DE\") to a JokeAPI language code, defaulting to \"en\" when unsupported. */\nexport function detectJokeLanguage(\n locale: string = Intl.DateTimeFormat().resolvedOptions().locale,\n): string {\n const primary = locale.split('-')[0].toLowerCase();\n return SUPPORTED_JOKE_LANGUAGES.includes(primary) ? primary : 'en';\n}\n\nfunction pickFallbackQuote(): string {\n return FALLBACK_QUOTES[Math.floor(Math.random() * FALLBACK_QUOTES.length)];\n}\n\nexport interface OsFacts {\n uptime: () => number;\n hostname: () => string;\n cpus: () => os.CpuInfo[];\n totalmem: () => number;\n platform: () => NodeJS.Platform;\n release: () => string;\n arch: () => string;\n}\n\nconst defaultOsFacts: OsFacts = {\n uptime: () => os.uptime(),\n hostname: () => os.hostname(),\n cpus: () => os.cpus(),\n totalmem: () => os.totalmem(),\n platform: () => os.platform(),\n release: () => os.release(),\n arch: () => os.arch(),\n};\n\nfunction formatUptime(seconds: number): string {\n const days = Math.floor(seconds / 86400);\n const hours = Math.floor((seconds % 86400) / 3600);\n const minutes = Math.floor((seconds % 3600) / 60);\n const parts: string[] = [];\n if (days > 0) parts.push(`${days}d`);\n if (days > 0 || hours > 0) parts.push(`${hours}h`);\n parts.push(`${minutes}m`);\n return parts.join(' ');\n}\n\nconst FACT_GENERATORS: Array<(facts: OsFacts) => string> = [\n (facts) => `This machine has been up for ${formatUptime(facts.uptime())}.`,\n (facts) => `Hostname: ${facts.hostname()}`,\n (facts) => {\n const cpus = facts.cpus();\n return `CPU: ${cpus[0]?.model.trim() ?? 'unknown'} (${cpus.length} logical cores)`;\n },\n (facts) => `RAM: ${(facts.totalmem() / 1024 ** 3).toFixed(1)} GB total`,\n (facts) => `OS: ${facts.platform()} ${facts.release()} (${facts.arch()})`,\n];\n\nexport function getRandomFunFact(osFacts: OsFacts = defaultOsFacts, randomFn: () => number = Math.random): string {\n const index = Math.floor(randomFn() * FACT_GENERATORS.length);\n return FACT_GENERATORS[index](osFacts);\n}\n\nasync function fetchJoke(language: string, fetchFn: typeof fetch): Promise<string | undefined> {\n try {\n const url = `${JOKE_API_URL}?lang=${language}&safe-mode`;\n const response = await fetchFn(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });\n if (!response.ok) return undefined;\n\n const data = (await response.json()) as JokeApiResponse;\n if (data.error) return undefined;\n return data.type === 'single' ? data.joke : `${data.setup}\\n${data.delivery}`;\n } catch {\n return undefined;\n }\n}\n\nexport type WhoamiMode = 'joke' | 'fact' | 'random';\n\nexport async function getWhoamiOutput(\n fetchFn: typeof fetch = fetch,\n osFacts: OsFacts = defaultOsFacts,\n randomFn: () => number = Math.random,\n mode: WhoamiMode = 'random',\n): Promise<string> {\n const showFact = mode === 'fact' || (mode === 'random' && randomFn() < 0.5);\n if (showFact) {\n return `${LOGO}\\n${getRandomFunFact(osFacts, randomFn)}\\n`;\n }\n\n const language = detectJokeLanguage();\n const quote = (await fetchJoke(language, fetchFn)) ?? pickFallbackQuote();\n return `${LOGO}\\n${quote}\\n`;\n}\n","// src/confetti.ts\n\nconst CONFETTI_VARIANTS = [\n '✨ 🎉 ✨ 🎊 ✨ 🎉 ✨\\n 🎈 🎊 🎈\\n✨ 🎉 ✨ 🎉',\n '🎊 ✨ 🎈 ✨ 🎊\\n 🎉 🎉 🎉\\n✨ 🎈 ✨ 🎈 ✨',\n '🎉✨🎊✨🎉✨🎊✨🎉\\n 🎈 🎈\\n✨🎊✨🎉✨🎊✨🎉✨',\n];\n\nexport function getConfetti(randomFn: () => number = Math.random): string {\n const index = Math.floor(randomFn() * CONFETTI_VARIANTS.length);\n return CONFETTI_VARIANTS[index];\n}\n\nexport function printConfetti(randomFn: () => number = Math.random): void {\n console.log(`\\n${getConfetti(randomFn)}\\n`);\n}\n"],"mappings":"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","names":["path","path","path","path","readFile","writeFile","path","path","path","readFile","writeFile","path","CORE_FILE_PATHS","extractCoreFields","readFile","path","writeFile","readFile","writeFile","path","FETCH_TIMEOUT_MS","checkNameAvailability","applyPrivateIntent","applyRegistryUrl","validateProjectName","path","pack","readFile","writeFile","path","existsSync","readFileSync","mkdir","readFile","writeFile","path","fileURLToPath","readFile","path","mkdir","writeFile","fileURLToPath","existsSync","readFileSync","path","readFile","writeFile","path","mkdir","readFile","writeFile","path","spawn","FETCH_TIMEOUT_MS","defaultRunCommand","spawn","readFile","path","mkdir","writeFile","FETCH_TIMEOUT_MS","path"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clispark",
3
- "version": "1.14.0",
3
+ "version": "1.15.0",
4
4
  "description": "Interactive scaffolding tool for new CLI projects",
5
5
  "keywords": [
6
6
  "cli",
@@ -0,0 +1,77 @@
1
+ # {{projectName}} Architecture
2
+
3
+ This document explains the conventions this project was generated with, so the automatic behavior (command discovery, logging, error handling) doesn't feel like unexplained magic.
4
+
5
+ ## Commands
6
+
7
+ Every command lives in `src/Commands/` and implements `ICliCommand` (`src/ICliCommand.cs`):
8
+
9
+ ```csharp
10
+ using System.CommandLine;
11
+
12
+ namespace Cli.Commands;
13
+
14
+ [CommandPath("my-command")]
15
+ public sealed class MyCommand : ICliCommand
16
+ {
17
+ public Command Build()
18
+ {
19
+ var command = new Command("my-command", "What this command does");
20
+ command.SetAction(parseResult => Console.WriteLine("done"));
21
+ return command;
22
+ }
23
+ }
24
+ ```
25
+
26
+ `[CommandPath("...")]` declares the full, space-separated invocation path — for a nested subcommand this is `"task list"`, not just `"list"`. This is what expresses nesting, **not** the folder a file lives in under `src/Commands/` (unlike the Node template, where a subcommand's folder location *is* its path) — `CommandDiscovery` finds commands via reflection over the whole assembly, not by scanning the filesystem, so there's no requirement to mirror command paths in folders.
27
+
28
+ ## Argument Types
29
+
30
+ Every entry in a command's argument list is a `System.CommandLine.Argument<T>`. `TaskCommand`/`TaskListCommand`/`TaskCompleteCommand` use a few of these:
31
+
32
+ - **`Argument<string>`** — plain text, required by default.
33
+ ```csharp
34
+ var nameArgument = new Argument<string>("name");
35
+ ```
36
+ - **`Argument<int>`** — parses digits into a real `int`, rejects non-numeric input.
37
+ ```csharp
38
+ var idArgument = new Argument<int>("id");
39
+ // `task complete abc` → "Cannot parse argument 'abc' for command 'complete' as expected type 'System.Int32'."
40
+ ```
41
+ - **`Argument<bool?>`** with `Arity = ArgumentArity.ZeroOrOne` — optional boolean.
42
+ ```csharp
43
+ var allArgument = new Argument<bool?>("all") { Arity = ArgumentArity.ZeroOrOne };
44
+ ```
45
+ - **`.AcceptOnlyFromAmong(...)`** — restricts a string argument to a fixed list of values.
46
+ ```csharp
47
+ statusArgument.AcceptOnlyFromAmong("open", "done");
48
+ // `task bogus` → "Das Argument 'bogus' wurde nicht erkannt. Folgendes ist erforderlich: 'open' / 'done'"
49
+ ```
50
+ - **`Arity = ArgumentArity.ZeroOrOne`** on any `Argument<T?>` — makes an otherwise-required argument optional; omit it for a required argument.
51
+
52
+ See `TaskCommand.cs` (string + allowed-values), `TaskCompleteCommand.cs` (integer), and `TaskListCommand.cs` (string + boolean, nested subcommand) for these in a real, runnable command.
53
+
54
+ ## Command Discovery
55
+
56
+ `Program.cs` calls `CommandDiscovery.RegisterAll(root, Assembly.GetExecutingAssembly())` at startup, which reflects over the assembly for every `ICliCommand` implementation, reads its `[CommandPath]`, and attaches it to the command tree — creating bare container commands automatically for any missing intermediate path segments (e.g. a bare `task` node is created automatically if only `"task list"` were declared and no class declared `"task"` itself). Dropping a new file in `src/Commands/` and rebuilding is enough; nothing needs to be manually registered.
57
+
58
+ ## Error Handling
59
+
60
+ There's no base-class inheritance model here (unlike the Node template's `BaseCommand`) — `System.CommandLine` doesn't have an equivalent lifecycle to hook into. Instead, `Program.cs` wraps the whole invocation in a single `try`/`catch`: a thrown `CliUserException` (`src/CliUserException.cs`) produces a clean `Error: <message>` with no stack trace and exit code 1; anything else is not caught here and surfaces with a full stack trace (still logged in full either way — see Logging below).
61
+
62
+ **Important `System.CommandLine` detail:** `ParseResult.Invoke()` has a default exception handler that silently swallows exceptions from a command's action and prints its own message — `Program.cs` disables it explicitly (`new InvocationConfiguration { EnableDefaultExceptionHandler = false }`) so `CliUserException` actually reaches the `catch` block. If you write a test that calls `.Invoke()` directly on a command's `Build()` output to assert on a thrown exception, you need the same `InvocationConfiguration` — see `TaskCompleteCommandTests.cs`.
63
+
64
+ ## Logging
65
+
66
+ `src/Logging/CliLoggerFactory.cs` writes structured logs via Serilog, one file per command invocation, to an OS-appropriate log directory (via `Xdg.Directories`, respecting `$XDG_STATE_HOME` on Linux) — never to the project's own working directory. `Program.cs` assigns the created logger to Serilog's static `Log.Logger`, so any command can log via `Serilog.Log.Information(...)` etc. without an injected instance. On failure, the full exception (including stack trace) is always logged to the file, while the terminal only ever shows a clean `Error: <message>`; the terminal also prints `Details: <path to the log file>` so the full error is one file away.
67
+
68
+ A few things run automatically around every log call:
69
+
70
+ - **Retention:** before opening a new log file, a sweep deletes files older than 14 days — throttled to run at most once per 24 hours via a `.last-sweep` marker file in the log directory, so a busy CLI doesn't re-scan the log directory on every single invocation.
71
+ - **Redaction:** `SensitivePropertyEnricher` masks known secret-shaped property names (`password`, `secret`, `token`, `apiKey`, `registryUrl`) case-insensitively before they reach any sink — edit `SensitiveKeys` in `CliLoggerFactory.cs` directly if your commands log other sensitive fields.
72
+ - **`DEBUG=1`:** streams every log line to stdout live in addition to the file, and prints `Details: <path>` on success too, not just on failure — useful while developing a new command.
73
+ - **File permissions:** the log file itself is created with `UnixFileMode.UserRead | UnixFileMode.UserWrite` (owner read/write only) on non-Windows platforms; this is a documented no-op on Windows, which has no POSIX permission bits — same principle as the Node template's `mode: 0o600`.
74
+
75
+ ## Testing
76
+
77
+ Tests use `xunit` and live in `tests/Cli.Tests.csproj` (a separate project referencing `src/Cli.csproj`, not colocated with the commands they test — the conventional .NET layout, unlike vitest's colocated `*.test.ts` files). A test exercises a command directly via `new SomeCommand().Build()` then `.Parse(args).Invoke(...)` — no process spawning needed. Remember the `EnableDefaultExceptionHandler = false` detail from "Error Handling" above when asserting on thrown exceptions.
@@ -0,0 +1,8 @@
1
+ <Solution>
2
+ <Folder Name="/src/">
3
+ <Project Path="src/Cli.csproj" />
4
+ </Folder>
5
+ <Folder Name="/tests/">
6
+ <Project Path="tests/Cli.Tests.csproj" />
7
+ </Folder>
8
+ </Solution>
@@ -0,0 +1,45 @@
1
+ # {{projectName}}
2
+
3
+ Generated with [clispark](https://github.com/martinwichner/clispark).
4
+
5
+ ## Requirements
6
+
7
+ .NET SDK **10.0** or later.
8
+
9
+ ## Building and running
10
+
11
+ ```bash
12
+ dotnet build Cli.slnx
13
+ dotnet run --project src -- hello World
14
+ ```
15
+
16
+ Or install it as a global tool on your own machine:
17
+
18
+ ```bash
19
+ dotnet pack src -c Release -o ./nupkg
20
+ dotnet tool install -g {{projectName}} --add-source ./nupkg
21
+ {{projectName}} hello World
22
+ ```
23
+
24
+ ## Example commands
25
+
26
+ Four example commands ship in `src/Commands/` as copy-paste starting points:
27
+
28
+ - **`hello`** (`src/Commands/HelloCommand.cs`) — the minimal case: one required string argument.
29
+ ```bash
30
+ dotnet run --project src -- hello World
31
+ ```
32
+ - **`task`** / **`task list`** / **`task complete`** (`src/Commands/TaskCommand.cs`, `TaskListCommand.cs`, `TaskCompleteCommand.cs`) — a reference for `System.CommandLine`'s argument and subcommand patterns: an optional allowed-values argument, a nested subcommand with a string + boolean pair, and a required integer argument that demonstrates the `CliUserException` error path.
33
+ ```bash
34
+ dotnet run --project src -- task open
35
+ dotnet run --project src -- task list groceries true
36
+ dotnet run --project src -- task complete 1
37
+ ```
38
+ See `ARCHITECTURE.md`'s "Argument Types" section for details.
39
+
40
+ ## Logging & debugging
41
+
42
+ Every command run writes a structured log file (one per invocation, in an OS-appropriate log directory — see `ARCHITECTURE.md`'s "Logging" section). By default the terminal only shows a clean `Error: <message>` on failure, or nothing on success.
43
+
44
+ - **`DEBUG=1`** — streams the raw log lines to stdout live as the command runs, and prints `Details: <path>` to the log file on both success and failure (normally that line only appears on failure).
45
+ - Fields that look like secrets (`password`, `token`, `apiKey`, etc. — see `SensitiveKeys` in `src/Logging/CliLoggerFactory.cs`) are redacted from log output automatically; edit that list directly in your own copy if you log other sensitive fields.
@@ -0,0 +1,4 @@
1
+ bin/
2
+ obj/
3
+ *.user
4
+ .vs/
@@ -0,0 +1,24 @@
1
+ <Project Sdk="Microsoft.NET.Sdk">
2
+
3
+ <PropertyGroup>
4
+ <OutputType>Exe</OutputType>
5
+ <TargetFramework>net10.0</TargetFramework>
6
+ <ImplicitUsings>enable</ImplicitUsings>
7
+ <Nullable>enable</Nullable>
8
+ <RootNamespace>Cli</RootNamespace>
9
+ <AssemblyName>{{projectName}}</AssemblyName>
10
+ <Version>0.0.0</Version>
11
+ <PackAsTool>true</PackAsTool>
12
+ <ToolCommandName>{{projectName}}</ToolCommandName>
13
+ <PackageId>{{projectName}}</PackageId>
14
+ </PropertyGroup>
15
+
16
+ <ItemGroup>
17
+ <PackageReference Include="System.CommandLine" Version="2.0.10" />
18
+ <PackageReference Include="Serilog" Version="4.4.0" />
19
+ <PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
20
+ <PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" />
21
+ <PackageReference Include="Xdg.Directories" Version="0.1.2" />
22
+ </ItemGroup>
23
+
24
+ </Project>
@@ -0,0 +1,4 @@
1
+ namespace Cli;
2
+
3
+ /// <summary>An expected, user-fixable failure — distinct from an unexpected crash. Mirrors clispark's own UserError.</summary>
4
+ public sealed class CliUserException(string message) : Exception(message);