devbrief 0.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.
package/dist/git.js ADDED
@@ -0,0 +1,150 @@
1
+ import { execa } from "execa";
2
+ import { readdir, stat } from "node:fs/promises";
3
+ import { basename, join } from "node:path";
4
+ import { parseGitLog } from "./parser.js";
5
+ export async function getGitRoot(cwd = process.cwd()) {
6
+ const result = await execa("git", ["rev-parse", "--show-toplevel"], { cwd });
7
+ return result.stdout;
8
+ }
9
+ export async function isGitRepository(cwd = process.cwd()) {
10
+ try {
11
+ await getGitRoot(cwd);
12
+ return true;
13
+ }
14
+ catch {
15
+ return false;
16
+ }
17
+ }
18
+ export async function getCommits(options = {}) {
19
+ const cwd = options.cwd ?? process.cwd();
20
+ const args = buildGitLogArgs(options);
21
+ try {
22
+ const result = await execa("git", args, { cwd });
23
+ return filterCommits(parseGitLog(result.stdout), options);
24
+ }
25
+ catch (error) {
26
+ if (isGitLogEmptyRepositoryError(error)) {
27
+ return [];
28
+ }
29
+ throw error;
30
+ }
31
+ }
32
+ export function buildGitLogArgs(options = {}) {
33
+ const args = [
34
+ "log",
35
+ "--date=short",
36
+ "--pretty=format:%h%x09%an%x09%ad%x09%s",
37
+ ];
38
+ if (options.since) {
39
+ args.push(`--since=${options.since}`);
40
+ }
41
+ if (options.limit) {
42
+ args.push(`--max-count=${options.limit}`);
43
+ }
44
+ if (options.author) {
45
+ args.push(`--author=${options.author}`);
46
+ }
47
+ if (options.excludeMerges) {
48
+ args.push("--no-merges");
49
+ }
50
+ return args;
51
+ }
52
+ function isGitLogEmptyRepositoryError(error) {
53
+ return (typeof error === "object" &&
54
+ error !== null &&
55
+ "stderr" in error &&
56
+ typeof error.stderr === "string" &&
57
+ error.stderr.includes("does not have any commits yet"));
58
+ }
59
+ export async function findGitRepositories(scanRoot) {
60
+ const repositories = [];
61
+ await walkForGitRepositories(scanRoot, repositories);
62
+ return repositories.sort();
63
+ }
64
+ export async function getRepositoryActivity(roots, options = {}) {
65
+ const activity = await Promise.all(roots.map(async (root) => ({
66
+ root,
67
+ name: basename(root),
68
+ commits: filterCommits(await getCommitsSafely(root, options), options),
69
+ })));
70
+ return activity.sort((left, right) => right.commits.length - left.commits.length);
71
+ }
72
+ async function getCommitsSafely(root, options) {
73
+ try {
74
+ return await getCommits({ ...options, cwd: root });
75
+ }
76
+ catch {
77
+ return [];
78
+ }
79
+ }
80
+ export async function getCurrentGitUser(cwd = process.cwd()) {
81
+ const email = await getGitConfigValue("user.email", cwd);
82
+ if (email) {
83
+ return email;
84
+ }
85
+ return getGitConfigValue("user.name", cwd);
86
+ }
87
+ export function filterCommits(commits, options = {}) {
88
+ if (!options.excludeBots) {
89
+ return commits;
90
+ }
91
+ return commits.filter((commit) => !isBotAuthor(commit.author));
92
+ }
93
+ function isBotAuthor(author) {
94
+ return /\b(bot|automation|github-actions)\b|\[bot\]/i.test(author);
95
+ }
96
+ async function getGitConfigValue(key, cwd) {
97
+ try {
98
+ const result = await execa("git", ["config", "--get", key], { cwd });
99
+ return result.stdout.trim() || undefined;
100
+ }
101
+ catch {
102
+ return undefined;
103
+ }
104
+ }
105
+ async function walkForGitRepositories(directory, repositories, depth = 0) {
106
+ if (depth > 8 || shouldSkipDirectory(directory)) {
107
+ return;
108
+ }
109
+ let entries;
110
+ try {
111
+ entries = await readdir(directory, { withFileTypes: true });
112
+ }
113
+ catch {
114
+ return;
115
+ }
116
+ if (entries.some((entry) => entry.isDirectory() && entry.name === ".git")) {
117
+ repositories.push(directory);
118
+ return;
119
+ }
120
+ for (const entry of entries) {
121
+ if (!entry.isDirectory() || shouldSkipDirectory(entry.name)) {
122
+ continue;
123
+ }
124
+ const nextDirectory = join(directory, entry.name);
125
+ try {
126
+ const stats = await stat(nextDirectory);
127
+ if (!stats.isDirectory()) {
128
+ continue;
129
+ }
130
+ }
131
+ catch {
132
+ continue;
133
+ }
134
+ await walkForGitRepositories(nextDirectory, repositories, depth + 1);
135
+ }
136
+ }
137
+ function shouldSkipDirectory(directory) {
138
+ const name = basename(directory).toLowerCase();
139
+ return [
140
+ ".git",
141
+ "node_modules",
142
+ "dist",
143
+ "build",
144
+ "coverage",
145
+ ".next",
146
+ ".cache",
147
+ "vendor",
148
+ ].includes(name);
149
+ }
150
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ReportDocument } from "./format.js";
2
+ export type LinkedInPostStyle = "humble" | "punchy" | "technical";
3
+ export type LinkedInPostInput = {
4
+ report: ReportDocument;
5
+ style: LinkedInPostStyle;
6
+ };
7
+ export declare function buildLinkedInPostPrompt(input: LinkedInPostInput): string;
8
+ //# sourceMappingURL=linkedin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linkedin.d.ts","sourceRoot":"","sources":["../src/linkedin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;AAElE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,iBAAiB,CAAC;CAC1B,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAuExE"}
@@ -0,0 +1,74 @@
1
+ export function buildLinkedInPostPrompt(input) {
2
+ const { report } = input;
3
+ const highlights = report.digest.recentHighlights.map((highlight) => `- ${highlight}`).join("\n");
4
+ const topAuthors = report.digest.commitsByAuthor
5
+ .slice(0, 5)
6
+ .map((author) => `- ${author.name}: ${author.count}`)
7
+ .join("\n");
8
+ const topRepos = report.digest.commitsByRepo
9
+ .slice(0, 5)
10
+ .map((repo) => `- ${repo.name}: ${repo.count}`)
11
+ .join("\n");
12
+ const repoDetails = (report.repositories ?? [])
13
+ .filter((repo) => repo.commits.length > 0)
14
+ .slice(0, 5)
15
+ .map((repo) => {
16
+ const commits = repo.commits
17
+ .slice(0, 3)
18
+ .map((commit) => ` - ${commit.date}: ${commit.subject}`)
19
+ .join("\n");
20
+ return `- ${repo.name}: ${repo.commits.length} commits\n${commits}`;
21
+ })
22
+ .join("\n");
23
+ return [
24
+ "Write a LinkedIn post from this local Git activity report.",
25
+ "",
26
+ "Rules:",
27
+ "- Sound natural and professional.",
28
+ "- Do not exaggerate or invent impact, metrics, users, revenue, launches, or production usage.",
29
+ "- Do not mention hashes.",
30
+ "- Keep the post length concise but comprehensive that captures the essence of the changes.",
31
+ "- Use short paragraphs.",
32
+ "- Avoid hashtags unless they are genuinely useful, maximum 2.",
33
+ "- Avoid emojis.",
34
+ " - Start with a hook that captures attention, such as a surprising insight, a question, or a bold statement.",
35
+ " - Don't just list the commits; instead, weave them into a narrative that highlights the progress, challenges, and learnings.",
36
+ " - Don't mention the number of commits or the date range explicitly; instead, focus on the story behind the work and its significance.",
37
+ "- End with a grounded reflection about consistency or learning.",
38
+ "",
39
+ `Tone: ${input.style}`,
40
+ `Scope: ${report.scope}`,
41
+ `Range: ${report.range}`,
42
+ report.repo ? `Repository path/name: ${report.repo}` : undefined,
43
+ report.repositoriesScanned !== undefined
44
+ ? `Repositories scanned: ${report.repositoriesScanned}`
45
+ : undefined,
46
+ "",
47
+ "Digest:",
48
+ `- Total commits: ${report.digest.totalCommits}`,
49
+ `- Active days: ${report.digest.activeDays}`,
50
+ `- Current streak: ${formatDays(report.digest.currentStreak)}`,
51
+ `- Longest streak: ${formatDays(report.digest.longestStreak)}`,
52
+ report.digest.mostActiveDay
53
+ ? `- Most active day: ${report.digest.mostActiveDay.name}`
54
+ : undefined,
55
+ "",
56
+ "Top authors:",
57
+ topAuthors || "- None",
58
+ "",
59
+ "Top repositories:",
60
+ topRepos || "- Not applicable",
61
+ "",
62
+ "Recent highlights:",
63
+ highlights || "- No commits found in this range.",
64
+ "",
65
+ "Repository details:",
66
+ repoDetails || "- Not applicable",
67
+ ]
68
+ .filter((line) => line !== undefined)
69
+ .join("\n");
70
+ }
71
+ function formatDays(days) {
72
+ return days === 1 ? "1 day" : `${days} days`;
73
+ }
74
+ //# sourceMappingURL=linkedin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linkedin.js","sourceRoot":"","sources":["../src/linkedin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export type CommitSummary = {
2
+ hash: string;
3
+ author: string;
4
+ date: string;
5
+ subject: string;
6
+ };
7
+ export declare function parseCommitLine(line: string): CommitSummary | null;
8
+ export declare function parseGitLog(output: string): CommitSummary[];
9
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CASlE;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,EAAE,CAO3D"}
package/dist/parser.js ADDED
@@ -0,0 +1,17 @@
1
+ export function parseCommitLine(line) {
2
+ const [hash, author, date, ...subjectParts] = line.split("\t");
3
+ const subject = subjectParts.join("\t");
4
+ if (!hash || !author || !date || !subject) {
5
+ return null;
6
+ }
7
+ return { hash, author, date, subject };
8
+ }
9
+ export function parseGitLog(output) {
10
+ return output
11
+ .split(/\r?\n/)
12
+ .map((line) => line.trim())
13
+ .filter(Boolean)
14
+ .map(parseCommitLine)
15
+ .filter((commit) => commit !== null);
16
+ }
17
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,YAAY,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAExC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,OAAO,MAAM;SACV,KAAK,CAAC,OAAO,CAAC;SACd,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,OAAO,CAAC;SACf,GAAG,CAAC,eAAe,CAAC;SACpB,MAAM,CAAC,CAAC,MAAM,EAA2B,EAAE,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC;AAClE,CAAC"}
@@ -0,0 +1,8 @@
1
+ export type DatePresetOptions = {
2
+ since: string;
3
+ week?: boolean;
4
+ month?: boolean;
5
+ now?: Date;
6
+ };
7
+ export declare function resolveSinceOption(options: DatePresetOptions): string;
8
+ //# sourceMappingURL=presets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../src/presets.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAcrE"}
@@ -0,0 +1,16 @@
1
+ export function resolveSinceOption(options) {
2
+ if (options.week && options.month) {
3
+ throw new Error("Use either --week or --month, not both.");
4
+ }
5
+ if (options.week) {
6
+ return formatDaysAgo(7);
7
+ }
8
+ if (options.month) {
9
+ return formatDaysAgo(30);
10
+ }
11
+ return options.since;
12
+ }
13
+ function formatDaysAgo(days) {
14
+ return `${days} days ago`;
15
+ }
16
+ //# sourceMappingURL=presets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.js","sourceRoot":"","sources":["../src/presets.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,kBAAkB,CAAC,OAA0B;IAC3D,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IAED,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,aAAa,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IAED,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,OAAO,aAAa,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO,OAAO,CAAC,KAAK,CAAC;AACvB,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,GAAG,IAAI,WAAW,CAAC;AAC5B,CAAC"}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "devbrief",
3
+ "version": "0.1.0",
4
+ "description": "Offline local Git repository activity digest and streak reporter",
5
+ "main": "dist/cli.js",
6
+ "types": "dist/cli.d.ts",
7
+ "scripts": {
8
+ "test": "vitest run",
9
+ "build": "tsc",
10
+ "dev": "tsx src/cli.ts",
11
+ "report": "npm run build && node dist/cli.js report",
12
+ "prepublishOnly": "npm run build && npm test",
13
+ "pack:check": "npm run build && npm pack --dry-run"
14
+ },
15
+ "keywords": [
16
+ "git",
17
+ "cli",
18
+ "developer-tools",
19
+ "activity",
20
+ "streak",
21
+ "digest",
22
+ "offline",
23
+ "groq"
24
+ ],
25
+ "author": "Ali Abdullah",
26
+ "license": "MIT",
27
+ "type": "module",
28
+ "engines": {
29
+ "node": ">=20"
30
+ },
31
+ "bin": {
32
+ "commitlens": "dist/cli.js"
33
+ },
34
+ "dependencies": {
35
+ "chalk": "^5.6.2",
36
+ "commander": "^14.0.3",
37
+ "execa": "^9.6.1",
38
+ "zod": "^4.4.3"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^25.9.1",
42
+ "tsx": "^4.22.3",
43
+ "typescript": "^6.0.3",
44
+ "vitest": "^4.1.7"
45
+ },
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "git+https://github.com/ali-ab-2003/commitlens.git"
49
+ },
50
+ "bugs": {
51
+ "url": "https://github.com/ali-ab-2003/commitlens/issues"
52
+ },
53
+ "homepage": "https://github.com/ali-ab-2003/commitlens#readme",
54
+ "files": [
55
+ "dist",
56
+ "README.md",
57
+ "LICENSE"
58
+ ],
59
+ "publishConfig": {
60
+ "access": "public"
61
+ }
62
+ }