Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5b61661
docs: Documentation update to remove out dated information.
SatanshuMishra Nov 30, 2025
caa480f
fix: add cross-platform editor detection and Node.js version requirement
SatanshuMishra Nov 30, 2025
eaa263b
docs: clarify global config as planned feature
SatanshuMishra Nov 30, 2025
7f3aa9a
build: optimize package contents for npm publishing
SatanshuMishra Nov 30, 2025
9a3198d
chore: remove processed changeset files
SatanshuMishra Nov 30, 2025
35ebb45
chore: apply code formatting and cleanup
SatanshuMishra Nov 30, 2025
2f8b120
fix: use ConfigError for all config loading errors
SatanshuMishra Nov 30, 2025
f7fcc5d
chore: apply prettier formatting to command files
SatanshuMishra Nov 30, 2025
26fc243
chore: apply prettier formatting to remaining source files
SatanshuMishra Nov 30, 2025
08e5152
docs: update documentation for version 0.3.0
SatanshuMishra Nov 30, 2025
b178505
chore: bump version to 0.3.0
SatanshuMishra Nov 30, 2025
18d5a56
feat: implement terminal emoji detection and display adaptation
SatanshuMishra Nov 30, 2025
1f8afac
feat: integrate emoji detection in commit command
SatanshuMishra Nov 30, 2025
b5c00f9
feat: integrate emoji detection in preview command
SatanshuMishra Nov 30, 2025
597d67e
feat: integrate emoji detection in revert command
SatanshuMishra Nov 30, 2025
250dcc5
fix: show actual commit message with emojis in preview
SatanshuMishra Nov 30, 2025
48bd866
fix: include emoji placeholder in generated config template
SatanshuMishra Nov 30, 2025
77296fc
chore: bump version to 0.4.0
SatanshuMishra Nov 30, 2025
0bf3705
chore: apply code formatting
SatanshuMishra Nov 30, 2025
1771d7a
chore: apply code formatting to command files
SatanshuMishra Nov 30, 2025
e278af7
Documentation, Error Handling and More (#28)
SatanshuMishra Nov 30, 2025
ba6f8a4
Revert "chore: bump version to 0.4.0"
SatanshuMishra Nov 30, 2025
4b0e3ce
Revert "chore: bump version to 0.4.0" (#29)
SatanshuMishra Nov 30, 2025
bef206c
Merge branch 'main' into dev
SatanshuMishra Nov 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/fix-init-template-emoji-placeholder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@labcatr/labcommitr": patch
---

fix: include emoji placeholder in generated config template

- Add {emoji} placeholder to template in buildConfig function
- Generated configs now include {emoji} in format.template field
- Fixes issue where emojis didn't appear in commits even when enabled
- Template now matches default config structure with emoji support
- Ensures formatCommitMessage can properly replace emoji placeholder
11 changes: 11 additions & 0 deletions .changeset/fix-preview-emoji-display.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@labcatr/labcommitr": patch
---

fix: show actual commit message with emojis in preview

- Preview now displays the exact commit message as it will be stored in Git
- Removed emoji stripping from preview display logic
- Users can see emojis even if terminal doesn't support emoji display
- Ensures preview accurately reflects what will be committed to Git/GitHub
- Fixes issue where emojis were hidden in preview on non-emoji terminals
12 changes: 12 additions & 0 deletions .changeset/implement-emoji-detection-and-display.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@labcatr/labcommitr": minor
---

feat: implement terminal emoji detection and display adaptation

- Add emoji detection utility with industry-standard heuristics (CI, TERM, NO_COLOR, Windows Terminal)
- Implement automatic emoji stripping for non-emoji terminals in Labcommitr UI
- Always store Unicode emojis in Git commits regardless of terminal support
- Update commit, preview, and revert commands to adapt display based on terminal capabilities
- Ensure GitHub and emoji-capable terminals always show emojis correctly
- Improve user experience by cleaning up broken emoji symbols on non-emoji terminals
2 changes: 2 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ docs/

# Exclude build artifacts
*.map
**/*.map
dist/**/*.map
tsconfig.json
.prettierrc*

Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# @labcatr/labcommitr

## 0.2.0
## 0.3.0

### Minor Changes

Expand Down
31 changes: 25 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,8 @@ See [`docs/CONFIG_SCHEMA.md`](docs/CONFIG_SCHEMA.md) for complete configuration
**Configuration discovery:**

- Searches from current directory up to project root
- Falls back to global configuration if available
- Uses sensible defaults if no configuration found
- Global configuration support is planned for future releases (see [Planned Features](#planned-features))

---

Expand Down Expand Up @@ -404,10 +404,6 @@ Before implementing any changes, please follow this process:
- Follow the project's development guidelines
- Ensure your commits follow the project's commit message format (you can set up using `lab init`)

### Development Guidelines

For detailed development guidelines, coding standards, and architecture information, see [`docs/DEVELOPMENT_GUIDELINES.md`](docs/DEVELOPMENT_GUIDELINES.md).

### Questions?

If you have questions or need clarification, feel free to open a discussion or issue.
Expand All @@ -416,4 +412,27 @@ If you have questions or need clarification, feel free to open a discussion or i

## Planned Features

_No planned features at this time. Check back later or open an issue to suggest new features!_
### Global Configuration

Support for user-level global configuration files to enable consistent commit conventions across multiple projects. This will allow you to:

- Set default commit types and preferences in a single location
- Apply your preferred commit conventions to all projects automatically
- Override global settings on a per-project basis when needed

**Use cases:**

- Developers working across multiple repositories who want consistent commit message formats
- Teams that want to standardize commit conventions organization-wide
- Personal projects where you want the same commit types everywhere

The global configuration will be stored in OS-specific locations:

- **macOS/Linux**: `~/.labcommitr.config.yaml` or XDG config directory
- **Windows**: `%USERPROFILE%\.labcommitr.config.yaml` or AppData directory

Project-specific configurations will always take precedence over global settings.

---

_Have a feature idea? Open an issue to suggest new features!_
15 changes: 12 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
{
"name": "@labcatr/labcommitr",
"version": "0.2.0",
"version": "0.3.0",
"description": "Labcommitr is a solution for building standardized git commits, hassle-free!",
"main": "dist/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "npx tsc",
"build:prod": "npx tsc && pnpm run clean:maps",
"clean:maps": "node scripts/clean-maps.js",
"format": "pnpm run format:code",
"format:ci": "pnpm run format:code",
"format:code": "prettier -w \"**/*\" --ignore-unknown --cache",
"version": "changeset version && pnpm install --no-frozen-lockfile && pnpm run format",
"dev:cli": "node dist/index-dev.js"
"dev:cli": "node dist/index-dev.js",
"prepublishOnly": "pnpm run build:prod"
},
"type": "module",
"bin": {
Expand All @@ -29,6 +32,9 @@
"homepage": "https://github.com/labcatr/labcommitr#readme",
"author": "Trevor Fox",
"license": "ISC",
"engines": {
"node": ">=18.0.0"
},
"dependencies": {
"@changesets/cli": "^2.29.7",
"@clack/prompts": "^0.11.0",
Expand Down Expand Up @@ -56,7 +62,10 @@
"dist/cli/commands/revert",
"dist/cli/commands/shared",
"dist/cli/utils",
"dist/lib"
"dist/lib",
"README.md",
"CHANGELOG.md",
"TESTING.md"
],
"publishConfig": {
"access": "public"
Expand Down
36 changes: 36 additions & 0 deletions scripts/clean-maps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env node

/**
* Remove all .map files from dist directory
* Cross-platform solution for cleaning source maps before publishing
*/

import { readdir, stat, unlink } from "fs/promises";
import { join } from "path";

async function removeMaps(dir) {
try {
const entries = await readdir(dir, { withFileTypes: true });

for (const entry of entries) {
const fullPath = join(dir, entry.name);

if (entry.isDirectory()) {
await removeMaps(fullPath);
} else if (entry.isFile() && entry.name.endsWith(".map")) {
await unlink(fullPath);
console.log(`Removed: ${fullPath}`);
}
}
} catch (error) {
if (error.code !== "ENOENT") {
throw error;
}
}
}

const distDir = join(process.cwd(), "dist");
removeMaps(distDir).catch((error) => {
console.error("Error removing source maps:", error);
process.exit(1);
});
63 changes: 54 additions & 9 deletions src/cli/commands/commit/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,75 @@ import {
unlinkSync,
mkdtempSync,
rmdirSync,
accessSync,
constants,
} from "fs";
import { join, dirname } from "path";
import { tmpdir } from "os";
import { tmpdir, platform } from "os";
import { Logger } from "../../../lib/logger.js";

/**
* Cross-platform command resolver
* On Windows: uses 'where' command
* On Unix: uses 'which' command
*
* @param command - Command name to find
* @returns Full path to command or null if not found
*/
function findCommand(command: string): string | null {
const isWindows = platform() === "win32";
const findCommand = isWindows ? "where" : "which";

try {
const result = spawnSync(findCommand, [command], {
encoding: "utf-8",
stdio: ["ignore", "pipe", "ignore"],
});

if (result.status === 0 && result.stdout) {
// On Windows, 'where' may return multiple paths, take the first one
const path = result.stdout.trim().split("\n")[0].trim();
return path || null;
}
} catch {
// Command not found or error occurred
}

return null;
}

/**
* Detect available editor in priority order: nvim → vim → vi
* Also checks $EDITOR and $VISUAL environment variables
* Cross-platform: works on Windows, macOS, and Linux
*/
export function detectEditor(): string | null {
// Check environment variables first (user preference)
const envEditor = process.env.EDITOR || process.env.VISUAL;
if (envEditor) {
// Verify the editor exists
const check = spawnSync("which", [envEditor], { encoding: "utf-8" });
if (check.status === 0) {
return envEditor.trim();
// If it's already a full path, verify it exists
if (envEditor.includes("/") || envEditor.includes("\\")) {
try {
accessSync(envEditor, constants.F_OK);
return envEditor.trim();
} catch {
// Path doesn't exist, try to find it as a command
}
}

// Try to find it as a command in PATH
const found = findCommand(envEditor);
if (found) {
return found;
}
}

// Try nvim, vim, vi in order
const editors = ["nvim", "vim", "vi"];
for (const editor of editors) {
const check = spawnSync("which", [editor], { encoding: "utf-8" });
if (check.status === 0) {
return editor;
const found = findCommand(editor);
if (found) {
return found;
}
}

Expand All @@ -58,9 +101,11 @@ export function editInEditor(

if (!editorCommand) {
Logger.error("No editor found");
const isWindows = platform() === "win32";
const envVar = isWindows ? "%EDITOR%" : "$EDITOR";
console.error("\n No editor available (nvim, vim, or vi)");
console.error(
" Set $EDITOR environment variable to your preferred editor\n",
` Set ${envVar} environment variable to your preferred editor\n`,
);
return null;
}
Expand Down
21 changes: 18 additions & 3 deletions src/cli/commands/commit/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import { loadConfig, ConfigError } from "../../../lib/config/index.js";
import type { LabcommitrConfig } from "../../../lib/config/types.js";
import { Logger } from "../../../lib/logger.js";
import { formatForDisplay } from "../../../lib/util/emoji.js";
import { isGitRepository } from "./git.js";
import {
stageAllTrackedFiles,
Expand Down Expand Up @@ -193,6 +194,7 @@ export async function commitAction(options: {
}

const config = configResult.config;
const emojiModeActive = configResult.emojiModeActive;

// Step 2: Verify git repository
if (!isGitRepository()) {
Expand Down Expand Up @@ -353,7 +355,11 @@ export async function commitAction(options: {
);

console.log(`${success("✓")} Commit created successfully!`);
console.log(` ${commitHash} ${formattedMessage}`);
const displayMessage = formatForDisplay(
formattedMessage,
emojiModeActive,
);
console.log(` ${commitHash} ${displayMessage}`);
} catch (error: unknown) {
// Cleanup on failure
await cleanup({
Expand Down Expand Up @@ -441,7 +447,12 @@ export async function commitAction(options: {
);

// Show preview and get user action
action = await displayPreview(formattedMessage, body, config);
action = await displayPreview(
formattedMessage,
body,
config,
emojiModeActive,
);

// Handle edit actions
if (action === "edit-type") {
Expand Down Expand Up @@ -508,7 +519,11 @@ export async function commitAction(options: {
);

console.log(`${success("✓")} Commit created successfully!`);
console.log(` ${commitHash} ${formattedMessage}`);
const displayMessage = formatForDisplay(
formattedMessage,
emojiModeActive,
);
console.log(` ${commitHash} ${displayMessage}`);
} catch (error: unknown) {
// Cleanup on failure
await cleanup({
Expand Down
13 changes: 10 additions & 3 deletions src/cli/commands/commit/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1076,6 +1076,7 @@ export async function displayPreview(
formattedMessage: string,
body: string | undefined,
config?: LabcommitrConfig,
emojiModeActive: boolean = true,
): Promise<
| "commit"
| "edit-type"
Expand All @@ -1084,6 +1085,12 @@ export async function displayPreview(
| "edit-body"
| "cancel"
> {
// Preview shows the actual commit message as it will be stored in Git
// We don't strip emojis here because the user needs to see what will be committed
// even if their terminal doesn't support emoji display
const displayMessage = formattedMessage;
const displayBody = body;

// Start connector line using @clack/prompts
log.info(
`${label("preview", "green")} ${textColors.pureWhite("Commit message preview:")}`,
Expand All @@ -1092,11 +1099,11 @@ export async function displayPreview(
// Render content with connector lines
// Empty line after header
console.log(renderWithConnector(""));
console.log(renderWithConnector(textColors.brightCyan(formattedMessage)));
console.log(renderWithConnector(textColors.brightCyan(displayMessage)));

if (body) {
if (displayBody) {
console.log(renderWithConnector(""));
const bodyLines = body.split("\n");
const bodyLines = displayBody.split("\n");
for (const line of bodyLines) {
console.log(renderWithConnector(textColors.white(line)));
}
Expand Down
Loading