@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.
- package/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +24 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +58 -25
- package/dist/cli.js.map +1 -1
- package/package.json +12 -4
- package/src/commands/corpus.test.ts +1129 -0
- package/src/commands/corpus.ts +93 -42
package/src/commands/corpus.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
577
|
-
|
|
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
|
},
|