@fragno-dev/cli 0.1.16 → 0.1.18

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.
@@ -5,6 +5,9 @@ import {
5
5
  getAllSubjects,
6
6
  getSubjectParent,
7
7
  getSubjectChildren,
8
+ getAllSubjectIdsInOrder,
9
+ isCategory,
10
+ getCategoryTitle,
8
11
  } from "@fragno-dev/corpus";
9
12
  import type { Subject, Example } from "@fragno-dev/corpus";
10
13
  import { marked } from "marked";
@@ -25,7 +28,7 @@ interface PrintOptions {
25
28
  /**
26
29
  * Build markdown content for multiple subjects
27
30
  */
28
- function buildSubjectsMarkdown(subjects: Subject[]): string {
31
+ export function buildSubjectsMarkdown(subjects: Subject[]): string {
29
32
  let fullMarkdown = "";
30
33
 
31
34
  for (const subject of subjects) {
@@ -61,7 +64,7 @@ function buildSubjectsMarkdown(subjects: Subject[]): string {
61
64
  /**
62
65
  * Add line numbers to content
63
66
  */
64
- function addLineNumbers(content: string, startFrom: number = 1): string {
67
+ export function addLineNumbers(content: string, startFrom: number = 1): string {
65
68
  const lines = content.split("\n");
66
69
  const maxDigits = String(startFrom + lines.length - 1).length;
67
70
 
@@ -77,7 +80,7 @@ function addLineNumbers(content: string, startFrom: number = 1): string {
77
80
  /**
78
81
  * Filter content by line range
79
82
  */
80
- function filterByLineRange(content: string, startLine: number, endLine: number): string {
83
+ export function filterByLineRange(content: string, startLine: number, endLine: number): string {
81
84
  const lines = content.split("\n");
82
85
  // Convert to 0-based index
83
86
  const start = Math.max(0, startLine - 1);
@@ -88,7 +91,7 @@ function filterByLineRange(content: string, startLine: number, endLine: number):
88
91
  /**
89
92
  * Extract headings and code block information with line numbers
90
93
  */
91
- function extractHeadingsAndBlocks(subjects: Subject[]): string {
94
+ export function extractHeadingsAndBlocks(subjects: Subject[]): string {
92
95
  let output = "";
93
96
  let currentLine = 1;
94
97
  let lastOutputLine = 0;
@@ -442,6 +445,52 @@ async function printCodeBlockById(
442
445
  }
443
446
  }
444
447
 
448
+ /**
449
+ * Print only the topic tree
450
+ */
451
+ function printTopicTree(): void {
452
+ const subjects = getSubjects();
453
+ const subjectMap = new Map(subjects.map((s) => [s.id, s]));
454
+
455
+ // Helper function to get title for any subject ID (including categories)
456
+ function getTitle(subjectId: string): string {
457
+ if (isCategory(subjectId)) {
458
+ return getCategoryTitle(subjectId);
459
+ }
460
+ const subject = subjectMap.get(subjectId);
461
+ return subject ? subject.title : subjectId;
462
+ }
463
+
464
+ // Helper function to recursively display tree
465
+ function displayNode(subjectId: string, indent: string, isLast: boolean, isRoot: boolean): void {
466
+ const title = getTitle(subjectId);
467
+
468
+ if (isRoot) {
469
+ console.log(` ${subjectId.padEnd(30)} ${title}`);
470
+ } else {
471
+ const connector = isLast ? "└─" : "├─";
472
+ console.log(`${indent}${connector} ${subjectId.padEnd(26)} ${title}`);
473
+ }
474
+
475
+ const children = getSubjectChildren(subjectId);
476
+ if (children.length > 0) {
477
+ const childIndent = isRoot ? " " : indent + (isLast ? " " : "│ ");
478
+ for (let i = 0; i < children.length; i++) {
479
+ displayNode(children[i], childIndent, i === children.length - 1, false);
480
+ }
481
+ }
482
+ }
483
+
484
+ // Get all root subject IDs (including categories)
485
+ const allIds = getAllSubjectIdsInOrder();
486
+ const rootIds = allIds.filter((id) => !getSubjectParent(id));
487
+
488
+ // Display root subjects
489
+ for (const subjectId of rootIds) {
490
+ displayNode(subjectId, "", false, true);
491
+ }
492
+ }
493
+
445
494
  /**
446
495
  * Print information about the corpus command
447
496
  */
@@ -456,9 +505,11 @@ function printCorpusHelp(): void {
456
505
  console.log(" -e, --end N Ending line number to display to");
457
506
  console.log(" --headings Show only headings and code block IDs");
458
507
  console.log(" --id <id> Retrieve a specific code block by ID");
508
+ console.log(" --tree Show only the topic tree");
459
509
  console.log("");
460
510
  console.log("Examples:");
461
511
  console.log(" fragno-cli corpus # List all available topics");
512
+ console.log(" fragno-cli corpus --tree # Show only the topic tree");
462
513
  console.log(" fragno-cli corpus defining-routes # Show route definition examples");
463
514
  console.log(" fragno-cli corpus --headings database-querying");
464
515
  console.log(" # Show structure overview");
@@ -470,42 +521,7 @@ function printCorpusHelp(): void {
470
521
  console.log("");
471
522
  console.log("Available topics:");
472
523
 
473
- const subjects = getSubjects();
474
-
475
- // Group subjects by their tree structure
476
- const rootSubjects: Array<{
477
- id: string;
478
- title: string;
479
- children: Array<{ id: string; title: string }>;
480
- }> = [];
481
- const subjectMap = new Map(subjects.map((s) => [s.id, s]));
482
-
483
- for (const subject of subjects) {
484
- const parent = getSubjectParent(subject.id);
485
- if (!parent) {
486
- // This is a root subject
487
- const children = getSubjectChildren(subject.id);
488
- rootSubjects.push({
489
- id: subject.id,
490
- title: subject.title,
491
- children: children.map((childId) => ({
492
- id: childId,
493
- title: subjectMap.get(childId)?.title || childId,
494
- })),
495
- });
496
- }
497
- }
498
-
499
- // Display in tree format
500
- for (const root of rootSubjects) {
501
- console.log(` ${root.id.padEnd(30)} ${root.title}`);
502
- for (let i = 0; i < root.children.length; i++) {
503
- const child = root.children[i];
504
- const isLast = i === root.children.length - 1;
505
- const connector = isLast ? "└─" : "├─";
506
- console.log(` ${connector} ${child.id.padEnd(26)} ${child.title}`);
507
- }
508
- }
524
+ printTopicTree();
509
525
  }
510
526
 
511
527
  export const corpusCommand = define({
@@ -535,6 +551,10 @@ export const corpusCommand = define({
535
551
  type: "string",
536
552
  description: "Retrieve a specific code block by ID",
537
553
  },
554
+ tree: {
555
+ type: "boolean",
556
+ description: "Show only the topic tree (without help text)",
557
+ },
538
558
  },
539
559
  run: async (ctx) => {
540
560
  const topics = ctx.positionals;
@@ -543,6 +563,7 @@ export const corpusCommand = define({
543
563
  const endLine = ctx.values.end;
544
564
  const headingsOnly = ctx.values.headings ?? false;
545
565
  const codeBlockId = ctx.values.id;
566
+ const treeOnly = ctx.values.tree ?? false;
546
567
 
547
568
  // Handle --id flag
548
569
  if (codeBlockId) {
@@ -550,6 +571,12 @@ export const corpusCommand = define({
550
571
  return;
551
572
  }
552
573
 
574
+ // Handle --tree flag
575
+ if (treeOnly) {
576
+ printTopicTree();
577
+ return;
578
+ }
579
+
553
580
  // No topics provided - show help
554
581
  if (topics.length === 0) {
555
582
  printCorpusHelp();
@@ -573,8 +600,32 @@ export const corpusCommand = define({
573
600
  headingsOnly,
574
601
  });
575
602
  } catch (error) {
576
- console.error("Error loading topics:", error instanceof Error ? error.message : error);
577
- console.log("\nRun 'fragno-cli corpus' to see available topics.");
603
+ if (error instanceof Error && error.message.includes("ENOENT")) {
604
+ // Extract the subject name from the error message or use the topics array
605
+ const missingTopics = topics.filter((topic) => {
606
+ try {
607
+ getSubject(topic);
608
+ return false;
609
+ } catch {
610
+ return true;
611
+ }
612
+ });
613
+
614
+ if (missingTopics.length === 1) {
615
+ console.error(`Error: Subject '${missingTopics[0]}' not found.`);
616
+ } else if (missingTopics.length > 1) {
617
+ console.error(
618
+ `Error: Subjects not found: ${missingTopics.map((t) => `'${t}'`).join(", ")}`,
619
+ );
620
+ } else {
621
+ console.error("Error: One or more subjects not found.");
622
+ }
623
+ console.log("\nAvailable topics:");
624
+ printTopicTree();
625
+ } else {
626
+ console.error("Error loading topics:", error instanceof Error ? error.message : error);
627
+ console.log("\nRun 'fragno-cli corpus' to see available topics.");
628
+ }
578
629
  process.exit(1);
579
630
  }
580
631
  },