@verndale/ai-commit 2.5.1 → 2.5.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.
Files changed (3) hide show
  1. package/README.md +5 -5
  2. package/bin/cli.js +20 -15
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -42,7 +42,7 @@ pnpm exec ai-commit init
42
42
  | Action | Detail |
43
43
  | --- | --- |
44
44
  | Roots | **Package root** — walks up from the current directory toward the git root and uses the first directory that has **`package.json`** (if none, uses cwd). **Git root** — `git rev-parse --show-toplevel`. Env files and **`package.json`** use the package root; hooks use the git root. |
45
- | Env files | Merges **`.env`** and the **example env file** (see below). Keys already set in **`.env.local`** are treated as satisfied for the **`.env`** merge only (same as runtime load order). Init does not write **`.env.local`**. |
45
+ | Env files | Merges ai-commit keys into **`.env.local`** when that file exists; otherwise into **`.env`** (creates **`.env`** from the bundled template if missing). **`.env`** is not used when **`.env.local`** is present. Also merges the **example env file** (see below). **`--force`** does not wholesale-replace **`.env.local`** (append / docs only). |
46
46
  | Example file | Uses **`.env.example`** if it exists; else **`.env-example`** if it exists; else creates **`.env.example`**. If both dot forms exist, init uses **`.env.example`** and prints a warning. The **bundled** template in the package remains [`.env-example`](.env-example) (hyphen). |
47
47
  | Husky | Runs **`npx husky@9 init`** at the **git root** if **`husky.sh`** is missing under the resolved hooks directory. |
48
48
  | Hooks directory | **`core.hooksPath`** relative to the git root when set; otherwise **`<git-root>/.husky`**. Falls back to **`.husky`** at the git root with a warning if the config path is invalid or outside the repo. |
@@ -64,7 +64,7 @@ Set **`OPENAI_API_KEY`** in **`.env`** and/or **`.env.local`**. Duplicate keys:
64
64
  | *(none)* | Full setup: env files + Husky + hooks + `package.json` updates (when applicable). |
65
65
  | `--env-only` | You only want env / example-file updates—no Git hooks. |
66
66
  | `--husky` | Hooks + Husky only; skips **`package.json`** changes. Combine with **`--workspace`** if you need **`package.json`** merged again. |
67
- | `--force` | Replace **`.env`** and the resolved example file (see table above) with the bundled template **(destructive)** and/or overwrite existing Husky hook files. |
67
+ | `--force` | Replace **`.env`** (when that is the merge target) and the resolved example file with the bundled template **(destructive)** and/or overwrite existing Husky hook files. Does **not** wholesale-replace **`.env.local`** (merge/append only). |
68
68
 
69
69
  **Edge cases**
70
70
 
@@ -72,9 +72,9 @@ Set **`OPENAI_API_KEY`** in **`.env`** and/or **`.env.local`**. Duplicate keys:
72
72
  | --- | --- |
73
73
  | Not in a git repo | Init updates env files only (under cwd) and reports that Git/Husky were skipped. |
74
74
  | Monorepo (package not at git root) | Run **`ai-commit init`** from the app folder that has **`package.json`** and the dependency. Hooks live at the repo root; hook scripts change into the package directory before running **`ai-commit`**. |
75
- | **`.env.local`** | **`OPENAI_API_KEY`** / **`COMMIT_AI_MODEL`** there count as already present when merging **`.env`**, so init will not add duplicate placeholders for those keys. |
75
+ | **`.env.local`** | If this file exists, init merges ai-commit keys **only** here and does not create or update **`.env`** (see [Env files](#2-run-init) row). |
76
76
  | Bundled vs consumer example name | The npm package ships **`.env-example`** (hyphen) as the template source; the file init merges into on disk follows **`.env.example`** first, then **`.env-example`**, then default **`.env.example`**. |
77
- | Without **`--force`** | Missing example file is created (**`.env.example`** when neither exists); otherwise missing ai-commit keys are **appended** to **`.env`** and the example file without wiping them. |
77
+ | Without **`--force`** | Missing example file is created (**`.env.example`** when neither dot form exists); otherwise missing ai-commit keys are **appended** to the env merge target (**`.env.local`** or **`.env`**) and the example file without wiping them. |
78
78
 
79
79
  ---
80
80
 
@@ -129,7 +129,7 @@ pnpm exec ai-commit init --force
129
129
  | Command | Purpose |
130
130
  | --- | --- |
131
131
  | **`ai-commit run`** | Build a message from the staged diff and run **`git commit`**. |
132
- | **`ai-commit init`** | Env merge (**`.env`** + resolved example file; **`.env.local`** satisfies keys for the **`.env`** merge only), Husky at git root if needed, **`package.json`** at package root, hooks in **`core.hooksPath`** or **`.husky`**. See [flags](#init-flags-and-shortcuts). |
132
+ | **`ai-commit init`** | Env merge into **`.env.local`** or **`.env`** (see [Env files](#2-run-init)) plus resolved example file; Husky at git root if needed, **`package.json`** at package root, hooks in **`core.hooksPath`** or **`.husky`**. See [flags](#init-flags-and-shortcuts). |
133
133
  | **`ai-commit prepare-commit-msg <file> [source]`** | Hook: fill an empty message; skips `merge` / `squash`. |
134
134
  | **`ai-commit lint --edit <file>`** | Hook: commitlint with this package’s default config. |
135
135
 
package/bin/cli.js CHANGED
@@ -16,7 +16,7 @@ const {
16
16
  hasStagedChanges,
17
17
  commitFromFile,
18
18
  } = require("../lib/core/git.js");
19
- const { mergeAiCommitEnvFile, parseDotenvAssignedKeys } = require("../lib/init-env.js");
19
+ const { mergeAiCommitEnvFile } = require("../lib/init-env.js");
20
20
  const { resolveEnvExamplePath, findPackageRoot } = require("../lib/init-paths.js");
21
21
  const {
22
22
  detectPackageExec,
@@ -45,7 +45,7 @@ Usage:
45
45
 
46
46
  Commands:
47
47
  run Generate a message from the staged diff and run git commit.
48
- init Merge env, then Husky + package.json + hooks (from a git repo). \`--env-only\` stops after env files. \`--husky\` skips package.json. \`--force\` replaces \`.env\` / example env file / hooks (example path: existing \`.env.example\` or \`.env-example\`, default \`.env.example\`).
48
+ init Merge env, then Husky + package.json + hooks (from a git repo). \`--env-only\` stops after env files. \`--husky\` skips package.json. Env merge targets \`.env.local\` when that file exists, else \`.env\`. \`--force\` replaces \`.env\` / example file / hooks (not a wholesale replace of \`.env.local\`).
49
49
  prepare-commit-msg Git hook: fill an empty commit message file (merge/squash skipped).
50
50
  lint Run commitlint with the package default config (for commit-msg hook).
51
51
 
@@ -99,14 +99,8 @@ function cmdInit(argv) {
99
99
  const gitRoot = inGit ? getGitRoot(cwd) : null;
100
100
  const packageRoot = findPackageRoot(cwd, gitRoot);
101
101
 
102
- const extraAssignedKeys = new Set();
103
102
  const envLocalPath = path.join(packageRoot, ".env.local");
104
- if (fs.existsSync(envLocalPath)) {
105
- const localContent = fs.readFileSync(envLocalPath, "utf8");
106
- for (const k of parseDotenvAssignedKeys(localContent)) {
107
- extraAssignedKeys.add(k);
108
- }
109
- }
103
+ const envPath = path.join(packageRoot, ".env");
110
104
 
111
105
  if (
112
106
  inGit &&
@@ -118,12 +112,21 @@ function cmdInit(argv) {
118
112
  );
119
113
  }
120
114
 
121
- const envDest = path.join(packageRoot, ".env");
122
- const envResult = mergeAiCommitEnvFile(envDest, bundledExamplePath, {
123
- force,
124
- extraAssignedKeys,
115
+ /** When `.env.local` exists it is the only env merge target (no `.env` created or updated). */
116
+ const envMergePath = fs.existsSync(envLocalPath) ? envLocalPath : envPath;
117
+ const mergeEnvIntoLocal =
118
+ path.resolve(envMergePath) === path.resolve(envLocalPath);
119
+ /** Never `--force`-replace `.env.local` with the bundled template (would wipe secrets). */
120
+ const envForce = force && !mergeEnvIntoLocal;
121
+ if (force && mergeEnvIntoLocal) {
122
+ process.stderr.write(
123
+ "note: --force does not replace .env.local with the bundled template; ai-commit keys are merged (append / docs) only.\n",
124
+ );
125
+ }
126
+ const envResult = mergeAiCommitEnvFile(envMergePath, bundledExamplePath, {
127
+ force: envForce,
125
128
  });
126
- const envRel = path.relative(cwd, envDest) || ".env";
129
+ const envRel = path.relative(cwd, envMergePath) || path.basename(envMergePath);
127
130
  switch (envResult.kind) {
128
131
  case "replaced":
129
132
  process.stdout.write(`Replaced ${envRel} with bundled template (--force).\n`);
@@ -136,7 +139,9 @@ function cmdInit(argv) {
136
139
  break;
137
140
  case "unchanged":
138
141
  process.stdout.write(
139
- `No missing @verndale/ai-commit keys in ${envRel}; left unchanged. Use --force to replace the file with the bundled template.\n`,
142
+ mergeEnvIntoLocal
143
+ ? `No missing @verndale/ai-commit keys in ${envRel}; left unchanged.\n`
144
+ : `No missing @verndale/ai-commit keys in ${envRel}; left unchanged. Use --force to replace the file with the bundled template.\n`,
140
145
  );
141
146
  break;
142
147
  default:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verndale/ai-commit",
3
- "version": "2.5.1",
3
+ "version": "2.5.2",
4
4
  "description": "AI-assisted conventional commits with bundled commitlint — one install, aligned rules",
5
5
  "license": "MIT",
6
6
  "author": "Verndale",