@fragno-dev/cli 0.1.14 → 0.1.16

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 CHANGED
@@ -1,13 +1,18 @@
1
1
  #!/usr/bin/env node
2
- import { cli, define, parseArgs, resolveArgs } from "gunshi";
2
+ import { cli, define } from "gunshi";
3
3
  import { mkdir, writeFile } from "node:fs/promises";
4
- import { dirname, relative, resolve } from "node:path";
4
+ import { dirname, join, relative, resolve } from "node:path";
5
5
  import { executeMigrations, generateMigrationsOrSchema } from "@fragno-dev/db/generation-engine";
6
6
  import { FragnoDatabase, isFragnoDatabase } from "@fragno-dev/db";
7
7
  import { fragnoDatabaseAdapterNameFakeSymbol, fragnoDatabaseAdapterVersionFakeSymbol } from "@fragno-dev/db/adapters";
8
8
  import { instantiatedFragmentFakeSymbol } from "@fragno-dev/core/api/fragment-instantiation";
9
9
  import { loadConfig } from "c12";
10
- import { getSubject, getSubjects } from "@fragno-dev/corpus";
10
+ import { getAllSubjects, getSubject, getSubjectChildren, getSubjectParent, getSubjects } from "@fragno-dev/corpus";
11
+ import { marked } from "marked";
12
+ import { markedTerminal } from "marked-terminal";
13
+ import { stripVTControlCharacters } from "node:util";
14
+ import { readFileSync } from "node:fs";
15
+ import { fileURLToPath } from "node:url";
11
16
 
12
17
  //#region src/utils/find-fragno-databases.ts
13
18
  async function importFragmentFile(path) {
@@ -391,75 +396,360 @@ const searchCommand = define({
391
396
 
392
397
  //#endregion
393
398
  //#region src/commands/corpus.ts
399
+ marked.use(markedTerminal());
394
400
  /**
395
- * Print a subject with its examples
401
+ * Build markdown content for multiple subjects
396
402
  */
397
- function printSubject(subject) {
398
- console.log(`\n${"=".repeat(60)}`);
399
- console.log(`${subject.title}`);
400
- console.log(`${"=".repeat(60)}\n`);
401
- if (subject.description) {
402
- console.log(subject.description);
403
- console.log();
403
+ function buildSubjectsMarkdown(subjects) {
404
+ let fullMarkdown = "";
405
+ for (const subject of subjects) {
406
+ fullMarkdown += `# ${subject.title}\n\n`;
407
+ if (subject.description) fullMarkdown += `${subject.description}\n\n`;
408
+ if (subject.imports) fullMarkdown += `### Imports\n\n\`\`\`typescript\n${subject.imports}\n\`\`\`\n\n`;
409
+ if (subject.prelude.length > 0) {
410
+ fullMarkdown += `### Prelude\n\n`;
411
+ for (const block of subject.prelude) fullMarkdown += `\`\`\`typescript\n${block.code}\n\`\`\`\n\n`;
412
+ }
413
+ for (const section of subject.sections) fullMarkdown += `## ${section.heading}\n\n${section.content}\n\n`;
414
+ }
415
+ return fullMarkdown;
416
+ }
417
+ /**
418
+ * Add line numbers to content
419
+ */
420
+ function addLineNumbers(content, startFrom = 1) {
421
+ const lines = content.split("\n");
422
+ const maxDigits = String(startFrom + lines.length - 1).length;
423
+ return lines.map((line, index) => {
424
+ const lineNum = startFrom + index;
425
+ return `${String(lineNum).padStart(maxDigits, " ")}│ ${line}`;
426
+ }).join("\n");
427
+ }
428
+ /**
429
+ * Filter content by line range
430
+ */
431
+ function filterByLineRange(content, startLine, endLine) {
432
+ const lines = content.split("\n");
433
+ const start = Math.max(0, startLine - 1);
434
+ const end = Math.min(lines.length, endLine);
435
+ return lines.slice(start, end).join("\n");
436
+ }
437
+ /**
438
+ * Extract headings and code block information with line numbers
439
+ */
440
+ function extractHeadingsAndBlocks(subjects) {
441
+ let output = "";
442
+ let currentLine = 1;
443
+ let lastOutputLine = 0;
444
+ const addGapIfNeeded = () => {
445
+ if (lastOutputLine > 0 && currentLine > lastOutputLine + 1) output += ` │\n`;
446
+ };
447
+ output += "Use --start N --end N flags to show specific line ranges\n\n";
448
+ for (const subject of subjects) {
449
+ addGapIfNeeded();
450
+ output += `${currentLine.toString().padStart(4, " ")}│ # ${subject.title}\n`;
451
+ lastOutputLine = currentLine;
452
+ currentLine += 1;
453
+ output += `${currentLine.toString().padStart(4, " ")}│\n`;
454
+ lastOutputLine = currentLine;
455
+ currentLine += 1;
456
+ if (subject.description) {
457
+ const descLines = subject.description.split("\n");
458
+ for (const line of descLines) {
459
+ output += `${currentLine.toString().padStart(4, " ")}│ ${line}\n`;
460
+ lastOutputLine = currentLine;
461
+ currentLine += 1;
462
+ }
463
+ output += `${currentLine.toString().padStart(4, " ")}│\n`;
464
+ lastOutputLine = currentLine;
465
+ currentLine += 1;
466
+ }
467
+ if (subject.imports) {
468
+ addGapIfNeeded();
469
+ output += `${currentLine.toString().padStart(4, " ")}│ ### Imports\n`;
470
+ lastOutputLine = currentLine;
471
+ currentLine += 1;
472
+ output += `${currentLine.toString().padStart(4, " ")}│\n`;
473
+ lastOutputLine = currentLine;
474
+ currentLine += 1;
475
+ output += `${currentLine.toString().padStart(4, " ")}│ \`\`\`typescript\n`;
476
+ lastOutputLine = currentLine;
477
+ currentLine += 1;
478
+ const importLines = subject.imports.split("\n");
479
+ for (const line of importLines) {
480
+ output += `${currentLine.toString().padStart(4, " ")}│ ${line}\n`;
481
+ lastOutputLine = currentLine;
482
+ currentLine += 1;
483
+ }
484
+ output += `${currentLine.toString().padStart(4, " ")}│ \`\`\`\n`;
485
+ lastOutputLine = currentLine;
486
+ currentLine += 1;
487
+ output += `${currentLine.toString().padStart(4, " ")}│\n`;
488
+ lastOutputLine = currentLine;
489
+ currentLine += 1;
490
+ }
491
+ if (subject.prelude.length > 0) {
492
+ addGapIfNeeded();
493
+ output += `${currentLine.toString().padStart(4, " ")}│ ### Prelude\n`;
494
+ lastOutputLine = currentLine;
495
+ currentLine += 1;
496
+ output += `${currentLine.toString().padStart(4, " ")}│\n`;
497
+ lastOutputLine = currentLine;
498
+ currentLine += 1;
499
+ for (const block of subject.prelude) {
500
+ const id = block.id || "(no-id)";
501
+ const blockStartLine = currentLine + 1;
502
+ const codeLines = block.code.split("\n").length;
503
+ const blockEndLine = currentLine + 1 + codeLines;
504
+ output += `${currentLine.toString().padStart(4, " ")}│ - id: \`${id}\`, L${blockStartLine}-${blockEndLine}\n`;
505
+ lastOutputLine = currentLine;
506
+ currentLine += codeLines + 3;
507
+ }
508
+ lastOutputLine = currentLine - 1;
509
+ }
510
+ const sectionToExamples = /* @__PURE__ */ new Map();
511
+ for (const example of subject.examples) for (const section of subject.sections) if (section.content.includes(example.code.substring(0, Math.min(50, example.code.length)))) {
512
+ if (!sectionToExamples.has(section.heading)) sectionToExamples.set(section.heading, []);
513
+ sectionToExamples.get(section.heading).push(example);
514
+ break;
515
+ }
516
+ for (const section of subject.sections) {
517
+ addGapIfNeeded();
518
+ output += `${currentLine.toString().padStart(4, " ")}│ ## ${section.heading}\n`;
519
+ lastOutputLine = currentLine;
520
+ currentLine += 1;
521
+ const examples = sectionToExamples.get(section.heading) || [];
522
+ if (examples.length > 0) {
523
+ const sectionStartLine = currentLine;
524
+ const lines = section.content.split("\n");
525
+ for (const example of examples) {
526
+ const id = example.id || "(no-id)";
527
+ let blockStartLine = sectionStartLine;
528
+ let blockEndLine = sectionStartLine;
529
+ let foundBlock = false;
530
+ for (let i = 0; i < lines.length; i++) if (lines[i].trim().startsWith("```") && true) {
531
+ const codeStart = i + 1;
532
+ let matches = true;
533
+ const exampleLines = example.code.split("\n");
534
+ for (let j = 0; j < Math.min(3, exampleLines.length); j++) if (lines[codeStart + j]?.trim() !== exampleLines[j]?.trim()) {
535
+ matches = false;
536
+ break;
537
+ }
538
+ if (matches) {
539
+ blockStartLine = sectionStartLine + i + 1;
540
+ blockEndLine = sectionStartLine + i + exampleLines.length;
541
+ foundBlock = true;
542
+ break;
543
+ }
544
+ }
545
+ if (foundBlock) output += `${currentLine.toString().padStart(4, " ")}│ - id: \`${id}\`, L${blockStartLine}-${blockEndLine}\n`;
546
+ else output += `${currentLine.toString().padStart(4, " ")}│ - id: \`${id}\`\n`;
547
+ lastOutputLine = currentLine;
548
+ }
549
+ }
550
+ const sectionLines = section.content.split("\n");
551
+ for (const _line of sectionLines) currentLine += 1;
552
+ currentLine += 1;
553
+ lastOutputLine = currentLine - 1;
554
+ }
404
555
  }
405
- if (subject.imports) {
406
- console.log("### Imports\n");
407
- console.log("```typescript");
408
- console.log(subject.imports);
409
- console.log("```\n");
556
+ return output;
557
+ }
558
+ /**
559
+ * Print subjects with the given options
560
+ */
561
+ async function printSubjects(subjects, options) {
562
+ if (options.headingsOnly) {
563
+ const headingsOutput = extractHeadingsAndBlocks(subjects);
564
+ console.log(headingsOutput);
565
+ return;
410
566
  }
411
- if (subject.init) {
412
- console.log("### Initialization\n");
413
- console.log("```typescript");
414
- console.log(subject.init);
415
- console.log("```\n");
567
+ const markdown = buildSubjectsMarkdown(subjects);
568
+ let output = await marked.parse(markdown);
569
+ const startLine = options.startLine ?? 1;
570
+ if (options.startLine !== void 0 || options.endLine !== void 0) {
571
+ const end = options.endLine ?? output.split("\n").length;
572
+ output = filterByLineRange(output, startLine, end);
416
573
  }
417
- for (let i = 0; i < subject.examples.length; i++) {
418
- const example = subject.examples[i];
419
- console.log(`### Example ${i + 1}\n`);
420
- console.log("```typescript");
421
- console.log(example.code);
422
- console.log("```");
423
- if (example.explanation) {
424
- console.log();
425
- console.log(example.explanation);
574
+ if (options.showLineNumbers) output = addLineNumbers(output, startLine);
575
+ console.log(output);
576
+ }
577
+ /**
578
+ * Find and print code blocks by ID
579
+ */
580
+ async function printCodeBlockById(id, topics, showLineNumbers) {
581
+ const subjects = topics.length > 0 ? getSubject(...topics) : getAllSubjects();
582
+ const matches = [];
583
+ for (const subject of subjects) {
584
+ const fullMarkdown = buildSubjectsMarkdown([subject]);
585
+ const renderedLines = (await marked.parse(fullMarkdown)).split("\n");
586
+ for (const block of subject.prelude) if (block.id === id) {
587
+ let startLine;
588
+ let endLine;
589
+ const codeLines = block.code.split("\n");
590
+ const firstCodeLine = codeLines[0].trim();
591
+ for (let i = 0; i < renderedLines.length; i++) if (stripVTControlCharacters(renderedLines[i]).trim() === firstCodeLine) {
592
+ startLine = i + 1;
593
+ endLine = i + codeLines.length;
594
+ break;
595
+ }
596
+ matches.push({
597
+ subjectId: subject.id,
598
+ subjectTitle: subject.title,
599
+ section: "Prelude",
600
+ code: block.code,
601
+ type: "prelude",
602
+ startLine,
603
+ endLine
604
+ });
605
+ }
606
+ for (const example of subject.examples) if (example.id === id) {
607
+ let sectionName = "Unknown Section";
608
+ let startLine;
609
+ let endLine;
610
+ for (const section of subject.sections) if (section.content.includes(example.code.substring(0, Math.min(50, example.code.length)))) {
611
+ sectionName = section.heading;
612
+ const codeLines = example.code.split("\n");
613
+ const firstCodeLine = codeLines[0].trim();
614
+ for (let i = 0; i < renderedLines.length; i++) if (stripVTControlCharacters(renderedLines[i]).trim() === firstCodeLine) {
615
+ startLine = i + 1;
616
+ endLine = i + codeLines.length;
617
+ break;
618
+ }
619
+ break;
620
+ }
621
+ matches.push({
622
+ subjectId: subject.id,
623
+ subjectTitle: subject.title,
624
+ section: sectionName,
625
+ code: example.code,
626
+ type: "example",
627
+ startLine,
628
+ endLine
629
+ });
426
630
  }
427
- console.log();
631
+ }
632
+ if (matches.length === 0) {
633
+ console.error(`Error: No code block found with id "${id}"`);
634
+ if (topics.length > 0) console.error(`Searched in topics: ${topics.join(", ")}`);
635
+ else console.error("Searched in all available topics");
636
+ process.exit(1);
637
+ }
638
+ for (let i = 0; i < matches.length; i++) {
639
+ const match = matches[i];
640
+ if (matches.length > 1 && i > 0) console.log("\n---\n");
641
+ let matchMarkdown = `# ${match.subjectTitle}\n\n`;
642
+ matchMarkdown += `## ${match.section}\n\n`;
643
+ if (showLineNumbers && match.startLine && match.endLine) console.log(`Lines ${match.startLine}-${match.endLine} (use with --start/--end)\n`);
644
+ matchMarkdown += `\`\`\`typescript\n${match.code}\n\`\`\`\n`;
645
+ const rendered = await marked.parse(matchMarkdown);
646
+ console.log(rendered);
428
647
  }
429
648
  }
430
649
  /**
431
650
  * Print information about the corpus command
432
651
  */
433
652
  function printCorpusHelp() {
434
- console.log("Fragno Corpus - Code examples and documentation");
653
+ console.log("Fragno Corpus - Code examples and documentation (similar to LLMs.txt");
654
+ console.log("");
655
+ console.log("Usage: fragno-cli corpus [options] [topic...]");
435
656
  console.log("");
436
- console.log("Usage: fragno-cli corpus [topic...]");
657
+ console.log("Options:");
658
+ console.log(" -n, --no-line-numbers Hide line numbers (shown by default)");
659
+ console.log(" -s, --start N Starting line number to display from");
660
+ console.log(" -e, --end N Ending line number to display to");
661
+ console.log(" --headings Show only headings and code block IDs");
662
+ console.log(" --id <id> Retrieve a specific code block by ID");
437
663
  console.log("");
438
664
  console.log("Examples:");
439
665
  console.log(" fragno-cli corpus # List all available topics");
440
666
  console.log(" fragno-cli corpus defining-routes # Show route definition examples");
667
+ console.log(" fragno-cli corpus --headings database-querying");
668
+ console.log(" # Show structure overview");
669
+ console.log(" fragno-cli corpus --start 10 --end 50 database-querying");
670
+ console.log(" # Show specific lines");
671
+ console.log(" fragno-cli corpus --id create-user # Get code block by ID");
441
672
  console.log(" fragno-cli corpus database-adapters kysely-adapter");
442
673
  console.log(" # Show multiple topics");
443
674
  console.log("");
444
675
  console.log("Available topics:");
445
676
  const subjects = getSubjects();
446
- for (const subject of subjects) console.log(` ${subject.id.padEnd(30)} ${subject.title}`);
677
+ const rootSubjects = [];
678
+ const subjectMap = new Map(subjects.map((s) => [s.id, s]));
679
+ for (const subject of subjects) if (!getSubjectParent(subject.id)) {
680
+ const children = getSubjectChildren(subject.id);
681
+ rootSubjects.push({
682
+ id: subject.id,
683
+ title: subject.title,
684
+ children: children.map((childId) => ({
685
+ id: childId,
686
+ title: subjectMap.get(childId)?.title || childId
687
+ }))
688
+ });
689
+ }
690
+ for (const root of rootSubjects) {
691
+ console.log(` ${root.id.padEnd(30)} ${root.title}`);
692
+ for (let i = 0; i < root.children.length; i++) {
693
+ const child = root.children[i];
694
+ const connector = i === root.children.length - 1 ? "└─" : "├─";
695
+ console.log(` ${connector} ${child.id.padEnd(26)} ${child.title}`);
696
+ }
697
+ }
447
698
  }
448
699
  const corpusCommand = define({
449
700
  name: "corpus",
450
701
  description: "View code examples and documentation for Fragno",
451
- run: (ctx) => {
702
+ args: {
703
+ "no-line-numbers": {
704
+ type: "boolean",
705
+ short: "n",
706
+ description: "Hide line numbers (line numbers are shown by default)"
707
+ },
708
+ start: {
709
+ type: "number",
710
+ short: "s",
711
+ description: "Starting line number (1-based) to display from"
712
+ },
713
+ end: {
714
+ type: "number",
715
+ short: "e",
716
+ description: "Ending line number (1-based) to display to"
717
+ },
718
+ headings: {
719
+ type: "boolean",
720
+ description: "Show only section headings and code block IDs with line numbers"
721
+ },
722
+ id: {
723
+ type: "string",
724
+ description: "Retrieve a specific code block by ID"
725
+ }
726
+ },
727
+ run: async (ctx) => {
452
728
  const topics = ctx.positionals;
729
+ const showLineNumbers = !(ctx.values["no-line-numbers"] ?? false);
730
+ const startLine = ctx.values.start;
731
+ const endLine = ctx.values.end;
732
+ const headingsOnly = ctx.values.headings ?? false;
733
+ const codeBlockId = ctx.values.id;
734
+ if (codeBlockId) {
735
+ await printCodeBlockById(codeBlockId, topics, showLineNumbers);
736
+ return;
737
+ }
453
738
  if (topics.length === 0) {
454
739
  printCorpusHelp();
455
740
  return;
456
741
  }
742
+ if (startLine !== void 0 && endLine !== void 0 && startLine > endLine) {
743
+ console.error("Error: --start must be less than or equal to --end");
744
+ process.exit(1);
745
+ }
457
746
  try {
458
- const subjects = getSubject(...topics);
459
- for (const subject of subjects) printSubject(subject);
460
- console.log(`${"=".repeat(60)}`);
461
- console.log(`Displayed ${subjects.length} topic(s)`);
462
- console.log(`${"=".repeat(60)}\n`);
747
+ await printSubjects(getSubject(...topics), {
748
+ showLineNumbers,
749
+ startLine,
750
+ endLine,
751
+ headingsOnly
752
+ });
463
753
  } catch (error) {
464
754
  console.error("Error loading topics:", error instanceof Error ? error.message : error);
465
755
  console.log("\nRun 'fragno-cli corpus' to see available topics.");
@@ -470,75 +760,91 @@ const corpusCommand = define({
470
760
 
471
761
  //#endregion
472
762
  //#region src/cli.ts
763
+ const __dirname = dirname(fileURLToPath(import.meta.url));
764
+ const version = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8")).version;
473
765
  const dbSubCommands = /* @__PURE__ */ new Map();
474
766
  dbSubCommands.set("generate", generateCommand);
475
767
  dbSubCommands.set("migrate", migrateCommand);
476
768
  dbSubCommands.set("info", infoCommand);
477
- function printDbHelp() {
478
- console.log("Database management commands for Fragno");
479
- console.log("");
480
- console.log("Usage: fragno-cli db <command> [options]");
481
- console.log("");
482
- console.log("Commands:");
483
- console.log(" generate Generate schema files from FragnoDatabase definitions");
484
- console.log(" migrate Run database migrations");
485
- console.log(" info Display database information and migration status");
486
- console.log("");
487
- console.log("Run 'fragno-cli db <command> --help' for more information.");
488
- }
489
769
  const dbCommand = define({
490
770
  name: "db",
491
- description: "Database management commands",
492
- run: printDbHelp
771
+ description: "Database management commands"
493
772
  });
494
- const rootSubCommands = /* @__PURE__ */ new Map();
495
- rootSubCommands.set("db", dbCommand);
496
- rootSubCommands.set("search", searchCommand);
497
- rootSubCommands.set("corpus", corpusCommand);
498
773
  const mainCommand = define({
499
774
  name: "fragno-cli",
500
- description: "Fragno CLI - Tools for working with Fragno fragments",
501
- run: () => {
502
- console.log("Fragno CLI - Tools for working with Fragno fragments");
503
- console.log("");
504
- console.log("Usage: fragno-cli <command> [options]");
505
- console.log("");
506
- console.log("Commands:");
507
- console.log(" db Database management commands");
508
- console.log(" search Search the Fragno documentation");
509
- console.log(" corpus View code examples and documentation");
510
- console.log("");
511
- console.log("Run 'fragno-cli <command> --help' for more information.");
512
- }
775
+ description: "Tools for working with Fragno fragments"
513
776
  });
514
777
  if (import.meta.main) try {
515
778
  const args = process.argv.slice(2);
516
- if (args[0] === "search") await cli(args.slice(1), searchCommand);
517
- else if (args[0] === "corpus") await cli(args.slice(1), corpusCommand);
518
- else if (args[0] === "db" && args.length > 1) {
779
+ if (args[0] === "search") await cli(args.slice(1), searchCommand, {
780
+ name: "fragno-cli search",
781
+ version
782
+ });
783
+ else if (args[0] === "corpus") await cli(args.slice(1), corpusCommand, {
784
+ name: "fragno-cli corpus",
785
+ version
786
+ });
787
+ else if (args[0] === "db") {
519
788
  const subCommandName = args[1];
520
- if (subCommandName === "--help" || subCommandName === "-h") {
521
- printDbHelp();
522
- process.exit(0);
523
- }
524
- const subCommand = dbSubCommands.get(subCommandName);
525
- if (!subCommand) {
526
- console.error(`Unknown command: ${subCommandName}`);
789
+ if (!subCommandName || subCommandName === "--help" || subCommandName === "-h") {
790
+ console.log("Database management commands");
527
791
  console.log("");
528
- printDbHelp();
529
- process.exit(1);
530
- }
531
- const subArgs = args.slice(2);
532
- const isSubCommandHelp = subArgs.includes("--help") || subArgs.includes("-h");
533
- let hasValidationError = false;
534
- if (!isSubCommandHelp && subCommand.args) {
535
- const tokens = parseArgs(subArgs);
536
- hasValidationError = !!resolveArgs(subCommand.args, tokens).error;
792
+ console.log("USAGE:");
793
+ console.log(" fragno-cli db <COMMAND>");
794
+ console.log("");
795
+ console.log("COMMANDS:");
796
+ console.log(" generate Generate schema files from FragnoDatabase definitions");
797
+ console.log(" migrate Run database migrations");
798
+ console.log(" info Display database information and migration status");
799
+ console.log("");
800
+ console.log("For more info, run any command with the `--help` flag:");
801
+ console.log(" fragno-cli db generate --help");
802
+ console.log(" fragno-cli db migrate --help");
803
+ console.log(" fragno-cli db info --help");
804
+ console.log("");
805
+ console.log("OPTIONS:");
806
+ console.log(" -h, --help Display this help message");
807
+ console.log(" -v, --version Display this version");
808
+ } else if (subCommandName === "--version" || subCommandName === "-v") console.log(version);
809
+ else {
810
+ const subCommand = dbSubCommands.get(subCommandName);
811
+ if (!subCommand) {
812
+ console.error(`Unknown command: ${subCommandName}`);
813
+ console.log("");
814
+ console.log("Run 'fragno-cli db --help' for available commands.");
815
+ process.exit(1);
816
+ }
817
+ await cli(args.slice(2), subCommand, {
818
+ name: `fragno-cli db ${subCommandName}`,
819
+ version
820
+ });
537
821
  }
538
- await cli(subArgs, subCommand);
539
- if (hasValidationError) process.exit(1);
540
- } else if (args[0] === "db") printDbHelp();
541
- else await cli(args, mainCommand, { subCommands: rootSubCommands });
822
+ } else if (!args.length || args[0] === "--help" || args[0] === "-h") {
823
+ console.log("Tools for working with Fragno");
824
+ console.log("");
825
+ console.log("USAGE:");
826
+ console.log(" fragno-cli <COMMAND>");
827
+ console.log("");
828
+ console.log("COMMANDS:");
829
+ console.log(" db Database management commands");
830
+ console.log(" search Search the Fragno documentation");
831
+ console.log(" corpus View code examples and documentation for Fragno");
832
+ console.log("");
833
+ console.log("For more info, run any command with the `--help` flag:");
834
+ console.log(" fragno-cli db --help");
835
+ console.log(" fragno-cli search --help");
836
+ console.log(" fragno-cli corpus --help");
837
+ console.log("");
838
+ console.log("OPTIONS:");
839
+ console.log(" -h, --help Display this help message");
840
+ console.log(" -v, --version Display this version");
841
+ } else if (args[0] === "--version" || args[0] === "-v") console.log(version);
842
+ else {
843
+ console.error(`Unknown command: ${args[0]}`);
844
+ console.log("");
845
+ console.log("Run 'fragno-cli --help' for available commands.");
846
+ process.exit(1);
847
+ }
542
848
  } catch (error) {
543
849
  console.error("Error:", error instanceof Error ? error.message : error);
544
850
  process.exit(1);