@gherk/requirements-extractor 1.0.6 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ import { type JsonRequirement } from "./types.js";
2
+ export declare function findAllMarkdownFiles(dir: string): string[];
3
+ export declare function parseMarkdownRequirements(files: string[], rootDir: string): JsonRequirement[];
4
+ export declare function generateJsonFromMarkdown(targetDir: string): Promise<void>;
5
+ //# sourceMappingURL=jsonify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonify.d.ts","sourceRoot":"","sources":["../src/jsonify.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAEtE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAe1D;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CAyD7F;AAED,wBAAsB,wBAAwB,CAAC,SAAS,EAAE,MAAM,iBAmB/D"}
@@ -0,0 +1,89 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { DEFAULT_CATEGORIES } from "./types.js";
4
+ export function findAllMarkdownFiles(dir) {
5
+ let results = [];
6
+ const list = fs.readdirSync(dir);
7
+ for (const file of list) {
8
+ const filePath = path.join(dir, file);
9
+ const stat = fs.statSync(filePath);
10
+ if (stat && stat.isDirectory()) {
11
+ results = results.concat(findAllMarkdownFiles(filePath));
12
+ }
13
+ else if (file.endsWith(".md") && !file.toLowerCase().includes("readme")) {
14
+ results.push(filePath);
15
+ }
16
+ }
17
+ return results;
18
+ }
19
+ export function parseMarkdownRequirements(files, rootDir) {
20
+ const requirements = [];
21
+ for (const filePath of files) {
22
+ const content = fs.readFileSync(filePath, "utf-8");
23
+ // Parse Table Metadata
24
+ const idMatch = content.match(/\|\s*\*\*Identificador\*\*\s*\|\s*(.*?)\s*\|/);
25
+ const titleMatch = content.match(/\|\s*\*\*Título\*\*\s*\|\s*(.*?)\s*\|/);
26
+ const typeMatch = content.match(/\|\s*\*\*Tipo\*\*\s*\|\s*(.*?)\s*\|/);
27
+ const sectionMatch = content.match(/\|\s*\*\*Sección\*\*\s*\|\s*(.*?)\s*\|/);
28
+ const pageMatch = content.match(/\|\s*\*\*Página\*\*\s*\|\s*(.*?)\s*\|/);
29
+ // Parse Sections
30
+ const objectiveMatch = content.match(/## Objetivo\s+([\s\S]*?)\s+---/);
31
+ const description = objectiveMatch ? objectiveMatch[1].trim() : "";
32
+ // Parse Acceptance Criteria
33
+ const criteriaLines = content.match(/^- \[ \] (.*)$/gm) || [];
34
+ const acceptance_criteria = criteriaLines.map(l => l.replace(/^- \[ \] /, "").trim());
35
+ // Process Categories
36
+ const sectionRaw = sectionMatch ? sectionMatch[1].trim() : "Backend";
37
+ // Split by comma or slash, clean names
38
+ const category_labels = sectionRaw.split(/[,/]/).map(s => s.trim());
39
+ // Map labels to IDs
40
+ const categories = category_labels.map(label => {
41
+ const cat = DEFAULT_CATEGORIES.find(c => c.label.toLowerCase() === label.toLowerCase());
42
+ return cat ? cat.id : label.toLowerCase();
43
+ });
44
+ // Determine primary category from directory structure relative to root
45
+ // e.g. root/backend/file.md -> backend
46
+ const relPath = path.relative(rootDir, filePath);
47
+ const dirParts = relPath.split(path.sep);
48
+ const primaryCategory = dirParts.length > 1 ? dirParts[0] : categories[0] || "backend";
49
+ const catObj = DEFAULT_CATEGORIES.find(c => c.id === primaryCategory);
50
+ if (idMatch && titleMatch) {
51
+ requirements.push({
52
+ id: idMatch[1].trim(),
53
+ title: titleMatch[1].trim(),
54
+ category: primaryCategory,
55
+ categories: categories,
56
+ category_label: catObj?.label || primaryCategory,
57
+ category_labels: category_labels,
58
+ type: typeMatch ? typeMatch[1].trim() : "Funcional",
59
+ page_range: pageMatch ? pageMatch[1].trim() : "",
60
+ description: description,
61
+ acceptance_criteria: acceptance_criteria
62
+ });
63
+ }
64
+ }
65
+ return requirements.sort((a, b) => a.id.localeCompare(b.id));
66
+ }
67
+ export async function generateJsonFromMarkdown(targetDir) {
68
+ console.log(`🔍 Scanning for Markdown files in: ${targetDir}`);
69
+ if (!fs.existsSync(targetDir)) {
70
+ console.error(`❌ Directory not found: ${targetDir}`);
71
+ throw new Error(`Directory not found: ${targetDir}`);
72
+ }
73
+ const files = findAllMarkdownFiles(targetDir);
74
+ console.log(`📄 Found ${files.length} markdown files.`);
75
+ const requirements = parseMarkdownRequirements(files, targetDir);
76
+ console.log(`📊 Parsed ${requirements.length} requirements.`);
77
+ const outputPath = path.join(targetDir, "requirements.json");
78
+ console.log(`💾 Writing to: ${outputPath}`);
79
+ fs.writeFileSync(outputPath, JSON.stringify(requirements, null, 2));
80
+ console.log(`✅ Done.`);
81
+ }
82
+ // Only run if called directly
83
+ import { fileURLToPath } from 'url';
84
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
85
+ const args = process.argv.slice(2);
86
+ const targetDir = args[0] || process.cwd();
87
+ generateJsonFromMarkdown(targetDir).catch(console.error);
88
+ }
89
+ //# sourceMappingURL=jsonify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonify.js","sourceRoot":"","sources":["../src/jsonify.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gherk/requirements-extractor",
3
- "version": "1.0.6",
3
+ "version": "1.1.0",
4
4
  "description": "MCP server that extracts, classifies and generates structured requirements from PDF documents using Ollama LLM",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -14,6 +14,7 @@
14
14
  "build": "tsc",
15
15
  "start": "node dist/index.js",
16
16
  "dev": "tsc --watch",
17
+ "jsonify": "node dist/jsonify.js",
17
18
  "prepublishOnly": "npm run build"
18
19
  },
19
20
  "publishConfig": {