blytz 1.0.0 → 1.0.2

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/README.md CHANGED
@@ -1,60 +1,105 @@
1
- ## Description
2
-
3
- diglet is a Node.js application. Add a brief description of its purpose and what problem it solves.
4
-
5
- ## Installation
6
-
7
- Follow these steps to install the project:
8
-
9
- ```bash
10
- npm install
11
- ```
12
-
13
- ## Usage
14
-
15
- You can run the following scripts:
16
-
17
- - `npm start`
18
-
19
- ## Dependencies
20
-
21
- This project uses the following dependencies:
22
-
23
- - @octokit/app
24
- - @octokit/rest
25
- - dotenv
26
- - express
27
-
28
- ## Folder Structure
29
-
30
- Project structure:
31
-
32
- ```
33
- ├── .env
34
- ├── .gitignore
35
- ├── bin
36
- │ └── cli.js
37
- ├── LICENSE
38
- ├── package-lock.json
39
- ├── package.json
40
- ├── README.md
41
- ├── server
42
- │ ├── analytics.js
43
- │ ├── bot.js
44
- │ ├── github.js
45
- │ └── server.js
46
- └── src
47
- ├── fileTree.js
48
- ├── index.js
49
- ├── processReadme.js
50
- ├── projectReader.js
51
- └── template.js
52
- ```
53
-
54
- ## License
55
-
56
- Add your license information here.
57
-
58
- ## Built By
59
-
60
- Built with ❤️ by @Aryan Sharma
1
+ # blytz
2
+
3
+ `blytz` is a small Node.js CLI that creates and updates a project's `README.md` from local project metadata.
4
+
5
+ ## What it does
6
+
7
+ - Updates an existing `README.md`
8
+ - Creates a new `README.md` with `--init`
9
+ - Replaces an existing `README.md` with `--force`
10
+ - Reads `package.json` to build README sections automatically
11
+ - Generates a basic folder tree for the project structure section
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm install -g blytz
17
+ ```
18
+
19
+ If you are developing locally from this repository, you can link it instead:
20
+
21
+ ```bash
22
+ npm link
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ Run the CLI from the root of the project you want to document:
28
+
29
+ ```bash
30
+ blytz
31
+ ```
32
+
33
+ You can also use the available flags:
34
+
35
+ ```bash
36
+ blytz --update
37
+ blytz --init
38
+ blytz --force
39
+ blytz --help
40
+ ```
41
+
42
+ ## Commands
43
+
44
+ ### `blytz`
45
+
46
+ Updates the existing `README.md` in the current directory.
47
+
48
+ ### `blytz --update`
49
+
50
+ Same as `blytz`. Refreshes the current `README.md` using the project metadata it finds.
51
+
52
+ ### `blytz --init`
53
+
54
+ Creates a new `README.md` only if one does not already exist.
55
+
56
+ If a README already exists, the CLI stops and suggests using `--force`.
57
+
58
+ ### `blytz --force`
59
+
60
+ Deletes the existing `README.md` and creates a fresh one.
61
+
62
+ ### `blytz --help`
63
+
64
+ Prints the usage summary.
65
+
66
+ ## Output sections
67
+
68
+ The generated README is built from these sections:
69
+
70
+ - Description
71
+ - Installation
72
+ - Usage
73
+ - Dependencies
74
+ - Folder Structure
75
+ - License
76
+ - Built By
77
+
78
+ ## Project requirements
79
+
80
+ For best results, include the following in your `package.json`:
81
+
82
+ - `name`
83
+ - `description`
84
+ - `author`
85
+ - `scripts`
86
+ - `dependencies`
87
+
88
+ The more metadata the CLI can read, the better the generated README will be.
89
+
90
+ ## Example
91
+
92
+ ```bash
93
+ cd my-project
94
+ blytz
95
+ ```
96
+
97
+ ## Notes
98
+
99
+ - The CLI expects to run in a folder that contains `package.json`
100
+ - The CLI reads the local `README.md` in the current directory
101
+ - The folder tree excludes `node_modules` and `.git`
102
+
103
+ ## License
104
+
105
+ MIT
@@ -0,0 +1,105 @@
1
+ # blytz
2
+
3
+ `blytz` is a small Node.js CLI that creates and updates a project's `README.md` from local project metadata.
4
+
5
+ ## What it does
6
+
7
+ - Updates an existing `README.md`
8
+ - Creates a new `README.md` with `--init`
9
+ - Replaces an existing `README.md` with `--force`
10
+ - Reads `package.json` to build README sections automatically
11
+ - Generates a basic folder tree for the project structure section
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm install -g blytz
17
+ ```
18
+
19
+ If you are developing locally from this repository, you can link it instead:
20
+
21
+ ```bash
22
+ npm link
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ Run the CLI from the root of the project you want to document:
28
+
29
+ ```bash
30
+ blytz
31
+ ```
32
+
33
+ You can also use the available flags:
34
+
35
+ ```bash
36
+ blytz --update
37
+ blytz --init
38
+ blytz --force
39
+ blytz --help
40
+ ```
41
+
42
+ ## Commands
43
+
44
+ ### `blytz`
45
+
46
+ Updates the existing `README.md` in the current directory.
47
+
48
+ ### `blytz --update`
49
+
50
+ Same as `blytz`. Refreshes the current `README.md` using the project metadata it finds.
51
+
52
+ ### `blytz --init`
53
+
54
+ Creates a new `README.md` only if one does not already exist.
55
+
56
+ If a README already exists, the CLI stops and suggests using `--force`.
57
+
58
+ ### `blytz --force`
59
+
60
+ Deletes the existing `README.md` and creates a fresh one.
61
+
62
+ ### `blytz --help`
63
+
64
+ Prints the usage summary.
65
+
66
+ ## Output sections
67
+
68
+ The generated README is built from these sections:
69
+
70
+ - Description
71
+ - Installation
72
+ - Usage
73
+ - Dependencies
74
+ - Folder Structure
75
+ - License
76
+ - Built By
77
+
78
+ ## Project requirements
79
+
80
+ For best results, include the following in your `package.json`:
81
+
82
+ - `name`
83
+ - `description`
84
+ - `author`
85
+ - `scripts`
86
+ - `dependencies`
87
+
88
+ The more metadata the CLI can read, the better the generated README will be.
89
+
90
+ ## Example
91
+
92
+ ```bash
93
+ cd my-project
94
+ blytz
95
+ ```
96
+
97
+ ## Notes
98
+
99
+ - The CLI expects to run in a folder that contains `package.json`
100
+ - The CLI reads the local `README.md` in the current directory
101
+ - The folder tree excludes `node_modules` and `.git`
102
+
103
+ ## License
104
+
105
+ MIT
package/bin/cli.js CHANGED
@@ -13,12 +13,12 @@ const shouldUpdate = args.length === 0 || args.includes('--update');
13
13
  const hasAction = shouldUpdate || shouldInit || shouldForce;
14
14
 
15
15
  if (shouldShowHelp) {
16
- console.log('Usage: fixme [--update|--init|--force]');
16
+ console.log('Usage: blytz [--update|--init|--force]');
17
17
  process.exit(0);
18
18
  }
19
19
 
20
20
  if (!hasAction) {
21
- console.log('Usage: fixme [--update|--init|--force]');
21
+ console.log('Usage: blytz [--update|--init|--force]');
22
22
  process.exit(0);
23
23
  }
24
24
 
package/package.json CHANGED
@@ -1,34 +1,46 @@
1
1
  {
2
2
  "name": "blytz",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "bin": {
5
5
  "blytz": "./bin/cli.js"
6
6
  },
7
+ "readme": "README-NPM.md",
8
+ "files": [
9
+ "bin/",
10
+ "src/",
11
+ "bin/README-NPM.md"
12
+ ],
7
13
  "description": "An automated CLI tool to fix and maintain project READMEs",
8
14
  "type": "module",
9
15
  "scripts": {
10
- "start": "node src/index.js"
16
+ "start": "node src/index.js",
17
+ "prepublishOnly": "node scripts/sync-readme.js prepublish",
18
+ "postpublish": "node scripts/sync-readme.js postpublish"
11
19
  },
12
20
  "repository": {
13
21
  "type": "git",
14
22
  "url": "git+https://github.com/AryanSharma48/readme-auto-fixer.git"
15
23
  },
16
24
  "keywords": [
25
+ "blytz",
17
26
  "cli",
18
27
  "readme",
28
+ "documentation",
19
29
  "automation",
20
30
  "github",
31
+ "npm",
21
32
  "bot",
22
33
  "readmefixer",
23
- "readmeauto",
34
+ "readme-generator",
35
+ "devtools",
36
+ "markdown",
37
+ "workflow",
38
+ "productivity",
39
+ "automated-docs",
40
+ "repository-manager",
41
+ "scaffold",
24
42
  "readme-maintainer",
25
- "readme-updater",
26
- "readme-helper",
27
- "readme-fixer",
28
- "readme-bot",
29
- "readme-automation",
30
- "readme-enhancer",
31
- "readme-improver"
43
+ "readme-updater"
32
44
  ],
33
45
  "author": "Aryan Sharma",
34
46
  "license": "MIT",
@@ -1,99 +0,0 @@
1
- import fs from "fs/promises";
2
- import path from "path";
3
- import { fileURLToPath } from "url";
4
-
5
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
- const DATA_FILE = path.join(__dirname, "../data/analytics.json");
7
-
8
- const DEFAULT_DATA = {
9
- installs: [],
10
- events: []
11
- };
12
-
13
- async function ensureDataDir() {
14
- const dir = path.dirname(DATA_FILE);
15
- try {
16
- await fs.mkdir(dir, { recursive: true });
17
- } catch (err) {
18
- if (err.code !== "EEXIST") throw err;
19
- }
20
- }
21
-
22
- async function readData() {
23
- try {
24
- const raw = await fs.readFile(DATA_FILE, "utf-8");
25
- return JSON.parse(raw);
26
- } catch (err) {
27
- if (err.code === "ENOENT") {
28
- return { ...DEFAULT_DATA };
29
- }
30
- if (err instanceof SyntaxError) {
31
- console.error("Invalid JSON in analytics file, resetting...");
32
- return { ...DEFAULT_DATA };
33
- }
34
- throw err;
35
- }
36
- }
37
-
38
- async function writeData(data) {
39
- await ensureDataDir();
40
- await fs.writeFile(DATA_FILE, JSON.stringify(data, null, 2));
41
- }
42
-
43
- export async function trackInstall(installationId, accountLogin, accountType) {
44
- const data = await readData();
45
-
46
- const exists = data.installs.some(i => i.installationId === installationId);
47
- if (!exists) {
48
- data.installs.push({
49
- installationId,
50
- accountLogin,
51
- accountType,
52
- installedAt: new Date().toISOString()
53
- });
54
- await writeData(data);
55
- }
56
-
57
- return !exists;
58
- }
59
-
60
- export async function trackUninstall(installationId) {
61
- const data = await readData();
62
- const initialLength = data.installs.length;
63
-
64
- data.installs = data.installs.filter(i => i.installationId !== installationId);
65
-
66
- if (data.installs.length !== initialLength) {
67
- await writeData(data);
68
- return true;
69
- }
70
- return false;
71
- }
72
-
73
- export async function trackEvent(eventType, payload = {}) {
74
- const data = await readData();
75
-
76
- data.events.push({
77
- type: eventType,
78
- payload,
79
- timestamp: new Date().toISOString()
80
- });
81
-
82
- await writeData(data);
83
- }
84
-
85
- export async function getStats() {
86
- const data = await readData();
87
-
88
- const eventCounts = data.events.reduce((acc, e) => {
89
- acc[e.type] = (acc[e.type] || 0) + 1;
90
- return acc;
91
- }, {});
92
-
93
- return {
94
- totalInstalls: data.installs.length,
95
- totalEvents: data.events.length,
96
- eventsByType: eventCounts,
97
- recentEvents: data.events.slice(-10).reverse()
98
- };
99
- }
package/server/bot.js DELETED
@@ -1,210 +0,0 @@
1
- import { getOctokit } from "./github.js";
2
- import processReadme from "../src/processReadme.js";
3
-
4
- async function findAllPackageJsonPaths(octokit, owner, repo) {
5
- try {
6
- const { data: repoData } = await octokit.request("GET /repos/{owner}/{repo}", {
7
- owner,
8
- repo,
9
- });
10
- const defaultBranch = repoData.default_branch;
11
-
12
- const { data: treeData } = await octokit.request("GET /repos/{owner}/{repo}/git/trees/{tree_sha}", {
13
- owner,
14
- repo,
15
- tree_sha: defaultBranch,
16
- recursive: "true",
17
- });
18
-
19
- const packageJsonPaths = treeData.tree
20
- .filter(item =>
21
- item.type === "blob" &&
22
- item.path.endsWith("package.json") &&
23
- !item.path.includes("node_modules/")
24
- )
25
- .map(item => item.path);
26
-
27
- return { packageJsonPaths, treeData };
28
- } catch (err) {
29
- console.error("Error scanning repository tree:", err.message);
30
- return { packageJsonPaths: [], treeData: null };
31
- }
32
- }
33
-
34
- async function fetchPackageJson(octokit, owner, repo, path) {
35
- try {
36
- const { data } = await octokit.request("GET /repos/{owner}/{repo}/contents/{path}", {
37
- owner,
38
- repo,
39
- path,
40
- });
41
- const content = JSON.parse(Buffer.from(data.content, "base64").toString());
42
- return { path, content, error: null };
43
- } catch (err) {
44
- console.warn(`Failed to fetch/parse ${path}:`, err.message);
45
- return { path, content: null, error: err.message };
46
- }
47
- }
48
-
49
- async function fetchAllPackages(octokit, owner, repo) {
50
- const { packageJsonPaths, treeData } = await findAllPackageJsonPaths(octokit, owner, repo);
51
- const fileTree = treeData ? buildFileTree(treeData) : null;
52
-
53
- if (packageJsonPaths.length === 0) {
54
- return { packages: [], fileTree };
55
- }
56
-
57
- const results = await Promise.all(
58
- packageJsonPaths.map(path => fetchPackageJson(octokit, owner, repo, path))
59
- );
60
-
61
- const packages = results.filter(pkg => pkg.content !== null);
62
- return { packages, fileTree };
63
- }
64
-
65
- function aggregateDependencies(packages) {
66
- const ignoreList = [
67
- 'eslint', 'prettier', 'husky', 'lint-staged', 'stylelint',
68
- 'typescript', 'vite', 'webpack', 'rollup', 'parcel', 'esbuild',
69
- 'babel', 'tsc', 'ts-node', 'tsx', 'nodemon', 'concurrently',
70
- 'jest', 'mocha', 'chai', 'vitest', 'cypress', 'playwright', 'supertest',
71
- 'postcss', 'autoprefixer', 'sass', 'less', 'dotenv', 'cross-env', 'rimraf',
72
- 'black', 'flake8', 'pylint', 'mypy', 'isort', 'autopep8', 'bandit',
73
- 'pytest', 'coverage', 'tox', 'mock', 'hypothesis', 'pytest-cov',
74
- 'setuptools', 'wheel', 'twine', 'build',
75
- 'python-dotenv', 'pip-tools', 'virtualenv', 'pre-commit'
76
- ];
77
-
78
- const depsSet = new Set();
79
-
80
- for (const pkg of packages) {
81
- const { content } = pkg;
82
- if (content?.dependencies) {
83
- Object.keys(content.dependencies).forEach(dep => {
84
- if (dep.startsWith('@types/')) return;
85
- if (dep.startsWith('@babel/')) return;
86
- if (dep.startsWith('@vitejs/')) return;
87
- if (dep.startsWith('types-')) return;
88
- if (ignoreList.includes(dep)) return;
89
- depsSet.add(dep);
90
- });
91
- }
92
- }
93
-
94
- return Array.from(depsSet).sort();
95
- }
96
-
97
- function buildFileTree(treeData, maxDepth = 4) {
98
- const fileTree = {};
99
-
100
- if (!treeData?.tree || !Array.isArray(treeData.tree)) {
101
- return fileTree;
102
- }
103
-
104
- for (const item of treeData.tree) {
105
- if (
106
- item.path === "node_modules" ||
107
- item.path.startsWith("node_modules/") ||
108
- item.path === ".git" ||
109
- item.path.startsWith(".git/")
110
- ) {
111
- continue;
112
- }
113
-
114
- const parts = item.path.split("/");
115
-
116
- if (parts.length > maxDepth) {
117
- continue;
118
- }
119
-
120
- let current = fileTree;
121
-
122
- for (let i = 0; i < parts.length; i++) {
123
- const part = parts[i];
124
- const isFile = i === parts.length - 1 && item.type === "blob";
125
-
126
- if (isFile) {
127
- current[part] = null;
128
- } else {
129
- if (!current[part]) {
130
- current[part] = {};
131
- }
132
- current = current[part];
133
- }
134
- }
135
- }
136
-
137
- return fileTree;
138
- }
139
-
140
- function aggregateScripts(packages) {
141
- const scriptsMap = new Map();
142
-
143
- for (const pkg of packages) {
144
- const { path, content } = pkg;
145
- const packageDir = path === "package.json" ? "(root)" : path.replace("/package.json", "");
146
-
147
- if (content?.scripts) {
148
- for (const [name, command] of Object.entries(content.scripts)) {
149
- if (!scriptsMap.has(name)) {
150
- scriptsMap.set(name, []);
151
- }
152
- scriptsMap.get(name).push({ package: packageDir, command });
153
- }
154
- }
155
- }
156
-
157
- return scriptsMap;
158
- }
159
-
160
- export async function runBot(payload) {
161
- try {
162
- const installationId = payload.installation.id;
163
- const owner = payload.repository.owner.login;
164
- const repo = payload.repository.name;
165
-
166
- const octokit = await getOctokit(installationId);
167
-
168
- const { data } = await octokit.request("GET /repos/{owner}/{repo}/contents/{path}", {
169
- owner,
170
- repo,
171
- path: "README.md",
172
- });
173
-
174
- const content = Buffer.from(data.content, "base64").toString();
175
- const { packages, fileTree } = await fetchAllPackages(octokit, owner, repo);
176
- const dependencies = aggregateDependencies(packages);
177
- const scripts = aggregateScripts(packages);
178
- const projectType = packages.length > 0 ? "node" : "unknown";
179
-
180
- const context = {
181
- packages,
182
- dependencies,
183
- scripts,
184
- fileTree,
185
- username: owner,
186
- isMonorepo: packages.length > 1,
187
- };
188
-
189
- const newReadme = processReadme(content, projectType, context);
190
-
191
- if (newReadme === content) {
192
- console.log("No changes needed");
193
- return;
194
- }
195
-
196
- await octokit.request("PUT /repos/{owner}/{repo}/contents/{path}", {
197
- owner,
198
- repo,
199
- path: "README.md",
200
- message: "Auto-update README",
201
- content: Buffer.from(newReadme).toString("base64"),
202
- sha: data.sha,
203
- });
204
-
205
- console.log("README updated successfully");
206
-
207
- } catch (err) {
208
- console.error("Bot error:", err.message);
209
- }
210
- }
package/server/github.js DELETED
@@ -1,11 +0,0 @@
1
- import { App } from "@octokit/app";
2
-
3
- const app = new App({
4
- appId: process.env.APP_ID,
5
- privateKey: process.env.PRIVATE_KEY.replace(/\\n/g, '\n'),
6
- });
7
-
8
- export async function getOctokit(installationId) {
9
- const octokit = await app.getInstallationOctokit(installationId);
10
- return octokit;
11
- }
package/server/server.js DELETED
@@ -1,67 +0,0 @@
1
- import express from "express";
2
- import dotenv from "dotenv";
3
- import { runBot } from "./bot.js";
4
- import { trackInstall, trackUninstall, trackEvent, getStats } from "./analytics.js";
5
-
6
- dotenv.config();
7
-
8
- const app = express();
9
- app.use(express.json());
10
-
11
- app.get("/health", (req, res) => {
12
- res.json({ status: "ok" });
13
- });
14
-
15
- app.get("/stats", async (req, res) => {
16
- try {
17
- const stats = await getStats();
18
- res.json(stats);
19
- } catch (err) {
20
- console.error("Stats error:", err.message);
21
- res.status(500).json({ error: "Failed to fetch stats" });
22
- }
23
- });
24
-
25
- app.post("/webhook", async (req, res) => {
26
- const event = req.headers["x-github-event"];
27
-
28
- try {
29
- if (event === "installation") {
30
- const { action, installation } = req.body;
31
-
32
- if (action === "created") {
33
- await trackInstall(
34
- installation.id,
35
- installation.account.login,
36
- installation.account.type
37
- );
38
- await trackEvent("installation", { action, installationId: installation.id });
39
- } else if (action === "deleted") {
40
- await trackUninstall(installation.id);
41
- await trackEvent("installation", { action, installationId: installation.id });
42
- }
43
- }
44
-
45
- if (event === "push") {
46
- console.log("Push event received");
47
-
48
- const { repository, installation } = req.body;
49
- await trackEvent("push", {
50
- repo: repository.full_name,
51
- installationId: installation.id
52
- });
53
-
54
- await runBot(req.body);
55
- }
56
- } catch (err) {
57
- console.error("Webhook error:", err.message);
58
- }
59
-
60
- res.sendStatus(200);
61
- });
62
-
63
- const PORT = process.env.PORT || 3000;
64
-
65
- app.listen(PORT, () => {
66
- console.log(`Server running on port ${PORT}`);
67
- });