@fragno-dev/cli 0.1.13 → 0.1.15

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,"file":"cli.js","names":["allDatabases: FragnoDatabase<AnySchema>[]","adapter: DatabaseAdapter | undefined","firstAdapterFile: string | undefined","fragnoDatabases: FragnoDatabase<AnySchema>[]","results: { schema: string; path: string; namespace: string }[]","results: ExecuteMigrationResult[]","info: {\n namespace: string;\n schemaVersion: number;\n migrationSupport: boolean;\n currentVersion?: number;\n pendingVersions?: number;\n status?: string;\n error?: string;\n }"],"sources":["../src/utils/find-fragno-databases.ts","../src/commands/db/generate.ts","../src/commands/db/migrate.ts","../src/commands/db/info.ts","../src/cli.ts"],"sourcesContent":["import { isFragnoDatabase, type DatabaseAdapter, FragnoDatabase } from \"@fragno-dev/db\";\nimport {\n fragnoDatabaseAdapterNameFakeSymbol,\n fragnoDatabaseAdapterVersionFakeSymbol,\n} from \"@fragno-dev/db/adapters\";\nimport type { AnySchema } from \"@fragno-dev/db/schema\";\nimport {\n instantiatedFragmentFakeSymbol,\n type FragnoInstantiatedFragment,\n} from \"@fragno-dev/core/api/fragment-instantiation\";\nimport { loadConfig } from \"c12\";\nimport { relative } from \"node:path\";\n\nexport async function importFragmentFile(path: string): Promise<Record<string, unknown>> {\n const { config } = await loadConfig({\n configFile: path,\n });\n\n const databases = findFragnoDatabases(config);\n const adapterNames = databases.map(\n (db) =>\n `${db.adapter[fragnoDatabaseAdapterNameFakeSymbol]}@${db.adapter[fragnoDatabaseAdapterVersionFakeSymbol]}`,\n );\n const uniqueAdapterNames = [...new Set(adapterNames)];\n\n if (uniqueAdapterNames.length > 1) {\n throw new Error(\n `All Fragno databases must use the same adapter name and version. ` +\n `Found mismatch: (${adapterNames.join(\", \")})`,\n );\n }\n\n return {\n adapter: databases[0].adapter,\n databases,\n };\n}\n\n/**\n * Imports multiple fragment files and validates they all use the same adapter.\n * Returns the combined databases from all files.\n */\nexport async function importFragmentFiles(paths: string[]): Promise<{\n adapter: DatabaseAdapter;\n databases: FragnoDatabase<AnySchema>[];\n}> {\n // De-duplicate paths (in case same file was specified multiple times)\n const uniquePaths = Array.from(new Set(paths));\n\n if (uniquePaths.length === 0) {\n throw new Error(\"No fragment files provided\");\n }\n\n const allDatabases: FragnoDatabase<AnySchema>[] = [];\n let adapter: DatabaseAdapter | undefined;\n let firstAdapterFile: string | undefined;\n const cwd = process.cwd();\n\n for (const path of uniquePaths) {\n const relativePath = relative(cwd, path);\n\n try {\n const result = await importFragmentFile(path);\n const databases = result[\"databases\"] as FragnoDatabase<AnySchema>[];\n const fileAdapter = result[\"adapter\"] as DatabaseAdapter;\n\n if (databases.length === 0) {\n console.warn(\n `Warning: No FragnoDatabase instances found in ${relativePath}.\\n` +\n `Make sure you export either:\\n` +\n ` - A FragnoDatabase instance created with .create(adapter)\\n` +\n ` - An instantiated fragment with embedded database definition\\n`,\n );\n continue;\n }\n\n // Set the adapter from the first file with databases\n if (!adapter) {\n adapter = fileAdapter;\n firstAdapterFile = relativePath;\n }\n\n // Validate all files use the same adapter name and version\n const firstAdapterName = adapter[fragnoDatabaseAdapterNameFakeSymbol];\n const firstAdapterVersion = adapter[fragnoDatabaseAdapterVersionFakeSymbol];\n const fileAdapterName = fileAdapter[fragnoDatabaseAdapterNameFakeSymbol];\n const fileAdapterVersion = fileAdapter[fragnoDatabaseAdapterVersionFakeSymbol];\n\n if (firstAdapterName !== fileAdapterName || firstAdapterVersion !== fileAdapterVersion) {\n const firstAdapterInfo = `${firstAdapterName}@${firstAdapterVersion}`;\n const fileAdapterInfo = `${fileAdapterName}@${fileAdapterVersion}`;\n\n throw new Error(\n `All fragments must use the same database adapter. Mixed adapters found:\\n` +\n ` - ${firstAdapterFile}: ${firstAdapterInfo}\\n` +\n ` - ${relativePath}: ${fileAdapterInfo}\\n\\n` +\n `Make sure all fragments use the same adapter name and version.`,\n );\n }\n\n allDatabases.push(...databases);\n console.log(` Found ${databases.length} database(s) in ${relativePath}`);\n } catch (error) {\n throw new Error(\n `Failed to import fragment file ${relativePath}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n if (allDatabases.length === 0) {\n throw new Error(\n `No FragnoDatabase instances found in any of the target files.\\n` +\n `Make sure your files export either:\\n` +\n ` - A FragnoDatabase instance created with .create(adapter)\\n` +\n ` - An instantiated fragment with embedded database definition\\n`,\n );\n }\n\n if (!adapter) {\n throw new Error(\"No adapter found in any of the fragment files\");\n }\n\n return {\n adapter,\n databases: allDatabases,\n };\n}\n\nfunction isFragnoInstantiatedFragment(\n value: unknown,\n): value is FragnoInstantiatedFragment<[], {}, {}, {}> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n instantiatedFragmentFakeSymbol in value &&\n value[instantiatedFragmentFakeSymbol] === instantiatedFragmentFakeSymbol\n );\n}\n\nfunction additionalContextIsDatabaseContext(additionalContext: unknown): additionalContext is {\n databaseSchema: AnySchema;\n databaseNamespace: string;\n databaseAdapter: DatabaseAdapter;\n} {\n return (\n typeof additionalContext === \"object\" &&\n additionalContext !== null &&\n \"databaseSchema\" in additionalContext &&\n \"databaseNamespace\" in additionalContext &&\n \"databaseAdapter\" in additionalContext\n );\n}\n\n/**\n * Finds all FragnoDatabase instances in a module, including those embedded\n * in instantiated fragments.\n */\nexport function findFragnoDatabases(\n targetModule: Record<string, unknown>,\n): FragnoDatabase<AnySchema>[] {\n const fragnoDatabases: FragnoDatabase<AnySchema>[] = [];\n\n for (const [_key, value] of Object.entries(targetModule)) {\n if (isFragnoDatabase(value)) {\n fragnoDatabases.push(value);\n } else if (isFragnoInstantiatedFragment(value)) {\n const additionalContext = value.additionalContext;\n\n if (!additionalContext || !additionalContextIsDatabaseContext(additionalContext)) {\n continue;\n }\n\n // Extract database schema, namespace, and adapter from instantiated fragment's additionalContext\n const { databaseSchema, databaseNamespace, databaseAdapter } = additionalContext;\n\n fragnoDatabases.push(\n new FragnoDatabase({\n namespace: databaseNamespace,\n schema: databaseSchema,\n adapter: databaseAdapter,\n }),\n );\n }\n }\n\n return fragnoDatabases;\n}\n","import { writeFile, mkdir } from \"node:fs/promises\";\nimport { resolve, dirname } from \"node:path\";\nimport { define } from \"gunshi\";\nimport { generateMigrationsOrSchema } from \"@fragno-dev/db/generation-engine\";\nimport { importFragmentFiles } from \"../../utils/find-fragno-databases\";\n\n// Define the db generate command with type safety\nexport const generateCommand = define({\n name: \"generate\",\n description: \"Generate schema files from FragnoDatabase definitions\",\n args: {\n output: {\n type: \"string\",\n short: \"o\",\n description:\n \"Output path: for single file, exact file path; for multiple files, output directory (default: current directory)\",\n },\n from: {\n type: \"number\",\n short: \"f\",\n description: \"Source version to generate migration from (default: current database version)\",\n },\n to: {\n type: \"number\",\n short: \"t\",\n description: \"Target version to generate migration to (default: latest schema version)\",\n },\n prefix: {\n type: \"string\",\n short: \"p\",\n description: \"String to prepend to the generated file (e.g., '/* eslint-disable */')\",\n },\n },\n run: async (ctx) => {\n // With `define()` and `multiple: true`, targets is properly typed as string[]\n const targets = ctx.positionals;\n const output = ctx.values.output;\n const toVersion = ctx.values.to;\n const fromVersion = ctx.values.from;\n const prefix = ctx.values.prefix;\n\n // Resolve all target paths\n const targetPaths = targets.map((target) => resolve(process.cwd(), target));\n\n // Import all fragment files and validate they use the same adapter\n const { databases: allFragnoDatabases, adapter } = await importFragmentFiles(targetPaths);\n\n // Check if adapter supports any form of schema generation\n if (!adapter.createSchemaGenerator && !adapter.createMigrationEngine) {\n throw new Error(\n `The adapter does not support schema generation. ` +\n `Please use an adapter that implements either createSchemaGenerator or createMigrationEngine.`,\n );\n }\n\n // Generate schema for all fragments\n console.log(\"Generating schema...\");\n\n let results: { schema: string; path: string; namespace: string }[];\n try {\n results = await generateMigrationsOrSchema(allFragnoDatabases, {\n path: output,\n toVersion,\n fromVersion,\n });\n } catch (error) {\n throw new Error(\n `Failed to generate schema: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n // Write all generated files\n for (const result of results) {\n // For single file: use output as exact file path\n // For multiple files: use output as base directory\n const finalOutputPath =\n output && results.length === 1\n ? resolve(process.cwd(), output)\n : output\n ? resolve(process.cwd(), output, result.path)\n : resolve(process.cwd(), result.path);\n\n // Ensure parent directory exists\n const parentDir = dirname(finalOutputPath);\n try {\n await mkdir(parentDir, { recursive: true });\n } catch (error) {\n throw new Error(\n `Failed to create directory: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n // Write schema to file\n try {\n const content = prefix ? `${prefix}\\n${result.schema}` : result.schema;\n await writeFile(finalOutputPath, content, { encoding: \"utf-8\" });\n } catch (error) {\n throw new Error(\n `Failed to write schema file: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n console.log(`✓ Generated: ${finalOutputPath}`);\n }\n\n console.log(`\\n✓ Schema generated successfully!`);\n console.log(` Files generated: ${results.length}`);\n console.log(` Fragments:`);\n for (const db of allFragnoDatabases) {\n console.log(` - ${db.namespace} (version ${db.schema.version})`);\n }\n },\n});\n","import { resolve } from \"node:path\";\nimport { define } from \"gunshi\";\nimport { importFragmentFiles } from \"../../utils/find-fragno-databases\";\nimport { executeMigrations, type ExecuteMigrationResult } from \"@fragno-dev/db/generation-engine\";\n\nexport const migrateCommand = define({\n name: \"migrate\",\n description: \"Run database migrations for all fragments to their latest versions\",\n args: {},\n run: async (ctx) => {\n const targets = ctx.positionals;\n\n if (targets.length === 0) {\n throw new Error(\"At least one target file path is required\");\n }\n\n // Resolve all target paths\n const targetPaths = targets.map((target) => resolve(process.cwd(), target));\n\n // Import all fragment files and validate they use the same adapter\n const { databases: allFragnoDatabases } = await importFragmentFiles(targetPaths);\n\n console.log(\"\\nMigrating all fragments to their latest versions...\\n\");\n\n let results: ExecuteMigrationResult[];\n try {\n results = await executeMigrations(allFragnoDatabases);\n } catch (error) {\n throw new Error(\n `Migration failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n // Display progress for each result\n for (const result of results) {\n console.log(`Fragment: ${result.namespace}`);\n console.log(` Current version: ${result.fromVersion}`);\n console.log(` Target version: ${result.toVersion}`);\n\n if (result.didMigrate) {\n console.log(` ✓ Migration completed: v${result.fromVersion} → v${result.toVersion}\\n`);\n } else {\n console.log(` ✓ Already at latest version. No migration needed.\\n`);\n }\n }\n\n // Summary\n console.log(\"═══════════════════════════════════════\");\n console.log(\"Migration Summary\");\n console.log(\"═══════════════════════════════════════\");\n\n const migrated = results.filter((r) => r.didMigrate);\n const skipped = results.filter((r) => !r.didMigrate);\n\n if (migrated.length > 0) {\n console.log(`\\n✓ Migrated ${migrated.length} fragment(s):`);\n for (const r of migrated) {\n console.log(` - ${r.namespace}: v${r.fromVersion} → v${r.toVersion}`);\n }\n }\n\n if (skipped.length > 0) {\n console.log(`\\n○ Skipped ${skipped.length} fragment(s) (already up-to-date):`);\n for (const r of skipped) {\n console.log(` - ${r.namespace}: v${r.toVersion}`);\n }\n }\n\n for (const db of allFragnoDatabases) {\n await db.adapter.close();\n }\n\n console.log(\"\\n✓ All migrations completed successfully\");\n },\n});\n","import { resolve } from \"node:path\";\nimport { define } from \"gunshi\";\nimport { importFragmentFiles } from \"../../utils/find-fragno-databases\";\n\nexport const infoCommand = define({\n name: \"info\",\n description: \"Display database information and migration status\",\n args: {},\n run: async (ctx) => {\n const targets = ctx.positionals;\n\n if (targets.length === 0) {\n throw new Error(\"At least one target file path is required\");\n }\n\n // Resolve all target paths\n const targetPaths = targets.map((target) => resolve(process.cwd(), target));\n\n // Import all fragment files\n const { databases: allFragnoDatabases } = await importFragmentFiles(targetPaths);\n\n // Collect database information\n const dbInfos = await Promise.all(\n allFragnoDatabases.map(async (fragnoDb) => {\n const info: {\n namespace: string;\n schemaVersion: number;\n migrationSupport: boolean;\n currentVersion?: number;\n pendingVersions?: number;\n status?: string;\n error?: string;\n } = {\n namespace: fragnoDb.namespace,\n schemaVersion: fragnoDb.schema.version,\n migrationSupport: !!fragnoDb.adapter.createMigrationEngine,\n };\n\n // Get current database version if migrations are supported\n if (fragnoDb.adapter.createMigrationEngine) {\n try {\n const migrator = fragnoDb.adapter.createMigrationEngine(\n fragnoDb.schema,\n fragnoDb.namespace,\n );\n const currentVersion = await migrator.getVersion();\n info.currentVersion = currentVersion;\n info.pendingVersions = fragnoDb.schema.version - currentVersion;\n\n if (info.pendingVersions > 0) {\n info.status = `Pending (${info.pendingVersions} migration(s))`;\n } else if (info.pendingVersions === 0) {\n info.status = \"Up to date\";\n }\n } catch (error) {\n info.error = error instanceof Error ? error.message : String(error);\n info.status = \"Error\";\n }\n } else {\n info.status = \"Schema only\";\n }\n\n return info;\n }),\n );\n\n // Determine if any database supports migrations\n const hasMigrationSupport = dbInfos.some((info) => info.migrationSupport);\n\n // Print compact table\n console.log(\"\");\n console.log(`Database Information:`);\n console.log(\"\");\n\n // Table header\n const namespaceHeader = \"Namespace\";\n const versionHeader = \"Schema\";\n const currentHeader = \"Current\";\n const statusHeader = \"Status\";\n\n const maxNamespaceLen = Math.max(\n namespaceHeader.length,\n ...dbInfos.map((info) => info.namespace.length),\n );\n const namespaceWidth = Math.max(maxNamespaceLen + 2, 20);\n const versionWidth = 8;\n const currentWidth = 9;\n const statusWidth = 25;\n\n // Print table\n console.log(\n namespaceHeader.padEnd(namespaceWidth) +\n versionHeader.padEnd(versionWidth) +\n (hasMigrationSupport ? currentHeader.padEnd(currentWidth) : \"\") +\n statusHeader,\n );\n console.log(\n \"-\".repeat(namespaceWidth) +\n \"-\".repeat(versionWidth) +\n (hasMigrationSupport ? \"-\".repeat(currentWidth) : \"\") +\n \"-\".repeat(statusWidth),\n );\n\n for (const info of dbInfos) {\n const currentVersionStr =\n info.currentVersion !== undefined ? String(info.currentVersion) : \"-\";\n console.log(\n info.namespace.padEnd(namespaceWidth) +\n String(info.schemaVersion).padEnd(versionWidth) +\n (hasMigrationSupport ? currentVersionStr.padEnd(currentWidth) : \"\") +\n (info.status || \"-\"),\n );\n }\n\n // Print help text\n console.log(\"\");\n if (!hasMigrationSupport) {\n console.log(\"Note: These adapters do not support migrations.\");\n console.log(\"Use 'fragno-cli db generate' to generate schema files.\");\n } else {\n const hasPendingMigrations = dbInfos.some(\n (info) => info.pendingVersions && info.pendingVersions > 0,\n );\n if (hasPendingMigrations) {\n console.log(\"Run 'fragno-cli db migrate <target>' to apply pending migrations.\");\n }\n }\n },\n});\n","#!/usr/bin/env node\n\nimport { cli, define, parseArgs, resolveArgs } from \"gunshi\";\nimport { generateCommand } from \"./commands/db/generate.js\";\nimport { migrateCommand } from \"./commands/db/migrate.js\";\nimport { infoCommand } from \"./commands/db/info.js\";\n\n// Create a Map of db sub-commands\nconst dbSubCommands = new Map();\ndbSubCommands.set(\"generate\", generateCommand);\ndbSubCommands.set(\"migrate\", migrateCommand);\ndbSubCommands.set(\"info\", infoCommand);\n\n// Helper function to print db command help\nfunction printDbHelp() {\n console.log(\"Database management commands for Fragno\");\n console.log(\"\");\n console.log(\"Usage: fragno-cli db <command> [options]\");\n console.log(\"\");\n console.log(\"Commands:\");\n console.log(\" generate Generate schema files from FragnoDatabase definitions\");\n console.log(\" migrate Run database migrations\");\n console.log(\" info Display database information and migration status\");\n console.log(\"\");\n console.log(\"Run 'fragno-cli db <command> --help' for more information.\");\n}\n\n// Define the db command with type safety\nexport const dbCommand = define({\n name: \"db\",\n description: \"Database management commands\",\n run: printDbHelp,\n});\n\n// Create a Map of root sub-commands\nconst rootSubCommands = new Map();\nrootSubCommands.set(\"db\", dbCommand);\n\n// Define the main command with type safety\nexport const mainCommand = define({\n name: \"fragno-cli\",\n description: \"Fragno CLI - Tools for working with Fragno fragments\",\n run: () => {\n console.log(\"Fragno CLI - Tools for working with Fragno fragments\");\n console.log(\"\");\n console.log(\"Usage: fragno-cli <command> [options]\");\n console.log(\"\");\n console.log(\"Commands:\");\n console.log(\" db Database management commands\");\n console.log(\"\");\n console.log(\"Run 'fragno-cli <command> --help' for more information.\");\n },\n});\n\nif (import.meta.main) {\n try {\n // Parse arguments to handle nested subcommands\n const args = process.argv.slice(2);\n\n // Check if we're calling a db subcommand directly\n if (args[0] === \"db\" && args.length > 1) {\n const subCommandName = args[1];\n\n // Check if it's a help request\n if (subCommandName === \"--help\" || subCommandName === \"-h\") {\n printDbHelp();\n process.exit(0);\n }\n\n const subCommand = dbSubCommands.get(subCommandName);\n\n if (!subCommand) {\n console.error(`Unknown command: ${subCommandName}`);\n console.log(\"\");\n printDbHelp();\n process.exit(1);\n }\n\n // Run the specific subcommand with its args\n const subArgs = args.slice(2);\n const isSubCommandHelp = subArgs.includes(\"--help\") || subArgs.includes(\"-h\");\n\n // Check for validation errors before running\n let hasValidationError = false;\n if (!isSubCommandHelp && subCommand.args) {\n const tokens = parseArgs(subArgs);\n const resolved = resolveArgs(subCommand.args, tokens);\n hasValidationError = !!resolved.error;\n }\n\n // Run the command (let gunshi handle printing errors/help)\n await cli(subArgs, subCommand);\n\n // Exit with error code if there was a validation error\n if (hasValidationError) {\n process.exit(1);\n }\n } else if (args[0] === \"db\") {\n // \"db\" command with no subcommand - show db help\n printDbHelp();\n } else {\n // Run the main CLI\n await cli(args, mainCommand, {\n subCommands: rootSubCommands,\n });\n }\n } catch (error) {\n console.error(\"Error:\", error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n\nexport { generateCommand, migrateCommand, infoCommand };\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","names":["allDatabases: FragnoDatabase<AnySchema>[]","adapter: DatabaseAdapter | undefined","firstAdapterFile: string | undefined","fragnoDatabases: FragnoDatabase<AnySchema>[]","results: { schema: string; path: string; namespace: string }[]","results: ExecuteMigrationResult[]","info: {\n namespace: string;\n schemaVersion: number;\n migrationSupport: boolean;\n currentVersion?: number;\n pendingVersions?: number;\n status?: string;\n error?: string;\n }","lines: string[]","matches: CodeBlockMatch[]","startLine: number | undefined","endLine: number | undefined","rootSubjects: Array<{\n id: string;\n title: string;\n children: Array<{ id: string; title: string }>;\n }>"],"sources":["../src/utils/find-fragno-databases.ts","../src/commands/db/generate.ts","../src/commands/db/migrate.ts","../src/commands/db/info.ts","../src/utils/format-search-results.ts","../src/commands/search.ts","../src/commands/corpus.ts","../src/cli.ts"],"sourcesContent":["import { isFragnoDatabase, type DatabaseAdapter, FragnoDatabase } from \"@fragno-dev/db\";\nimport {\n fragnoDatabaseAdapterNameFakeSymbol,\n fragnoDatabaseAdapterVersionFakeSymbol,\n} from \"@fragno-dev/db/adapters\";\nimport type { AnySchema } from \"@fragno-dev/db/schema\";\nimport {\n instantiatedFragmentFakeSymbol,\n type FragnoInstantiatedFragment,\n} from \"@fragno-dev/core/api/fragment-instantiation\";\nimport { loadConfig } from \"c12\";\nimport { relative } from \"node:path\";\n\nexport async function importFragmentFile(path: string): Promise<Record<string, unknown>> {\n const { config } = await loadConfig({\n configFile: path,\n });\n\n const databases = findFragnoDatabases(config);\n const adapterNames = databases.map(\n (db) =>\n `${db.adapter[fragnoDatabaseAdapterNameFakeSymbol]}@${db.adapter[fragnoDatabaseAdapterVersionFakeSymbol]}`,\n );\n const uniqueAdapterNames = [...new Set(adapterNames)];\n\n if (uniqueAdapterNames.length > 1) {\n throw new Error(\n `All Fragno databases must use the same adapter name and version. ` +\n `Found mismatch: (${adapterNames.join(\", \")})`,\n );\n }\n\n return {\n adapter: databases[0].adapter,\n databases,\n };\n}\n\n/**\n * Imports multiple fragment files and validates they all use the same adapter.\n * Returns the combined databases from all files.\n */\nexport async function importFragmentFiles(paths: string[]): Promise<{\n adapter: DatabaseAdapter;\n databases: FragnoDatabase<AnySchema>[];\n}> {\n // De-duplicate paths (in case same file was specified multiple times)\n const uniquePaths = Array.from(new Set(paths));\n\n if (uniquePaths.length === 0) {\n throw new Error(\"No fragment files provided\");\n }\n\n const allDatabases: FragnoDatabase<AnySchema>[] = [];\n let adapter: DatabaseAdapter | undefined;\n let firstAdapterFile: string | undefined;\n const cwd = process.cwd();\n\n for (const path of uniquePaths) {\n const relativePath = relative(cwd, path);\n\n try {\n const result = await importFragmentFile(path);\n const databases = result[\"databases\"] as FragnoDatabase<AnySchema>[];\n const fileAdapter = result[\"adapter\"] as DatabaseAdapter;\n\n if (databases.length === 0) {\n console.warn(\n `Warning: No FragnoDatabase instances found in ${relativePath}.\\n` +\n `Make sure you export either:\\n` +\n ` - A FragnoDatabase instance created with .create(adapter)\\n` +\n ` - An instantiated fragment with embedded database definition\\n`,\n );\n continue;\n }\n\n // Set the adapter from the first file with databases\n if (!adapter) {\n adapter = fileAdapter;\n firstAdapterFile = relativePath;\n }\n\n // Validate all files use the same adapter name and version\n const firstAdapterName = adapter[fragnoDatabaseAdapterNameFakeSymbol];\n const firstAdapterVersion = adapter[fragnoDatabaseAdapterVersionFakeSymbol];\n const fileAdapterName = fileAdapter[fragnoDatabaseAdapterNameFakeSymbol];\n const fileAdapterVersion = fileAdapter[fragnoDatabaseAdapterVersionFakeSymbol];\n\n if (firstAdapterName !== fileAdapterName || firstAdapterVersion !== fileAdapterVersion) {\n const firstAdapterInfo = `${firstAdapterName}@${firstAdapterVersion}`;\n const fileAdapterInfo = `${fileAdapterName}@${fileAdapterVersion}`;\n\n throw new Error(\n `All fragments must use the same database adapter. Mixed adapters found:\\n` +\n ` - ${firstAdapterFile}: ${firstAdapterInfo}\\n` +\n ` - ${relativePath}: ${fileAdapterInfo}\\n\\n` +\n `Make sure all fragments use the same adapter name and version.`,\n );\n }\n\n allDatabases.push(...databases);\n console.log(` Found ${databases.length} database(s) in ${relativePath}`);\n } catch (error) {\n throw new Error(\n `Failed to import fragment file ${relativePath}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n if (allDatabases.length === 0) {\n throw new Error(\n `No FragnoDatabase instances found in any of the target files.\\n` +\n `Make sure your files export either:\\n` +\n ` - A FragnoDatabase instance created with .create(adapter)\\n` +\n ` - An instantiated fragment with embedded database definition\\n`,\n );\n }\n\n if (!adapter) {\n throw new Error(\"No adapter found in any of the fragment files\");\n }\n\n return {\n adapter,\n databases: allDatabases,\n };\n}\n\nfunction isFragnoInstantiatedFragment(\n value: unknown,\n): value is FragnoInstantiatedFragment<[], {}, {}, {}> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n instantiatedFragmentFakeSymbol in value &&\n value[instantiatedFragmentFakeSymbol] === instantiatedFragmentFakeSymbol\n );\n}\n\nfunction additionalContextIsDatabaseContext(additionalContext: unknown): additionalContext is {\n databaseSchema: AnySchema;\n databaseNamespace: string;\n databaseAdapter: DatabaseAdapter;\n} {\n return (\n typeof additionalContext === \"object\" &&\n additionalContext !== null &&\n \"databaseSchema\" in additionalContext &&\n \"databaseNamespace\" in additionalContext &&\n \"databaseAdapter\" in additionalContext\n );\n}\n\n/**\n * Finds all FragnoDatabase instances in a module, including those embedded\n * in instantiated fragments.\n */\nexport function findFragnoDatabases(\n targetModule: Record<string, unknown>,\n): FragnoDatabase<AnySchema>[] {\n const fragnoDatabases: FragnoDatabase<AnySchema>[] = [];\n\n for (const [_key, value] of Object.entries(targetModule)) {\n if (isFragnoDatabase(value)) {\n fragnoDatabases.push(value);\n } else if (isFragnoInstantiatedFragment(value)) {\n const additionalContext = value.additionalContext;\n\n if (!additionalContext || !additionalContextIsDatabaseContext(additionalContext)) {\n continue;\n }\n\n // Extract database schema, namespace, and adapter from instantiated fragment's additionalContext\n const { databaseSchema, databaseNamespace, databaseAdapter } = additionalContext;\n\n fragnoDatabases.push(\n new FragnoDatabase({\n namespace: databaseNamespace,\n schema: databaseSchema,\n adapter: databaseAdapter,\n }),\n );\n }\n }\n\n return fragnoDatabases;\n}\n","import { writeFile, mkdir } from \"node:fs/promises\";\nimport { resolve, dirname } from \"node:path\";\nimport { define } from \"gunshi\";\nimport { generateMigrationsOrSchema } from \"@fragno-dev/db/generation-engine\";\nimport { importFragmentFiles } from \"../../utils/find-fragno-databases\";\n\n// Define the db generate command with type safety\nexport const generateCommand = define({\n name: \"generate\",\n description: \"Generate schema files from FragnoDatabase definitions\",\n args: {\n output: {\n type: \"string\",\n short: \"o\",\n description:\n \"Output path: for single file, exact file path; for multiple files, output directory (default: current directory)\",\n },\n from: {\n type: \"number\",\n short: \"f\",\n description: \"Source version to generate migration from (default: current database version)\",\n },\n to: {\n type: \"number\",\n short: \"t\",\n description: \"Target version to generate migration to (default: latest schema version)\",\n },\n prefix: {\n type: \"string\",\n short: \"p\",\n description: \"String to prepend to the generated file (e.g., '/* eslint-disable */')\",\n },\n },\n run: async (ctx) => {\n // With `define()` and `multiple: true`, targets is properly typed as string[]\n const targets = ctx.positionals;\n const output = ctx.values.output;\n const toVersion = ctx.values.to;\n const fromVersion = ctx.values.from;\n const prefix = ctx.values.prefix;\n\n // Resolve all target paths\n const targetPaths = targets.map((target) => resolve(process.cwd(), target));\n\n // Import all fragment files and validate they use the same adapter\n const { databases: allFragnoDatabases, adapter } = await importFragmentFiles(targetPaths);\n\n // Check if adapter supports any form of schema generation\n if (!adapter.createSchemaGenerator && !adapter.createMigrationEngine) {\n throw new Error(\n `The adapter does not support schema generation. ` +\n `Please use an adapter that implements either createSchemaGenerator or createMigrationEngine.`,\n );\n }\n\n // Generate schema for all fragments\n console.log(\"Generating schema...\");\n\n let results: { schema: string; path: string; namespace: string }[];\n try {\n results = await generateMigrationsOrSchema(allFragnoDatabases, {\n path: output,\n toVersion,\n fromVersion,\n });\n } catch (error) {\n throw new Error(\n `Failed to generate schema: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n // Write all generated files\n for (const result of results) {\n // For single file: use output as exact file path\n // For multiple files: use output as base directory\n const finalOutputPath =\n output && results.length === 1\n ? resolve(process.cwd(), output)\n : output\n ? resolve(process.cwd(), output, result.path)\n : resolve(process.cwd(), result.path);\n\n // Ensure parent directory exists\n const parentDir = dirname(finalOutputPath);\n try {\n await mkdir(parentDir, { recursive: true });\n } catch (error) {\n throw new Error(\n `Failed to create directory: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n // Write schema to file\n try {\n const content = prefix ? `${prefix}\\n${result.schema}` : result.schema;\n await writeFile(finalOutputPath, content, { encoding: \"utf-8\" });\n } catch (error) {\n throw new Error(\n `Failed to write schema file: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n console.log(`✓ Generated: ${finalOutputPath}`);\n }\n\n console.log(`\\n✓ Schema generated successfully!`);\n console.log(` Files generated: ${results.length}`);\n console.log(` Fragments:`);\n for (const db of allFragnoDatabases) {\n console.log(` - ${db.namespace} (version ${db.schema.version})`);\n }\n },\n});\n","import { resolve } from \"node:path\";\nimport { define } from \"gunshi\";\nimport { importFragmentFiles } from \"../../utils/find-fragno-databases\";\nimport { executeMigrations, type ExecuteMigrationResult } from \"@fragno-dev/db/generation-engine\";\n\nexport const migrateCommand = define({\n name: \"migrate\",\n description: \"Run database migrations for all fragments to their latest versions\",\n args: {},\n run: async (ctx) => {\n const targets = ctx.positionals;\n\n if (targets.length === 0) {\n throw new Error(\"At least one target file path is required\");\n }\n\n // Resolve all target paths\n const targetPaths = targets.map((target) => resolve(process.cwd(), target));\n\n // Import all fragment files and validate they use the same adapter\n const { databases: allFragnoDatabases } = await importFragmentFiles(targetPaths);\n\n console.log(\"\\nMigrating all fragments to their latest versions...\\n\");\n\n let results: ExecuteMigrationResult[];\n try {\n results = await executeMigrations(allFragnoDatabases);\n } catch (error) {\n throw new Error(\n `Migration failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n // Display progress for each result\n for (const result of results) {\n console.log(`Fragment: ${result.namespace}`);\n console.log(` Current version: ${result.fromVersion}`);\n console.log(` Target version: ${result.toVersion}`);\n\n if (result.didMigrate) {\n console.log(` ✓ Migration completed: v${result.fromVersion} → v${result.toVersion}\\n`);\n } else {\n console.log(` ✓ Already at latest version. No migration needed.\\n`);\n }\n }\n\n // Summary\n console.log(\"═══════════════════════════════════════\");\n console.log(\"Migration Summary\");\n console.log(\"═══════════════════════════════════════\");\n\n const migrated = results.filter((r) => r.didMigrate);\n const skipped = results.filter((r) => !r.didMigrate);\n\n if (migrated.length > 0) {\n console.log(`\\n✓ Migrated ${migrated.length} fragment(s):`);\n for (const r of migrated) {\n console.log(` - ${r.namespace}: v${r.fromVersion} → v${r.toVersion}`);\n }\n }\n\n if (skipped.length > 0) {\n console.log(`\\n○ Skipped ${skipped.length} fragment(s) (already up-to-date):`);\n for (const r of skipped) {\n console.log(` - ${r.namespace}: v${r.toVersion}`);\n }\n }\n\n for (const db of allFragnoDatabases) {\n await db.adapter.close();\n }\n\n console.log(\"\\n✓ All migrations completed successfully\");\n },\n});\n","import { resolve } from \"node:path\";\nimport { define } from \"gunshi\";\nimport { importFragmentFiles } from \"../../utils/find-fragno-databases\";\n\nexport const infoCommand = define({\n name: \"info\",\n description: \"Display database information and migration status\",\n args: {},\n run: async (ctx) => {\n const targets = ctx.positionals;\n\n if (targets.length === 0) {\n throw new Error(\"At least one target file path is required\");\n }\n\n // Resolve all target paths\n const targetPaths = targets.map((target) => resolve(process.cwd(), target));\n\n // Import all fragment files\n const { databases: allFragnoDatabases } = await importFragmentFiles(targetPaths);\n\n // Collect database information\n const dbInfos = await Promise.all(\n allFragnoDatabases.map(async (fragnoDb) => {\n const info: {\n namespace: string;\n schemaVersion: number;\n migrationSupport: boolean;\n currentVersion?: number;\n pendingVersions?: number;\n status?: string;\n error?: string;\n } = {\n namespace: fragnoDb.namespace,\n schemaVersion: fragnoDb.schema.version,\n migrationSupport: !!fragnoDb.adapter.createMigrationEngine,\n };\n\n // Get current database version if migrations are supported\n if (fragnoDb.adapter.createMigrationEngine) {\n try {\n const migrator = fragnoDb.adapter.createMigrationEngine(\n fragnoDb.schema,\n fragnoDb.namespace,\n );\n const currentVersion = await migrator.getVersion();\n info.currentVersion = currentVersion;\n info.pendingVersions = fragnoDb.schema.version - currentVersion;\n\n if (info.pendingVersions > 0) {\n info.status = `Pending (${info.pendingVersions} migration(s))`;\n } else if (info.pendingVersions === 0) {\n info.status = \"Up to date\";\n }\n } catch (error) {\n info.error = error instanceof Error ? error.message : String(error);\n info.status = \"Error\";\n }\n } else {\n info.status = \"Schema only\";\n }\n\n return info;\n }),\n );\n\n // Determine if any database supports migrations\n const hasMigrationSupport = dbInfos.some((info) => info.migrationSupport);\n\n // Print compact table\n console.log(\"\");\n console.log(`Database Information:`);\n console.log(\"\");\n\n // Table header\n const namespaceHeader = \"Namespace\";\n const versionHeader = \"Schema\";\n const currentHeader = \"Current\";\n const statusHeader = \"Status\";\n\n const maxNamespaceLen = Math.max(\n namespaceHeader.length,\n ...dbInfos.map((info) => info.namespace.length),\n );\n const namespaceWidth = Math.max(maxNamespaceLen + 2, 20);\n const versionWidth = 8;\n const currentWidth = 9;\n const statusWidth = 25;\n\n // Print table\n console.log(\n namespaceHeader.padEnd(namespaceWidth) +\n versionHeader.padEnd(versionWidth) +\n (hasMigrationSupport ? currentHeader.padEnd(currentWidth) : \"\") +\n statusHeader,\n );\n console.log(\n \"-\".repeat(namespaceWidth) +\n \"-\".repeat(versionWidth) +\n (hasMigrationSupport ? \"-\".repeat(currentWidth) : \"\") +\n \"-\".repeat(statusWidth),\n );\n\n for (const info of dbInfos) {\n const currentVersionStr =\n info.currentVersion !== undefined ? String(info.currentVersion) : \"-\";\n console.log(\n info.namespace.padEnd(namespaceWidth) +\n String(info.schemaVersion).padEnd(versionWidth) +\n (hasMigrationSupport ? currentVersionStr.padEnd(currentWidth) : \"\") +\n (info.status || \"-\"),\n );\n }\n\n // Print help text\n console.log(\"\");\n if (!hasMigrationSupport) {\n console.log(\"Note: These adapters do not support migrations.\");\n console.log(\"Use 'fragno-cli db generate' to generate schema files.\");\n } else {\n const hasPendingMigrations = dbInfos.some(\n (info) => info.pendingVersions && info.pendingVersions > 0,\n );\n if (hasPendingMigrations) {\n console.log(\"Run 'fragno-cli db migrate <target>' to apply pending migrations.\");\n }\n }\n },\n});\n","interface SearchResult {\n id: string;\n type: \"page\" | \"heading\" | \"text\";\n content: string;\n breadcrumbs?: string[];\n contentWithHighlights?: Array<{\n type: string;\n content: string;\n styles?: { highlight?: boolean };\n }>;\n url: string;\n}\n\ninterface MergedResult {\n url: string;\n urlWithMd: string;\n fullUrl: string;\n fullUrlWithMd: string;\n title?: string;\n breadcrumbs?: string[];\n type: \"page\" | \"heading\" | \"text\";\n sections: Array<{\n content: string;\n type: \"page\" | \"heading\" | \"text\";\n }>;\n}\n\n/**\n * Merge search results by URL, grouping sections and content under each URL (without hash)\n */\nexport function mergeResultsByUrl(results: SearchResult[], baseUrl: string): MergedResult[] {\n const mergedMap = new Map<string, MergedResult>();\n\n for (const result of results) {\n // Strip hash to get base URL for merging\n const baseUrlWithoutHash = result.url.split(\"#\")[0];\n const existing = mergedMap.get(baseUrlWithoutHash);\n\n if (existing) {\n // Add this result as a section\n existing.sections.push({\n content: result.content,\n type: result.type,\n });\n } else {\n // Create new merged result\n const urlWithMd = `${baseUrlWithoutHash}.md`;\n\n const fullUrl = `https://${baseUrl}${baseUrlWithoutHash}`;\n const fullUrlWithMd = `https://${baseUrl}${urlWithMd}`;\n\n mergedMap.set(baseUrlWithoutHash, {\n url: baseUrlWithoutHash,\n urlWithMd,\n fullUrl,\n fullUrlWithMd,\n title: result.type === \"page\" ? result.content : undefined,\n breadcrumbs: result.breadcrumbs,\n type: result.type,\n sections: [\n {\n content: result.content,\n type: result.type,\n },\n ],\n });\n }\n }\n\n return Array.from(mergedMap.values());\n}\n\n/**\n * Format merged results as markdown\n */\nexport function formatAsMarkdown(mergedResults: MergedResult[]): string {\n const lines: string[] = [];\n\n for (const result of mergedResults) {\n // Title (use first section content if it's a page, or just use content)\n const title = result.title || result.sections[0]?.content || \"Untitled\";\n lines.push(`## Page: '${title}'`);\n // Breadcrumbs\n if (result.breadcrumbs && result.breadcrumbs.length > 0) {\n lines.push(\" \" + result.breadcrumbs.join(\" > \"));\n lines.push(\"\");\n }\n\n // Both URLs\n lines.push(\"URLs:\");\n lines.push(` - ${result.fullUrl}`);\n lines.push(` - ${result.fullUrlWithMd}`);\n lines.push(\"\");\n\n // Show all sections found on this page\n if (result.sections.length > 1) {\n lines.push(\"Relevant sections:\");\n for (let i = 0; i < result.sections.length; i++) {\n const section = result.sections[i];\n // Skip the first section if it's just the page title repeated\n if (i === 0 && result.type === \"page\" && section.content === result.title) {\n continue;\n }\n lines.push(` - ${section.content}`);\n }\n lines.push(\"\");\n }\n\n lines.push(\"---\");\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format merged results as JSON\n */\nexport function formatAsJson(mergedResults: MergedResult[]): string {\n return JSON.stringify(mergedResults, null, 2);\n}\n","import { define } from \"gunshi\";\nimport {\n mergeResultsByUrl,\n formatAsMarkdown,\n formatAsJson,\n} from \"../utils/format-search-results.js\";\n\ninterface SearchResult {\n id: string;\n type: \"page\" | \"heading\" | \"text\";\n content: string;\n breadcrumbs?: string[];\n contentWithHighlights?: Array<{\n type: string;\n content: string;\n styles?: { highlight?: boolean };\n }>;\n url: string;\n}\n\nexport const searchCommand = define({\n name: \"search\",\n description: \"Search the Fragno documentation\",\n args: {\n limit: {\n type: \"number\",\n description: \"Maximum number of results to show\",\n default: 10,\n },\n json: {\n type: \"boolean\",\n description: \"Output results in JSON format\",\n default: false,\n },\n markdown: {\n type: \"boolean\",\n description: \"Output results in Markdown format (default)\",\n default: true,\n },\n \"base-url\": {\n type: \"string\",\n description: \"Base URL for the documentation site\",\n default: \"fragno.dev\",\n },\n },\n run: async (ctx) => {\n const query = ctx.positionals.join(\" \");\n\n if (!query || query.trim().length === 0) {\n throw new Error(\"Please provide a search query\");\n }\n\n // Determine output mode\n const jsonMode = ctx.values.json as boolean;\n const baseUrl = ctx.values[\"base-url\"] as string;\n\n if (!jsonMode) {\n console.log(`Searching for: \"${query}\"\\n`);\n }\n\n try {\n // Make request to the docs search API\n const encodedQuery = encodeURIComponent(query);\n const response = await fetch(`https://${baseUrl}/api/search?query=${encodedQuery}`);\n\n if (!response.ok) {\n throw new Error(`API request failed with status ${response.status}`);\n }\n\n const results = (await response.json()) as SearchResult[];\n\n // Apply limit\n const limit = ctx.values.limit as number;\n const limitedResults = results.slice(0, limit);\n\n if (limitedResults.length === 0) {\n if (jsonMode) {\n console.log(\"[]\");\n } else {\n console.log(\"No results found.\");\n }\n return;\n }\n\n // Merge results by URL\n const mergedResults = mergeResultsByUrl(limitedResults, baseUrl);\n\n // Output based on mode\n if (jsonMode) {\n console.log(formatAsJson(mergedResults));\n } else {\n // Markdown mode (default)\n console.log(\n `Found ${results.length} result${results.length === 1 ? \"\" : \"s\"}${results.length > limit ? ` (showing ${limit})` : \"\"}\\n`,\n );\n console.log(formatAsMarkdown(mergedResults));\n }\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Search failed: ${error.message}`);\n }\n throw new Error(\"Search failed: An unknown error occurred\");\n }\n },\n});\n","import { define } from \"gunshi\";\nimport {\n getSubjects,\n getSubject,\n getAllSubjects,\n getSubjectParent,\n getSubjectChildren,\n} from \"@fragno-dev/corpus\";\nimport type { Subject, Example } from \"@fragno-dev/corpus\";\nimport { marked } from \"marked\";\n// @ts-expect-error - marked-terminal types are outdated for v7\nimport { markedTerminal } from \"marked-terminal\";\nimport { stripVTControlCharacters } from \"node:util\";\n\n// Always configure marked to use terminal renderer\nmarked.use(markedTerminal());\n\ninterface PrintOptions {\n showLineNumbers: boolean;\n startLine?: number;\n endLine?: number;\n headingsOnly: boolean;\n}\n\n/**\n * Build markdown content for multiple subjects\n */\nfunction buildSubjectsMarkdown(subjects: Subject[]): string {\n let fullMarkdown = \"\";\n\n for (const subject of subjects) {\n fullMarkdown += `# ${subject.title}\\n\\n`;\n\n if (subject.description) {\n fullMarkdown += `${subject.description}\\n\\n`;\n }\n\n // Add imports block if present\n if (subject.imports) {\n fullMarkdown += `### Imports\\n\\n\\`\\`\\`typescript\\n${subject.imports}\\n\\`\\`\\`\\n\\n`;\n }\n\n // Add prelude blocks if present\n if (subject.prelude.length > 0) {\n fullMarkdown += `### Prelude\\n\\n`;\n for (const block of subject.prelude) {\n // Don't include the directive in the displayed code fence\n fullMarkdown += `\\`\\`\\`typescript\\n${block.code}\\n\\`\\`\\`\\n\\n`;\n }\n }\n\n // Add all sections\n for (const section of subject.sections) {\n fullMarkdown += `## ${section.heading}\\n\\n${section.content}\\n\\n`;\n }\n }\n\n return fullMarkdown;\n}\n\n/**\n * Add line numbers to content\n */\nfunction addLineNumbers(content: string, startFrom: number = 1): string {\n const lines = content.split(\"\\n\");\n const maxDigits = String(startFrom + lines.length - 1).length;\n\n return lines\n .map((line, index) => {\n const lineNum = startFrom + index;\n const paddedNum = String(lineNum).padStart(maxDigits, \" \");\n return `${paddedNum}│ ${line}`;\n })\n .join(\"\\n\");\n}\n\n/**\n * Filter content by line range\n */\nfunction filterByLineRange(content: string, startLine: number, endLine: number): string {\n const lines = content.split(\"\\n\");\n // Convert to 0-based index\n const start = Math.max(0, startLine - 1);\n const end = Math.min(lines.length, endLine);\n return lines.slice(start, end).join(\"\\n\");\n}\n\n/**\n * Extract headings and code block information with line numbers\n */\nfunction extractHeadingsAndBlocks(subjects: Subject[]): string {\n let output = \"\";\n let currentLine = 1;\n let lastOutputLine = 0;\n\n // Helper to add a gap indicator if we skipped lines\n const addGapIfNeeded = () => {\n if (lastOutputLine > 0 && currentLine > lastOutputLine + 1) {\n output += ` │\\n`;\n }\n };\n\n // Add instruction header\n output += \"Use --start N --end N flags to show specific line ranges\\n\\n\";\n\n for (const subject of subjects) {\n // Title\n addGapIfNeeded();\n output += `${currentLine.toString().padStart(4, \" \")}│ # ${subject.title}\\n`;\n lastOutputLine = currentLine;\n currentLine += 1;\n\n // Empty line after title - SHOW IT\n output += `${currentLine.toString().padStart(4, \" \")}│\\n`;\n lastOutputLine = currentLine;\n currentLine += 1;\n\n // Description - show full text\n if (subject.description) {\n const descLines = subject.description.split(\"\\n\");\n for (const line of descLines) {\n output += `${currentLine.toString().padStart(4, \" \")}│ ${line}\\n`;\n lastOutputLine = currentLine;\n currentLine += 1;\n }\n // Empty line after description - SHOW IT\n output += `${currentLine.toString().padStart(4, \" \")}│\\n`;\n lastOutputLine = currentLine;\n currentLine += 1;\n }\n\n // Imports block - show full code\n if (subject.imports) {\n addGapIfNeeded();\n output += `${currentLine.toString().padStart(4, \" \")}│ ### Imports\\n`;\n lastOutputLine = currentLine;\n currentLine += 1;\n // Empty line after heading - SHOW IT\n output += `${currentLine.toString().padStart(4, \" \")}│\\n`;\n lastOutputLine = currentLine;\n currentLine += 1;\n output += `${currentLine.toString().padStart(4, \" \")}│ \\`\\`\\`typescript\\n`;\n lastOutputLine = currentLine;\n currentLine += 1;\n const importLines = subject.imports.split(\"\\n\");\n for (const line of importLines) {\n output += `${currentLine.toString().padStart(4, \" \")}│ ${line}\\n`;\n lastOutputLine = currentLine;\n currentLine += 1;\n }\n output += `${currentLine.toString().padStart(4, \" \")}│ \\`\\`\\`\\n`;\n lastOutputLine = currentLine;\n currentLine += 1;\n // Empty line after code block - SHOW IT\n output += `${currentLine.toString().padStart(4, \" \")}│\\n`;\n lastOutputLine = currentLine;\n currentLine += 1;\n }\n\n // Prelude blocks - show as list\n if (subject.prelude.length > 0) {\n addGapIfNeeded();\n output += `${currentLine.toString().padStart(4, \" \")}│ ### Prelude\\n`;\n lastOutputLine = currentLine;\n currentLine += 1;\n // Empty line after heading\n output += `${currentLine.toString().padStart(4, \" \")}│\\n`;\n lastOutputLine = currentLine;\n currentLine += 1;\n\n for (const block of subject.prelude) {\n const id = block.id || \"(no-id)\";\n const blockStartLine = currentLine + 1; // +1 for opening ```\n const codeLines = block.code.split(\"\\n\").length;\n const blockEndLine = currentLine + 1 + codeLines; // opening ``` + code lines\n output += `${currentLine.toString().padStart(4, \" \")}│ - id: \\`${id}\\`, L${blockStartLine}-${blockEndLine}\\n`;\n lastOutputLine = currentLine;\n currentLine += codeLines + 3; // opening ```, code, closing ```, blank line\n }\n // Update lastOutputLine to current position to avoid gap indicator\n lastOutputLine = currentLine - 1;\n }\n\n // Sections - show headings and any example IDs that belong to them\n const sectionToExamples = new Map<string, Example[]>();\n\n // Group examples by their rough section (based on heading appearance in explanations)\n for (const example of subject.examples) {\n // Try to match the example to a section based on context\n // For now, we'll list all example IDs under the sections where they appear\n for (const section of subject.sections) {\n // Check if the section contains references to this example\n if (\n section.content.includes(example.code.substring(0, Math.min(50, example.code.length)))\n ) {\n if (!sectionToExamples.has(section.heading)) {\n sectionToExamples.set(section.heading, []);\n }\n sectionToExamples.get(section.heading)!.push(example);\n break;\n }\n }\n }\n\n for (const section of subject.sections) {\n addGapIfNeeded();\n output += `${currentLine.toString().padStart(4, \" \")}│ ## ${section.heading}\\n`;\n lastOutputLine = currentLine;\n currentLine += 1;\n\n // Show code block IDs as a list if any examples match this section\n const examples = sectionToExamples.get(section.heading) || [];\n if (examples.length > 0) {\n // We need to parse the section content to find where each example appears\n const sectionStartLine = currentLine;\n const lines = section.content.split(\"\\n\");\n\n for (const example of examples) {\n const id = example.id || \"(no-id)\";\n // Find the code block in section content\n let blockStartLine = sectionStartLine;\n let blockEndLine = sectionStartLine;\n let inCodeBlock = false;\n let foundBlock = false;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line.trim().startsWith(\"```\") && !inCodeBlock) {\n // Check if next lines match the example\n const codeStart = i + 1;\n let matches = true;\n const exampleLines = example.code.split(\"\\n\");\n for (let j = 0; j < Math.min(3, exampleLines.length); j++) {\n if (lines[codeStart + j]?.trim() !== exampleLines[j]?.trim()) {\n matches = false;\n break;\n }\n }\n if (matches) {\n blockStartLine = sectionStartLine + i + 1; // +1 to skip opening ```\n blockEndLine = sectionStartLine + i + exampleLines.length;\n foundBlock = true;\n break;\n }\n }\n }\n\n if (foundBlock) {\n output += `${currentLine.toString().padStart(4, \" \")}│ - id: \\`${id}\\`, L${blockStartLine}-${blockEndLine}\\n`;\n } else {\n output += `${currentLine.toString().padStart(4, \" \")}│ - id: \\`${id}\\`\\n`;\n }\n lastOutputLine = currentLine;\n }\n }\n\n // Count lines\n const sectionLines = section.content.split(\"\\n\");\n for (const _line of sectionLines) {\n currentLine += 1;\n }\n currentLine += 1; // blank line after section\n // Update lastOutputLine to current position to avoid gap indicator\n lastOutputLine = currentLine - 1;\n }\n }\n\n return output;\n}\n\n/**\n * Print subjects with the given options\n */\nasync function printSubjects(subjects: Subject[], options: PrintOptions): Promise<void> {\n if (options.headingsOnly) {\n // Show only headings and code block IDs\n const headingsOutput = extractHeadingsAndBlocks(subjects);\n console.log(headingsOutput);\n return;\n }\n\n // Build the full markdown content\n const markdown = buildSubjectsMarkdown(subjects);\n\n // Render markdown to terminal for nice formatting\n let output = await marked.parse(markdown);\n\n // Apply line range filter if specified (after rendering)\n const startLine = options.startLine ?? 1;\n if (options.startLine !== undefined || options.endLine !== undefined) {\n const end = options.endLine ?? output.split(\"\\n\").length;\n output = filterByLineRange(output, startLine, end);\n }\n\n // Add line numbers after rendering (if requested)\n // Line numbers correspond to the rendered output that agents interact with\n if (options.showLineNumbers) {\n output = addLineNumbers(output, startLine);\n }\n\n console.log(output);\n}\n\n/**\n * Find and print code blocks by ID\n */\nasync function printCodeBlockById(\n id: string,\n topics: string[],\n showLineNumbers: boolean,\n): Promise<void> {\n // If topics are specified, search only those; otherwise search all subjects\n const subjects = topics.length > 0 ? getSubject(...topics) : getAllSubjects();\n\n interface CodeBlockMatch {\n subjectId: string;\n subjectTitle: string;\n section: string;\n code: string;\n type: \"prelude\" | \"example\";\n startLine?: number;\n endLine?: number;\n }\n\n const matches: CodeBlockMatch[] = [];\n\n for (const subject of subjects) {\n // Build the rendered markdown to get correct line numbers (matching --start/--end behavior)\n const fullMarkdown = buildSubjectsMarkdown([subject]);\n const renderedOutput = await marked.parse(fullMarkdown);\n const renderedLines = renderedOutput.split(\"\\n\");\n\n // Search in prelude blocks\n for (const block of subject.prelude) {\n if (block.id === id) {\n // Find line numbers in the rendered output\n let startLine: number | undefined;\n let endLine: number | undefined;\n\n // Search for the prelude code in the rendered output\n const codeLines = block.code.split(\"\\n\");\n const firstCodeLine = codeLines[0].trim();\n\n for (let i = 0; i < renderedLines.length; i++) {\n // Strip ANSI codes before comparing\n if (stripVTControlCharacters(renderedLines[i]).trim() === firstCodeLine) {\n // Found the start of the code\n startLine = i + 1; // 1-based line numbers\n endLine = i + codeLines.length;\n break;\n }\n }\n\n matches.push({\n subjectId: subject.id,\n subjectTitle: subject.title,\n section: \"Prelude\",\n code: block.code,\n type: \"prelude\",\n startLine,\n endLine,\n });\n }\n }\n\n // Search in examples\n for (const example of subject.examples) {\n if (example.id === id) {\n // Try to find which section this example belongs to\n let sectionName = \"Unknown Section\";\n let startLine: number | undefined;\n let endLine: number | undefined;\n\n for (const section of subject.sections) {\n if (\n section.content.includes(example.code.substring(0, Math.min(50, example.code.length)))\n ) {\n sectionName = section.heading;\n\n // Find line numbers in the rendered output\n const codeLines = example.code.split(\"\\n\");\n const firstCodeLine = codeLines[0].trim();\n\n for (let i = 0; i < renderedLines.length; i++) {\n // Strip ANSI codes before comparing\n if (stripVTControlCharacters(renderedLines[i]).trim() === firstCodeLine) {\n // Found the start of the code\n startLine = i + 1; // 1-based line numbers\n endLine = i + codeLines.length;\n break;\n }\n }\n break;\n }\n }\n\n matches.push({\n subjectId: subject.id,\n subjectTitle: subject.title,\n section: sectionName,\n code: example.code,\n type: \"example\",\n startLine,\n endLine,\n });\n }\n }\n }\n\n if (matches.length === 0) {\n console.error(`Error: No code block found with id \"${id}\"`);\n if (topics.length > 0) {\n console.error(`Searched in topics: ${topics.join(\", \")}`);\n } else {\n console.error(\"Searched in all available topics\");\n }\n process.exit(1);\n }\n\n // Build markdown output\n for (let i = 0; i < matches.length; i++) {\n const match = matches[i];\n\n if (matches.length > 1 && i > 0) {\n console.log(\"\\n---\\n\");\n }\n\n // Build markdown for this match\n let matchMarkdown = `# ${match.subjectTitle}\\n\\n`;\n matchMarkdown += `## ${match.section}\\n\\n`;\n\n // Add line number info if available and requested (as plain text, not in markdown)\n if (showLineNumbers && match.startLine && match.endLine) {\n console.log(`Lines ${match.startLine}-${match.endLine} (use with --start/--end)\\n`);\n }\n\n matchMarkdown += `\\`\\`\\`typescript\\n${match.code}\\n\\`\\`\\`\\n`;\n\n // Render the markdown\n const rendered = await marked.parse(matchMarkdown);\n console.log(rendered);\n }\n}\n\n/**\n * Print information about the corpus command\n */\nfunction printCorpusHelp(): void {\n console.log(\"Fragno Corpus - Code examples and documentation (similar to LLMs.txt\");\n console.log(\"\");\n console.log(\"Usage: fragno-cli corpus [options] [topic...]\");\n console.log(\"\");\n console.log(\"Options:\");\n console.log(\" -n, --no-line-numbers Hide line numbers (shown by default)\");\n console.log(\" -s, --start N Starting line number to display from\");\n console.log(\" -e, --end N Ending line number to display to\");\n console.log(\" --headings Show only headings and code block IDs\");\n console.log(\" --id <id> Retrieve a specific code block by ID\");\n console.log(\"\");\n console.log(\"Examples:\");\n console.log(\" fragno-cli corpus # List all available topics\");\n console.log(\" fragno-cli corpus defining-routes # Show route definition examples\");\n console.log(\" fragno-cli corpus --headings database-querying\");\n console.log(\" # Show structure overview\");\n console.log(\" fragno-cli corpus --start 10 --end 50 database-querying\");\n console.log(\" # Show specific lines\");\n console.log(\" fragno-cli corpus --id create-user # Get code block by ID\");\n console.log(\" fragno-cli corpus database-adapters kysely-adapter\");\n console.log(\" # Show multiple topics\");\n console.log(\"\");\n console.log(\"Available topics:\");\n\n const subjects = getSubjects();\n\n // Group subjects by their tree structure\n const rootSubjects: Array<{\n id: string;\n title: string;\n children: Array<{ id: string; title: string }>;\n }> = [];\n const subjectMap = new Map(subjects.map((s) => [s.id, s]));\n\n for (const subject of subjects) {\n const parent = getSubjectParent(subject.id);\n if (!parent) {\n // This is a root subject\n const children = getSubjectChildren(subject.id);\n rootSubjects.push({\n id: subject.id,\n title: subject.title,\n children: children.map((childId) => ({\n id: childId,\n title: subjectMap.get(childId)?.title || childId,\n })),\n });\n }\n }\n\n // Display in tree format\n for (const root of rootSubjects) {\n console.log(` ${root.id.padEnd(30)} ${root.title}`);\n for (let i = 0; i < root.children.length; i++) {\n const child = root.children[i];\n const isLast = i === root.children.length - 1;\n const connector = isLast ? \"└─\" : \"├─\";\n console.log(` ${connector} ${child.id.padEnd(26)} ${child.title}`);\n }\n }\n}\n\nexport const corpusCommand = define({\n name: \"corpus\",\n description: \"View code examples and documentation for Fragno\",\n args: {\n \"no-line-numbers\": {\n type: \"boolean\",\n short: \"n\",\n description: \"Hide line numbers (line numbers are shown by default)\",\n },\n start: {\n type: \"number\",\n short: \"s\",\n description: \"Starting line number (1-based) to display from\",\n },\n end: {\n type: \"number\",\n short: \"e\",\n description: \"Ending line number (1-based) to display to\",\n },\n headings: {\n type: \"boolean\",\n description: \"Show only section headings and code block IDs with line numbers\",\n },\n id: {\n type: \"string\",\n description: \"Retrieve a specific code block by ID\",\n },\n },\n run: async (ctx) => {\n const topics = ctx.positionals;\n const showLineNumbers = !(ctx.values[\"no-line-numbers\"] ?? false);\n const startLine = ctx.values.start;\n const endLine = ctx.values.end;\n const headingsOnly = ctx.values.headings ?? false;\n const codeBlockId = ctx.values.id;\n\n // Handle --id flag\n if (codeBlockId) {\n await printCodeBlockById(codeBlockId, topics, showLineNumbers);\n return;\n }\n\n // No topics provided - show help\n if (topics.length === 0) {\n printCorpusHelp();\n return;\n }\n\n // Validate line range\n if (startLine !== undefined && endLine !== undefined && startLine > endLine) {\n console.error(\"Error: --start must be less than or equal to --end\");\n process.exit(1);\n }\n\n // Load and display requested topics\n try {\n const subjects = getSubject(...topics);\n\n await printSubjects(subjects, {\n showLineNumbers,\n startLine,\n endLine,\n headingsOnly,\n });\n } catch (error) {\n console.error(\"Error loading topics:\", error instanceof Error ? error.message : error);\n console.log(\"\\nRun 'fragno-cli corpus' to see available topics.\");\n process.exit(1);\n }\n },\n});\n","#!/usr/bin/env node\n\nimport { cli, define } from \"gunshi\";\nimport { generateCommand } from \"./commands/db/generate.js\";\nimport { migrateCommand } from \"./commands/db/migrate.js\";\nimport { infoCommand } from \"./commands/db/info.js\";\nimport { searchCommand } from \"./commands/search.js\";\nimport { corpusCommand } from \"./commands/corpus.js\";\nimport { readFileSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { dirname, join } from \"node:path\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst packageJson = JSON.parse(readFileSync(join(__dirname, \"../package.json\"), \"utf-8\"));\nconst version = packageJson.version;\n\n// Create a Map of db sub-commands\nconst dbSubCommands = new Map();\ndbSubCommands.set(\"generate\", generateCommand);\ndbSubCommands.set(\"migrate\", migrateCommand);\ndbSubCommands.set(\"info\", infoCommand);\n\n// Define the db command with nested subcommands\nexport const dbCommand = define({\n name: \"db\",\n description: \"Database management commands\",\n});\n\n// Define the main command\nexport const mainCommand = define({\n name: \"fragno-cli\",\n description: \"Tools for working with Fragno fragments\",\n});\n\nif (import.meta.main) {\n try {\n const args = process.argv.slice(2);\n\n // Manual routing for top-level commands\n if (args[0] === \"search\") {\n // Run search command directly\n await cli(args.slice(1), searchCommand, {\n name: \"fragno-cli search\",\n version,\n });\n } else if (args[0] === \"corpus\") {\n // Run corpus command directly\n await cli(args.slice(1), corpusCommand, {\n name: \"fragno-cli corpus\",\n version,\n });\n } else if (args[0] === \"db\") {\n // Handle db subcommands\n const subCommandName = args[1];\n\n if (!subCommandName || subCommandName === \"--help\" || subCommandName === \"-h\") {\n // Show db help with subcommands\n console.log(\"Database management commands\");\n console.log(\"\");\n console.log(\"USAGE:\");\n console.log(\" fragno-cli db <COMMAND>\");\n console.log(\"\");\n console.log(\"COMMANDS:\");\n console.log(\n \" generate Generate schema files from FragnoDatabase definitions\",\n );\n console.log(\" migrate Run database migrations\");\n console.log(\" info Display database information and migration status\");\n console.log(\"\");\n console.log(\"For more info, run any command with the `--help` flag:\");\n console.log(\" fragno-cli db generate --help\");\n console.log(\" fragno-cli db migrate --help\");\n console.log(\" fragno-cli db info --help\");\n console.log(\"\");\n console.log(\"OPTIONS:\");\n console.log(\" -h, --help Display this help message\");\n console.log(\" -v, --version Display this version\");\n } else if (subCommandName === \"--version\" || subCommandName === \"-v\") {\n console.log(version);\n } else {\n // Route to specific db subcommand\n const subCommand = dbSubCommands.get(subCommandName);\n\n if (!subCommand) {\n console.error(`Unknown command: ${subCommandName}`);\n console.log(\"\");\n console.log(\"Run 'fragno-cli db --help' for available commands.\");\n process.exit(1);\n }\n\n // Run the subcommand\n await cli(args.slice(2), subCommand, {\n name: `fragno-cli db ${subCommandName}`,\n version,\n });\n }\n } else if (!args.length || args[0] === \"--help\" || args[0] === \"-h\") {\n // Show main help\n console.log(\"Tools for working with Fragno\");\n console.log(\"\");\n console.log(\"USAGE:\");\n console.log(\" fragno-cli <COMMAND>\");\n console.log(\"\");\n console.log(\"COMMANDS:\");\n console.log(\" db Database management commands\");\n console.log(\" search Search the Fragno documentation\");\n console.log(\" corpus View code examples and documentation for Fragno\");\n console.log(\"\");\n console.log(\"For more info, run any command with the `--help` flag:\");\n console.log(\" fragno-cli db --help\");\n console.log(\" fragno-cli search --help\");\n console.log(\" fragno-cli corpus --help\");\n console.log(\"\");\n console.log(\"OPTIONS:\");\n console.log(\" -h, --help Display this help message\");\n console.log(\" -v, --version Display this version\");\n } else if (args[0] === \"--version\" || args[0] === \"-v\") {\n console.log(version);\n } else {\n // Unknown command\n console.error(`Unknown command: ${args[0]}`);\n console.log(\"\");\n console.log(\"Run 'fragno-cli --help' for available commands.\");\n process.exit(1);\n }\n } catch (error) {\n console.error(\"Error:\", error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n\nexport { generateCommand, migrateCommand, infoCommand, searchCommand, corpusCommand };\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fragno-dev/cli",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "exports": {
5
5
  ".": {
6
6
  "development": "./src/cli.ts",
@@ -24,8 +24,11 @@
24
24
  "@clack/prompts": "^0.11.0",
25
25
  "c12": "^3.3.1",
26
26
  "gunshi": "^0.26.3",
27
- "@fragno-dev/core": "0.1.5",
28
- "@fragno-dev/db": "0.1.11"
27
+ "marked": "^15.0.12",
28
+ "marked-terminal": "^7.3.0",
29
+ "@fragno-dev/core": "0.1.6",
30
+ "@fragno-dev/corpus": "0.0.3",
31
+ "@fragno-dev/db": "0.1.13"
29
32
  },
30
33
  "main": "./dist/cli.js",
31
34
  "module": "./dist/cli.js",
package/src/cli.ts CHANGED
@@ -1,9 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { cli, define, parseArgs, resolveArgs } from "gunshi";
3
+ import { cli, define } from "gunshi";
4
4
  import { generateCommand } from "./commands/db/generate.js";
5
5
  import { migrateCommand } from "./commands/db/migrate.js";
6
6
  import { infoCommand } from "./commands/db/info.js";
7
+ import { searchCommand } from "./commands/search.js";
8
+ import { corpusCommand } from "./commands/corpus.js";
9
+ import { readFileSync } from "node:fs";
10
+ import { fileURLToPath } from "node:url";
11
+ import { dirname, join } from "node:path";
12
+
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+ const packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
15
+ const version = packageJson.version;
7
16
 
8
17
  // Create a Map of db sub-commands
9
18
  const dbSubCommands = new Map();
@@ -11,98 +20,108 @@ dbSubCommands.set("generate", generateCommand);
11
20
  dbSubCommands.set("migrate", migrateCommand);
12
21
  dbSubCommands.set("info", infoCommand);
13
22
 
14
- // Helper function to print db command help
15
- function printDbHelp() {
16
- console.log("Database management commands for Fragno");
17
- console.log("");
18
- console.log("Usage: fragno-cli db <command> [options]");
19
- console.log("");
20
- console.log("Commands:");
21
- console.log(" generate Generate schema files from FragnoDatabase definitions");
22
- console.log(" migrate Run database migrations");
23
- console.log(" info Display database information and migration status");
24
- console.log("");
25
- console.log("Run 'fragno-cli db <command> --help' for more information.");
26
- }
27
-
28
- // Define the db command with type safety
23
+ // Define the db command with nested subcommands
29
24
  export const dbCommand = define({
30
25
  name: "db",
31
26
  description: "Database management commands",
32
- run: printDbHelp,
33
27
  });
34
28
 
35
- // Create a Map of root sub-commands
36
- const rootSubCommands = new Map();
37
- rootSubCommands.set("db", dbCommand);
38
-
39
- // Define the main command with type safety
29
+ // Define the main command
40
30
  export const mainCommand = define({
41
31
  name: "fragno-cli",
42
- description: "Fragno CLI - Tools for working with Fragno fragments",
43
- run: () => {
44
- console.log("Fragno CLI - Tools for working with Fragno fragments");
45
- console.log("");
46
- console.log("Usage: fragno-cli <command> [options]");
47
- console.log("");
48
- console.log("Commands:");
49
- console.log(" db Database management commands");
50
- console.log("");
51
- console.log("Run 'fragno-cli <command> --help' for more information.");
52
- },
32
+ description: "Tools for working with Fragno fragments",
53
33
  });
54
34
 
55
35
  if (import.meta.main) {
56
36
  try {
57
- // Parse arguments to handle nested subcommands
58
37
  const args = process.argv.slice(2);
59
38
 
60
- // Check if we're calling a db subcommand directly
61
- if (args[0] === "db" && args.length > 1) {
39
+ // Manual routing for top-level commands
40
+ if (args[0] === "search") {
41
+ // Run search command directly
42
+ await cli(args.slice(1), searchCommand, {
43
+ name: "fragno-cli search",
44
+ version,
45
+ });
46
+ } else if (args[0] === "corpus") {
47
+ // Run corpus command directly
48
+ await cli(args.slice(1), corpusCommand, {
49
+ name: "fragno-cli corpus",
50
+ version,
51
+ });
52
+ } else if (args[0] === "db") {
53
+ // Handle db subcommands
62
54
  const subCommandName = args[1];
63
55
 
64
- // Check if it's a help request
65
- if (subCommandName === "--help" || subCommandName === "-h") {
66
- printDbHelp();
67
- process.exit(0);
68
- }
69
-
70
- const subCommand = dbSubCommands.get(subCommandName);
71
-
72
- if (!subCommand) {
73
- console.error(`Unknown command: ${subCommandName}`);
56
+ if (!subCommandName || subCommandName === "--help" || subCommandName === "-h") {
57
+ // Show db help with subcommands
58
+ console.log("Database management commands");
74
59
  console.log("");
75
- printDbHelp();
76
- process.exit(1);
77
- }
78
-
79
- // Run the specific subcommand with its args
80
- const subArgs = args.slice(2);
81
- const isSubCommandHelp = subArgs.includes("--help") || subArgs.includes("-h");
82
-
83
- // Check for validation errors before running
84
- let hasValidationError = false;
85
- if (!isSubCommandHelp && subCommand.args) {
86
- const tokens = parseArgs(subArgs);
87
- const resolved = resolveArgs(subCommand.args, tokens);
88
- hasValidationError = !!resolved.error;
89
- }
90
-
91
- // Run the command (let gunshi handle printing errors/help)
92
- await cli(subArgs, subCommand);
93
-
94
- // Exit with error code if there was a validation error
95
- if (hasValidationError) {
96
- process.exit(1);
60
+ console.log("USAGE:");
61
+ console.log(" fragno-cli db <COMMAND>");
62
+ console.log("");
63
+ console.log("COMMANDS:");
64
+ console.log(
65
+ " generate Generate schema files from FragnoDatabase definitions",
66
+ );
67
+ console.log(" migrate Run database migrations");
68
+ console.log(" info Display database information and migration status");
69
+ console.log("");
70
+ console.log("For more info, run any command with the `--help` flag:");
71
+ console.log(" fragno-cli db generate --help");
72
+ console.log(" fragno-cli db migrate --help");
73
+ console.log(" fragno-cli db info --help");
74
+ console.log("");
75
+ console.log("OPTIONS:");
76
+ console.log(" -h, --help Display this help message");
77
+ console.log(" -v, --version Display this version");
78
+ } else if (subCommandName === "--version" || subCommandName === "-v") {
79
+ console.log(version);
80
+ } else {
81
+ // Route to specific db subcommand
82
+ const subCommand = dbSubCommands.get(subCommandName);
83
+
84
+ if (!subCommand) {
85
+ console.error(`Unknown command: ${subCommandName}`);
86
+ console.log("");
87
+ console.log("Run 'fragno-cli db --help' for available commands.");
88
+ process.exit(1);
89
+ }
90
+
91
+ // Run the subcommand
92
+ await cli(args.slice(2), subCommand, {
93
+ name: `fragno-cli db ${subCommandName}`,
94
+ version,
95
+ });
97
96
  }
98
- } else if (args[0] === "db") {
99
- // "db" command with no subcommand - show db help
100
- printDbHelp();
97
+ } else if (!args.length || args[0] === "--help" || args[0] === "-h") {
98
+ // Show main help
99
+ console.log("Tools for working with Fragno");
100
+ console.log("");
101
+ console.log("USAGE:");
102
+ console.log(" fragno-cli <COMMAND>");
103
+ console.log("");
104
+ console.log("COMMANDS:");
105
+ console.log(" db Database management commands");
106
+ console.log(" search Search the Fragno documentation");
107
+ console.log(" corpus View code examples and documentation for Fragno");
108
+ console.log("");
109
+ console.log("For more info, run any command with the `--help` flag:");
110
+ console.log(" fragno-cli db --help");
111
+ console.log(" fragno-cli search --help");
112
+ console.log(" fragno-cli corpus --help");
113
+ console.log("");
114
+ console.log("OPTIONS:");
115
+ console.log(" -h, --help Display this help message");
116
+ console.log(" -v, --version Display this version");
117
+ } else if (args[0] === "--version" || args[0] === "-v") {
118
+ console.log(version);
101
119
  } else {
102
- // Run the main CLI
103
- await cli(args, mainCommand, {
104
- subCommands: rootSubCommands,
105
- });
120
+ // Unknown command
121
+ console.error(`Unknown command: ${args[0]}`);
122
+ console.log("");
123
+ console.log("Run 'fragno-cli --help' for available commands.");
124
+ process.exit(1);
106
125
  }
107
126
  } catch (error) {
108
127
  console.error("Error:", error instanceof Error ? error.message : error);
@@ -110,4 +129,4 @@ if (import.meta.main) {
110
129
  }
111
130
  }
112
131
 
113
- export { generateCommand, migrateCommand, infoCommand };
132
+ export { generateCommand, migrateCommand, infoCommand, searchCommand, corpusCommand };