blytz 1.0.4 → 1.1.1

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/bin/cli.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  import fs from 'fs';
4
4
  import path from 'path';
5
+ import readline from 'readline/promises';
5
6
 
6
7
  import processReadme from '../src/processReadme.js';
7
8
 
@@ -49,6 +50,18 @@ function collectDependencies(packageJson) {
49
50
  return Object.keys(packageJson.dependencies || {}).sort();
50
51
  }
51
52
 
53
+ function collectPythonDependencies(requirementsContent) {
54
+ return requirementsContent
55
+ .split(/\r?\n/)
56
+ .map(line => line.trim())
57
+ .filter(line => line && !line.startsWith('#'))
58
+ .filter(line => !line.startsWith('-'))
59
+ .map(line => line.split(';')[0].trim())
60
+ .map(line => line.split(/[=<>!~]/)[0].trim())
61
+ .filter(Boolean)
62
+ .sort((left, right) => left.localeCompare(right));
63
+ }
64
+
52
65
  function collectScripts(packageJson) {
53
66
  const scripts = new Map();
54
67
 
@@ -59,64 +72,122 @@ function collectScripts(packageJson) {
59
72
  return scripts;
60
73
  }
61
74
 
62
- console.log("Scanning for project files...");
75
+ function getLicenseName(licenseContent) {
76
+ const firstLine = licenseContent
77
+ .split(/\r?\n/)
78
+ .map(line => line.trim())
79
+ .find(Boolean);
63
80
 
64
- const targetDir = process.cwd();
65
- const readmePath = path.join(targetDir, 'README.md');
66
- const packageJsonPath = path.join(targetDir, 'package.json');
67
- const readmeExists = fs.existsSync(readmePath);
68
-
69
- if (!readmeExists && !shouldInit && !shouldForce) {
70
- console.error("Error: No README.md found in this directory. Try --init.");
71
- process.exit(1);
81
+ return firstLine || '';
72
82
  }
73
83
 
74
- if (readmeExists && shouldInit && !shouldForce) {
75
- console.error("README.md already exists. Try --force.");
76
- process.exit(1);
84
+ async function promptForTitleAndDescription() {
85
+ const rl = readline.createInterface({
86
+ input: process.stdin,
87
+ output: process.stdout,
88
+ });
89
+
90
+ try {
91
+ const titleContent = (await rl.question('Add title: ')).trim();
92
+ const descriptionContent = (await rl.question('Add description: ')).trim();
93
+ return { titleContent, descriptionContent };
94
+ } finally {
95
+ rl.close();
96
+ }
77
97
  }
78
98
 
79
- if (!fs.existsSync(packageJsonPath)) {
80
- console.error("Error: No package.json found in this directory.");
81
- process.exit(1);
82
- }
99
+ async function main() {
100
+ console.log("Scanning for project files...");
101
+
102
+ const targetDir = process.cwd();
103
+ const readmePath = path.join(targetDir, 'README.md');
104
+ const packageJsonPath = path.join(targetDir, 'package.json');
105
+ const requirementsPath = path.join(targetDir, 'requirements.txt');
106
+ const licensePath = path.join(targetDir, 'LICENSE');
107
+ const readmeExists = fs.existsSync(readmePath);
108
+ const hasPackageJson = fs.existsSync(packageJsonPath);
109
+ const hasRequirements = fs.existsSync(requirementsPath);
110
+ const hasLicense = fs.existsSync(licensePath);
111
+
112
+ if (!readmeExists && !shouldInit && !shouldForce) {
113
+ console.error("Error: No README.md found in this directory. Try --init.");
114
+ process.exit(1);
115
+ }
83
116
 
84
- console.log("Files found. Processing README...");
117
+ if (readmeExists && shouldInit && !shouldForce) {
118
+ console.error("README.md already exists. Try --force.");
119
+ process.exit(1);
120
+ }
121
+
122
+ if (!hasPackageJson && !hasRequirements) {
123
+ console.error("Error: No package.json or requirements.txt found in this directory.");
124
+ process.exit(1);
125
+ }
126
+
127
+ console.log("Files found. Processing README...");
85
128
 
86
- try {
87
129
  if (shouldForce && readmeExists) {
88
130
  fs.unlinkSync(readmePath);
89
131
  }
90
132
 
91
- // 1. Read the raw text of both files
92
133
  const readmeContent = fs.existsSync(readmePath) ? fs.readFileSync(readmePath, 'utf-8') : '';
93
- const packageJsonData = fs.readFileSync(packageJsonPath, 'utf-8');
94
-
95
- // 2. Parse package.json to build the context object your engine expects
96
- const packageJson = JSON.parse(packageJsonData);
97
134
  const fileTree = buildFileTree(targetDir);
98
- const context = {
99
- packageJson,
100
- packages: [{ path: 'package.json', content: packageJson }],
101
- dependencies: collectDependencies(packageJson),
102
- scripts: collectScripts(packageJson),
103
- fileTree,
104
- username: packageJson.author || 'Unknown Author',
105
- projectName: packageJson.name || 'this project',
106
- hasPackageJson: true,
107
- isMonorepo: false
108
- };
109
-
110
- // 3. Feed everything into your pure engine
111
- // Hardcoding 'node' as projectType for now since we rely on package.json
112
- const updatedReadme = processReadme(readmeContent, 'node', context);
113
-
114
- // 4. Overwrite the existing README.md with the new content
135
+ const projectName = path.basename(targetDir);
136
+ const licenseName = hasLicense ? getLicenseName(fs.readFileSync(licensePath, 'utf-8')) : '';
137
+ const shouldPromptMetadata = !shouldUpdate;
138
+ const { titleContent, descriptionContent } = shouldPromptMetadata
139
+ ? await promptForTitleAndDescription()
140
+ : { titleContent: '', descriptionContent: '' };
141
+ let context;
142
+ let projectType;
143
+
144
+ if (hasPackageJson) {
145
+ const packageJsonData = fs.readFileSync(packageJsonPath, 'utf-8');
146
+ const packageJson = JSON.parse(packageJsonData);
147
+
148
+ context = {
149
+ packageJson,
150
+ packages: [{ path: 'package.json', content: packageJson }],
151
+ dependencies: collectDependencies(packageJson),
152
+ scripts: collectScripts(packageJson),
153
+ fileTree,
154
+ titleContent,
155
+ descriptionContent,
156
+ licenseName,
157
+ username: packageJson.author || process.env.USERNAME || 'Unknown Author',
158
+ projectName: packageJson.name || projectName,
159
+ hasPackageJson: true,
160
+ isMonorepo: false
161
+ };
162
+ projectType = 'node';
163
+ } else {
164
+ const requirementsContent = fs.readFileSync(requirementsPath, 'utf-8');
165
+
166
+ context = {
167
+ packages: [{ path: 'requirements.txt', content: requirementsContent }],
168
+ dependencies: collectPythonDependencies(requirementsContent),
169
+ scripts: new Map(),
170
+ fileTree,
171
+ titleContent,
172
+ descriptionContent,
173
+ licenseName,
174
+ username: process.env.USERNAME || 'Unknown Author',
175
+ projectName,
176
+ hasPackageJson: false,
177
+ isMonorepo: false
178
+ };
179
+ projectType = 'python';
180
+ }
181
+
182
+ const updatedReadme = processReadme(readmeContent, projectType, context);
183
+
115
184
  fs.writeFileSync(readmePath, updatedReadme, 'utf-8');
116
185
 
117
186
  console.log("Success! README.md has been auto-fixed.");
187
+ }
118
188
 
119
- } catch (error) {
120
- console.error("An error occurred during processing:", error.message);
121
- process.exit(1);
122
- }
189
+ (main()
190
+ .catch(error => {
191
+ console.error("An error occurred during processing:", error.message);
192
+ process.exit(1);
193
+ }));
package/package.json CHANGED
@@ -1,49 +1,49 @@
1
- {
2
- "name": "blytz",
3
- "version": "1.0.4",
4
- "bin": {
5
- "blytz": "./bin/cli.js"
6
- },
7
- "readme": "README-NPM.md",
8
- "files": [
9
- "bin/",
10
- "src/",
11
- "bin/README-NPM.md"
12
- ],
13
- "description": "An automated CLI tool to fix and maintain project READMEs",
14
- "type": "module",
15
- "scripts": {
16
- "start": "node src/index.js",
17
- "prepublishOnly": "node scripts/sync-readme.js prepublish",
18
- "postpublish": "node scripts/sync-readme.js postpublish"
19
- },
20
- "keywords": [
21
- "blytz",
22
- "cli",
23
- "readme",
24
- "documentation",
25
- "automation",
26
- "github",
27
- "npm",
28
- "bot",
29
- "readmefixer",
30
- "readme-generator",
31
- "devtools",
32
- "markdown",
33
- "workflow",
34
- "productivity",
35
- "automated-docs",
36
- "repository-manager",
37
- "scaffold",
38
- "readme-maintainer",
39
- "readme-updater"
40
- ],
41
- "author": "Aryan Sharma",
42
- "license": "MIT",
43
- "dependencies": {
44
- "@octokit/app": "^16.1.2",
45
- "@octokit/rest": "^22.0.1",
46
- "dotenv": "^17.4.0",
47
- "express": "^5.2.1"
48
- }
49
- }
1
+ {
2
+ "name": "blytz",
3
+ "version": "1.1.1",
4
+ "bin": {
5
+ "blytz": "./bin/cli.js"
6
+ },
7
+ "readme": "README-NPM.md",
8
+ "files": [
9
+ "bin/",
10
+ "src/",
11
+ "bin/README-NPM.md"
12
+ ],
13
+ "description": "An automated CLI tool to fix and maintain project READMEs",
14
+ "type": "module",
15
+ "scripts": {
16
+ "start": "node src/index.js",
17
+ "prepublishOnly": "node scripts/sync-readme.js prepublish",
18
+ "postpublish": "node scripts/sync-readme.js postpublish"
19
+ },
20
+ "keywords": [
21
+ "blytz",
22
+ "cli",
23
+ "readme",
24
+ "documentation",
25
+ "automation",
26
+ "github",
27
+ "npm",
28
+ "bot",
29
+ "readmefixer",
30
+ "readme-generator",
31
+ "devtools",
32
+ "markdown",
33
+ "workflow",
34
+ "productivity",
35
+ "automated-docs",
36
+ "repository-manager",
37
+ "scaffold",
38
+ "readme-maintainer",
39
+ "readme-updater"
40
+ ],
41
+ "author": "Aryan Sharma",
42
+ "license": "MIT",
43
+ "dependencies": {
44
+ "@octokit/app": "^16.1.2",
45
+ "@octokit/rest": "^22.0.1",
46
+ "dotenv": "^17.4.0",
47
+ "express": "^5.2.1"
48
+ }
49
+ }
@@ -2,16 +2,18 @@ import getDefaultContent from "./template.js";
2
2
 
3
3
 
4
4
  //Core engine: processes README content and returns updated version
5
- export default function processReadme(content, projectType, context = {}) {
6
-
7
- const normalizedContent = (content || "").replace(/^##(?=\S)/gm, "## ");
5
+ export default function processReadme(content, projectType, context = {}) {
6
+ const { titleContent = "" } = context ?? {};
7
+
8
+ const normalizedContent = (content || "").replace(/^##(?=\S)/gm, "## ");
8
9
 
9
10
  // Split into sections
10
11
  const sections = normalizedContent.split("## ");
11
12
  const sectionMap = {};
12
13
 
13
14
  // Extract intro (title + description)
14
- const intro = sections[0].trim();
15
+ const intro = sections[0].trim();
16
+ const finalIntro = titleContent ? `# ${titleContent}` : intro;
15
17
 
16
18
  // Parse sections
17
19
  sections.slice(1).forEach(section => {
@@ -65,7 +67,7 @@ export default function processReadme(content, projectType, context = {}) {
65
67
  .join(" ");
66
68
 
67
69
  // Rebuild README
68
- let newReadme = intro ? intro + "\n\n" : "";
70
+ let newReadme = finalIntro ? finalIntro + "\n\n" : "";
69
71
 
70
72
  // Ordered sections
71
73
  requiredSections.forEach(section => {
@@ -80,4 +82,4 @@ export default function processReadme(content, projectType, context = {}) {
80
82
  });
81
83
 
82
84
  return newReadme.trim();
83
- }
85
+ }
package/src/template.js CHANGED
@@ -1,22 +1,24 @@
1
1
  import getProjectStructure from "./fileTree.js";
2
2
 
3
3
  export default function getDefaultContent(section, projectType, context = {}) {
4
- const {
5
- packages = [],
6
- dependencies = [],
7
- scripts = new Map(),
8
- fileTree = null,
9
- username = "Unknown",
10
- projectName = "this project",
11
- isMonorepo = false,
12
- } = context ?? {};
4
+ const {
5
+ packages = [],
6
+ dependencies = [],
7
+ scripts = new Map(),
8
+ fileTree = null,
9
+ descriptionContent = "",
10
+ licenseName = "",
11
+ username = "Unknown",
12
+ projectName = "this project",
13
+ isMonorepo = false,
14
+ } = context ?? {};
13
15
 
14
16
  const safeProjectType = projectType || "unknown";
15
17
  const safeSection = (section || "").toLowerCase().trim();
16
18
 
17
- switch (safeSection) {
18
- case "description":
19
- return getDescriptionContent(safeProjectType, projectName, isMonorepo);
19
+ switch (safeSection) {
20
+ case "description":
21
+ return getDescriptionContent(safeProjectType, projectName, isMonorepo, descriptionContent);
20
22
  case "installation":
21
23
  return getInstallationContent(safeProjectType, packages, isMonorepo);
22
24
  case "usage":
@@ -25,8 +27,8 @@ export default function getDefaultContent(section, projectType, context = {}) {
25
27
  return getDependenciesContent(dependencies, packages);
26
28
  case "folder structure":
27
29
  return getFolderStructureContent(fileTree);
28
- case "license":
29
- return "Add your license information here.";
30
+ case "license":
31
+ return getLicenseContent(licenseName);
30
32
  case "built by":
31
33
  return `Built with ❤️ by @${(username || "Unknown").trim()}`;
32
34
  default:
@@ -34,8 +36,12 @@ export default function getDefaultContent(section, projectType, context = {}) {
34
36
  }
35
37
  }
36
38
 
37
- function getDescriptionContent(projectType, projectName, isMonorepo) {
38
- const name = projectName || "this project";
39
+ function getDescriptionContent(projectType, projectName, isMonorepo, descriptionContent) {
40
+ if (descriptionContent) {
41
+ return descriptionContent;
42
+ }
43
+
44
+ const name = projectName || "this project";
39
45
  if (projectType === "node") {
40
46
  if (isMonorepo) {
41
47
  return `${name} is a Node.js monorepo containing multiple packages. Add a brief description of its purpose and what problem it solves.`;
@@ -93,21 +99,28 @@ function getUsageContent(projectType, scripts, isMonorepo) {
93
99
  return "Add usage instructions here.";
94
100
  }
95
101
 
96
- function getDependenciesContent(dependencies, packages) {
97
- if (dependencies && dependencies.length > 0) {
98
- const isMonorepo = packages && packages.length > 1;
99
- const header = isMonorepo
100
- ? `This project uses the following dependencies (across ${packages.length} packages):\n\n`
102
+ function getDependenciesContent(dependencies, packages) {
103
+ if (dependencies && dependencies.length > 0) {
104
+ const isMonorepo = packages && packages.length > 1;
105
+ const header = isMonorepo
106
+ ? `This project uses the following dependencies (across ${packages.length} packages):\n\n`
101
107
  : "This project uses the following dependencies:\n\n";
102
108
 
103
109
  return header + dependencies.map(d => `- ${d}`).join("\n");
104
- }
105
- return "No dependencies found.";
106
- }
107
-
108
- function getFolderStructureContent(fileTree) {
110
+ }
111
+ return "No dependencies found.";
112
+ }
113
+
114
+ function getLicenseContent(licenseName) {
115
+ if (licenseName) {
116
+ return `This project is licensed under the ${licenseName}. See the LICENSE file for details.`;
117
+ }
118
+ return "Add your license information here.";
119
+ }
120
+
121
+ function getFolderStructureContent(fileTree) {
109
122
  if (!fileTree || typeof fileTree !== "object" || Object.keys(fileTree).length === 0) {
110
123
  return "Project structure:\n\n```\n(No file tree provided)\n```";
111
124
  }
112
125
  return "Project structure:\n\n" + getProjectStructure(fileTree);
113
- }
126
+ }