From 0d47a6d2b377e186a30e58c711292ad9da362889 Mon Sep 17 00:00:00 2001
From: Claude
Date: Tue, 8 Sep 2026 03:15:16 +0000
Subject: [PATCH 1/2] fix(mcp): resolve the plugin bundle against the plugin
root
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
`.mcp.json` named `./dist/commitlore.mjs` and set `"cwd": "."`, and both of
those resolve against the *session's* working directory rather than the
plugin's install directory. The plugin MCP server therefore died at launch
with MODULE_NOT_FOUND in every session whose cwd was not a built commitlore
checkout. Capture is MCP-only, so those sessions committed no records, and the
only visible symptom was one line about a cached connection failure. The one
cwd where the defect cannot appear is this repository, which is where every
existing check ran.
The entry point is now `${CLAUDE_PLUGIN_ROOT:-.}/dist/commitlore.mjs` and there
is no `cwd` — the form ADR-0026 and the F14 ticket already documented. The
default is what keeps this repository's own registration working when a host
loads it as an ordinary project file, where nothing sets that variable.
Readers of a registration now expand `${VAR}` and `${VAR:-default}` the way a
host expands them before it spawns. Without that, doctor's unattended-initiator
probe launched the literal `${...}` as a path and reported a working
registration unhealthy — a report that sends an operator to repair the one
thing that is not broken. An unset placeholder with no default is left as
written: a host refuses that registration outright, and expanding it to nothing
would turn the refusal into a plausible-looking path whose failure names a file
nobody wrote.
The manifest checks now launch from a directory that is not a checkout, and one
of them drives the server to an MCP initialize — the launch the old
configuration could not complete and the old check never attempted.
Closes #870
Claude-Session: https://claude.ai/code/session_01USc9G3aJ1s8pnhWy5K5hLr
Co-Authored-By: Claude Opus 5
---
.mcp.json | 5 ++-
docs/COMPATIBILITY.md | 4 +--
src/core/mcp-registration.ts | 38 ++++++++++++++++++--
test/codex-plugin.test.ts | 8 +++--
test/doctor.test.ts | 39 +++++++++++++++++++++
test/manifest.test.ts | 67 ++++++++++++++++++++++++++++++++----
6 files changed, 144 insertions(+), 17 deletions(-)
diff --git a/.mcp.json b/.mcp.json
index e759f6cb..75ffbcae 100644
--- a/.mcp.json
+++ b/.mcp.json
@@ -3,10 +3,9 @@
"commitlore": {
"command": "node",
"args": [
- "./dist/commitlore.mjs",
+ "${CLAUDE_PLUGIN_ROOT:-.}/dist/commitlore.mjs",
"mcp"
- ],
- "cwd": "."
+ ]
}
}
}
diff --git a/docs/COMPATIBILITY.md b/docs/COMPATIBILITY.md
index eda083e0..786475f0 100644
--- a/docs/COMPATIBILITY.md
+++ b/docs/COMPATIBILITY.md
@@ -200,7 +200,7 @@ skill and MCP server.
| Capability | Provided by | Value |
|---|---|---|
-| MCP server | `.mcp.json` | `node ./dist/commitlore.mjs mcp` with plugin-root `cwd` |
+| MCP server | `.mcp.json` | `node ${CLAUDE_PLUGIN_ROOT:-.}/dist/commitlore.mjs mcp` |
| capture skill | `skills/commitlore-codex/SKILL.md` | transcript-backed capture; claims lacking support are dropped, never cited by invention |
| plugin identity | `.codex-plugin/plugin.json` | `commitlore` at the `package.json` version |
@@ -211,7 +211,7 @@ named in the middle column rather than one it assumed.
| Capability | Provided by | Value |
|---|---|---|
-| MCP server | `.mcp.json` | `node ./dist/commitlore.mjs mcp` |
+| MCP server | `.mcp.json` | `node ${CLAUDE_PLUGIN_ROOT:-.}/dist/commitlore.mjs mcp` |
| pre-edit context hook | `hooks/hooks.json` | `PreToolUse` on `Read\|Edit\|Write\|MultiEdit\|NotebookEdit` |
| skills | `skills/` | `commitlore-commits`, `commitlore-codex`, `commitlore-query`, `commitlore-setup` |
| plugin identity | `.claude-plugin/plugin.json` | `commitlore` at the `package.json` version |
diff --git a/src/core/mcp-registration.ts b/src/core/mcp-registration.ts
index 40210d58..0b5e40f1 100644
--- a/src/core/mcp-registration.ts
+++ b/src/core/mcp-registration.ts
@@ -61,6 +61,34 @@ const messageOf = (error: unknown): string => (error instanceof Error ? error.me
const isLaunchableEntry = (value: unknown): boolean =>
isJsonObject(value) && typeof value['command'] === 'string' && value['command'].trim() !== '';
+/**
+ * `${VAR}` and `${VAR:-default}`, expanded the way a host expands them before
+ * it launches the server.
+ *
+ * A registration is a launch instruction for a host, and the hosts that read
+ * this file substitute environment placeholders in `command` and `args` first —
+ * which is what lets one committed file name a path that only the host knows,
+ * `${CLAUDE_PLUGIN_ROOT}` being the one this repository's own registration uses
+ * (#870). Every reader here answers questions about that launch: what command a
+ * host will run, whether it is ours, and — in doctor's unattended-initiator
+ * check — whether it actually answers an MCP initialize. Reading the raw text
+ * answered those questions about a command no host ever runs, and the probe
+ * spawned the literal `${...}` as a path.
+ *
+ * An unset placeholder with no default is left as written rather than expanded
+ * to nothing. A host refuses that registration outright, and `""/dist/x.mjs`
+ * would turn the refusal into a plausible-looking path whose failure names a
+ * file nobody wrote.
+ */
+const expandHostPlaceholders = (value: string): string =>
+ value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}/g, (whole, name: string, fallback: string | undefined) => {
+ const set = process.env[name];
+ // `:-` is shell semantics, which the syntax is borrowed from: an empty
+ // value takes the default, because an empty path is not a path.
+ if (set !== undefined && set !== '') return set;
+ return fallback ?? whole;
+ });
+
/**
* The command a registration under our key names, or null when there is none a
* host could launch.
@@ -74,7 +102,10 @@ export const registeredMcpCommand = (cwd: string): string | null => {
return launch?.command ?? null;
};
-/** The complete launch command a host will use, when its argv is parseable. */
+/**
+ * The complete launch command a host will use, when its argv is parseable, with
+ * `${VAR}` placeholders expanded as the host would expand them.
+ */
export const registeredMcpLaunch = (cwd: string): { command: string; args: string[] } | null => {
const path = mcpRegistrationPath(cwd);
if (path === null) return null;
@@ -91,7 +122,10 @@ export const registeredMcpLaunch = (cwd: string): { command: string; args: strin
if (!isLaunchableEntry(entry)) return null;
const args = (entry as JsonObject)['args'];
if (args !== undefined && (!Array.isArray(args) || !args.every((arg) => typeof arg === 'string'))) return null;
- return { command: String((entry as JsonObject)['command']), args: (args ?? []) as string[] };
+ return {
+ command: expandHostPlaceholders(String((entry as JsonObject)['command'])),
+ args: ((args ?? []) as string[]).map(expandHostPlaceholders),
+ };
};
/**
diff --git a/test/codex-plugin.test.ts b/test/codex-plugin.test.ts
index d585499d..77fa7592 100644
--- a/test/codex-plugin.test.ts
+++ b/test/codex-plugin.test.ts
@@ -253,7 +253,7 @@ describe('Codex plugin package', () => {
mcpServers: string;
};
const mcp = JSON.parse(readFileSync(join(ROOT, '.mcp.json'), 'utf8')) as {
- mcpServers: { commitlore: { command: string; args: string[]; cwd: string } };
+ mcpServers: { commitlore: { command: string; args: string[] } };
};
expect(manifest).toMatchObject({
@@ -262,10 +262,12 @@ describe('Codex plugin package', () => {
skills: './skills/',
mcpServers: './.mcp.json',
});
+ // #870: the entry point is bound to the plugin root. A relative path, or a
+ // `cwd`, resolves against whatever directory the session was started in —
+ // so the server died at launch everywhere but a built checkout.
expect(mcp.mcpServers.commitlore).toEqual({
command: 'node',
- args: ['./dist/commitlore.mjs', 'mcp'],
- cwd: '.',
+ args: ['${CLAUDE_PLUGIN_ROOT:-.}/dist/commitlore.mjs', 'mcp'],
});
});
diff --git a/test/doctor.test.ts b/test/doctor.test.ts
index 4ef0630a..ad80e0e2 100644
--- a/test/doctor.test.ts
+++ b/test/doctor.test.ts
@@ -1376,6 +1376,45 @@ describe('#527 unattended capture initiator', () => {
});
}
+ /**
+ * #870: this repository's own registration names its entry point as
+ * `${CLAUDE_PLUGIN_ROOT:-.}/dist/commitlore.mjs`, because the hosts that read
+ * `.mcp.json` expand placeholders before they spawn anything. The reader
+ * returned the raw text, so the probe launched a literal `${...}` as a path
+ * and reported a working registration unhealthy — a report that sends an
+ * operator to repair the one thing that is not broken.
+ */
+ it('probes the launch a host would run, with ${VAR} placeholders expanded', () => {
+ const repo = initRepo('unattended-placeholder');
+ enableUnattended(repo);
+ const command = mcpWrapper(repo, 'commitlore');
+ // A default rather than a set variable: the expansion is what is under
+ // test, and a test that exports the answer first proves less.
+ registerMcp(repo, `\${COMMITLORE_TEST_UNSET:-${command}}`);
+
+ const check = runDoctor({ cwd: repo }).checks.find((entry) => entry.id === 'unattended-initiator');
+
+ expect(check?.status).toBe('ok');
+ expect(check?.evidence?.['initiator']).toBe('capture-tools-advertised');
+ });
+
+ /**
+ * The other half of the same rule. An unset placeholder with no default is
+ * left as written, because a host refuses that registration outright and
+ * expanding it to nothing would turn the refusal into a plausible-looking
+ * path whose failure names a file nobody wrote.
+ */
+ it('leaves an unset placeholder with no default as written', () => {
+ const repo = initRepo('unattended-placeholder-unset');
+ enableUnattended(repo);
+ registerMcp(repo, '${COMMITLORE_TEST_UNSET}/dist/commitlore.mjs');
+
+ const check = runDoctor({ cwd: repo }).checks.find((entry) => entry.id === 'unattended-initiator');
+
+ expect(check?.status).toBe('warn');
+ expect(check?.detail).toContain('${COMMITLORE_TEST_UNSET}');
+ });
+
it.each([
['a dead command', (repo: string) => join(repo, 'missing'), 'command-not-found'],
['a command that is a directory', (repo: string) => repo, 'command-is-directory'],
diff --git a/test/manifest.test.ts b/test/manifest.test.ts
index 2b2b4ee0..c77fd8a5 100644
--- a/test/manifest.test.ts
+++ b/test/manifest.test.ts
@@ -50,6 +50,7 @@ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { execGit } from '../src/core/git.js';
import { createTestRepo } from './git-fixtures.js';
+import { startStub } from './mcp-client.js';
const REPO_ROOT = fileURLToPath(new URL('..', import.meta.url));
@@ -171,20 +172,72 @@ describe('plugin manifest: entry points are runnable from a clean clone', () =>
expect(run.stdout.trim()).toBe(manifest.version);
});
- it('the Codex MCP declaration resolves the plugin-local bundle', () => {
- const manifest = readJson(clonePath('.codex-plugin/plugin.json')) as PluginManifest;
+ /**
+ * #870: the declaration named `./dist/commitlore.mjs` and set `"cwd": "."`,
+ * and both of those resolve against the *session's* working directory, never
+ * the plugin's. So the plugin's MCP server died at launch with
+ * `MODULE_NOT_FOUND` in every session whose cwd was not a built commitlore
+ * checkout — which is every real use, and the one cwd where the defect cannot
+ * appear is this repository, where the old check ran.
+ *
+ * Everything below therefore launches from a directory that is not a
+ * checkout, with the placeholder expanded the way a host expands it before
+ * spawning. Reverting `.mcp.json` fails these and passes the old one.
+ */
+ const pluginLaunch = (): { entry: string; args: string[] } => {
const mcp = readJson(clonePath('.mcp.json')) as {
- mcpServers: { commitlore: { command: string; args: string[]; cwd: string } };
+ mcpServers: { commitlore: { command: string; args: string[]; cwd?: string } };
};
const server = mcp.mcpServers.commitlore;
- const run = spawnSync(server.command, [server.args[0]!, '--version'], {
- cwd: clonePath(server.cwd),
+ expect(
+ server.cwd,
+ 'a "cwd" here is resolved against the session, not the plugin (#870)',
+ ).toBeUndefined();
+ expect(
+ server.args[0],
+ 'the entry point must be bound to the plugin root, not the session cwd (#870)',
+ ).toContain('${CLAUDE_PLUGIN_ROOT');
+ const expand = (arg: string): string =>
+ arg.replace(/\$\{CLAUDE_PLUGIN_ROOT(?::-[^}]*)?\}/g, cloneDir);
+ return { entry: expand(server.args[0]!), args: server.args.slice(1).map(expand) };
+ };
+
+ it('the MCP declaration resolves the bundle against the plugin root, not the session cwd', () => {
+ const manifest = readJson(clonePath('.claude-plugin/plugin.json')) as PluginManifest;
+ const codex = readJson(clonePath('.codex-plugin/plugin.json')) as PluginManifest;
+ const { entry } = pluginLaunch();
+ const run = spawnSync(process.execPath, [entry, '--version'], {
+ // Deliberately not the clone: this is the cwd a real session has.
+ cwd: tempDir('foreign-cwd'),
encoding: 'utf8',
});
- expect(run.status).toBe(0);
+ expect(run.status, run.stderr).toBe(0);
expect(run.stdout.trim()).toBe(manifest.version);
- expect(manifest.mcpServers).toBe('./.mcp.json');
+ expect(codex.mcpServers).toBe('./.mcp.json');
+ });
+
+ it('that launch reaches an MCP initialize from a cwd that is not a checkout', async () => {
+ // `--version` proves the module resolves; this proves the server the host
+ // actually asked for comes up and answers. The old configuration exited
+ // before a byte was exchanged, and the only visible symptom was one line
+ // about a cached connection failure.
+ const { entry, args } = pluginLaunch();
+ const stub = startStub(tempDir('foreign-session'), entry, args);
+ try {
+ const initialized = await stub.request('initialize', {
+ protocolVersion: '2024-11-05',
+ capabilities: {},
+ clientInfo: { name: 'manifest', version: '1' },
+ });
+ expect(
+ initialized.error,
+ `initialize failed: ${JSON.stringify(initialized.error)}; stderr: ${stub.stderr()}`,
+ ).toBeUndefined();
+ expect(initialized.result?.['serverInfo']).toMatchObject({ name: 'commitlore' });
+ } finally {
+ await stub.close();
+ }
});
it('scripts/commitlore-run.sh resolves the plugin entry point via CLAUDE_PLUGIN_ROOT', () => {
From 6647cf024a47a9b3c7f49955e93b4692eeb46317 Mon Sep 17 00:00:00 2001
From: Claude
Date: Tue, 8 Sep 2026 03:16:49 +0000
Subject: [PATCH 2/2] release: 1.2.2
Version fields, install pins and the changelog entry for 1.2.2.
The release carries one fix: the plugin's MCP server resolved its entry point
against the session's working directory, so it died at launch with
MODULE_NOT_FOUND in every session that was not a built commitlore checkout
(#870). Capture is MCP-only, so those sessions recorded nothing.
The install pins move; the field-report paragraph in each README keeps saying
v1.2.1, because that is the version the run it describes was made on and a
measurement does not follow the version number.
`dist/` is deliberately not in this branch. `canonical-merge.yml` rebuilds the
bundle from the merged tree and refuses a pull request that touches it, so the
committed bundle matches the source it lands with.
Claude-Session: https://claude.ai/code/session_01USc9G3aJ1s8pnhWy5K5hLr
Co-Authored-By: Claude Opus 5
---
.claude-plugin/plugin.json | 2 +-
.codex-plugin/plugin.json | 2 +-
CHANGELOG.md | 50 ++++++++++++++++++++++++++++++++++++++
README.ja.md | 12 ++++-----
README.ko.md | 12 ++++-----
README.md | 12 ++++-----
README.zh-CN.md | 12 ++++-----
install.ps1 | 4 +--
install.sh | 4 +--
package-lock.json | 4 +--
package.json | 2 +-
server.json | 6 ++---
12 files changed, 86 insertions(+), 36 deletions(-)
diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json
index 58c9000f..1a1701b0 100644
--- a/.claude-plugin/plugin.json
+++ b/.claude-plugin/plugin.json
@@ -1,7 +1,7 @@
{
"name": "commitlore",
"displayName": "CommitLore",
- "version": "1.2.1",
+ "version": "1.2.2",
"description": "Recorded decisions from git history, delivered to the agent before it edits. Constraints, alternatives already ruled out, and warnings left by whoever was here last.",
"author": {
"name": "MongLong0214",
diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json
index 7470e9e7..1a79f028 100644
--- a/.codex-plugin/plugin.json
+++ b/.codex-plugin/plugin.json
@@ -1,6 +1,6 @@
{
"name": "commitlore",
- "version": "1.2.1",
+ "version": "1.2.2",
"description": "Decision memory from Git history, with verified capture for coding sessions.",
"author": {
"name": "MongLong0214",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9a816821..51d244d1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,56 @@ Release notes for 1.0.0, 1.0.1 and 1.0.2 are on the
[GitHub releases page](https://github.com/MongLong0214/commitlore/releases); they
were not written here.
+## 1.2.2
+
+One line of configuration, and the plugin's MCP server had never started for
+anybody.
+
+**The plugin's MCP server died at launch everywhere but a commitlore checkout
+(#870).** `.mcp.json` named the entry point as `./dist/commitlore.mjs` and set
+`"cwd": "."`, and both of those resolve against the *session's* working
+directory rather than the plugin's install directory. So node was asked for
+`/dist/commitlore.mjs`, which does not exist, and the server exited
+in under 60ms with `MODULE_NOT_FOUND`. Capture is MCP-only, so a session with
+the plugin installed made commits carrying no records at all, and the only
+visible symptom was one line saying a connection failure had been cached. It was
+identical in the `0.8.0` and `1.2.0` plugin caches, so no release ever shipped a
+working one. The entry point is now
+`${CLAUDE_PLUGIN_ROOT:-.}/dist/commitlore.mjs` with no `cwd` — the form
+[ADR-0026](docs/adr/ADR-0026-node-only-distribution.md) and the F14 ticket had
+both documented while the file said otherwise.
+
+**The defect was invisible to every check because they all ran in the one place
+it cannot appear.** A checkout is the one cwd where a session-relative path is
+also the plugin's path, and that is where the suite, the release gate and the
+maintainers all work. The manifest checks now launch from a directory that is
+not a checkout, and one of them drives the server to an MCP `initialize` rather
+than stopping at "something resolved" — the same distinction #483 forced on the
+plugin entry point two releases ago.
+
+**A registration is read the way a host reads it.** `.mcp.json` is a launch
+instruction for a host, and hosts expand `${VAR}` and `${VAR:-default}` before
+they spawn anything; the readers here returned the raw text. Doctor's
+unattended-initiator probe therefore launched a literal `${...}` as a path and
+would have called this repository's own registration unhealthy — sending an
+operator to repair the one thing that is not broken. An unset placeholder with
+no default is left as written on purpose: a host refuses that registration
+outright, and expanding it to nothing would turn the refusal into a
+plausible-looking path whose failure names a file nobody wrote.
+
+**The default is what keeps this repository working on itself.** `:-.` is not
+decoration. This repository's `.mcp.json` is also an ordinary project file here,
+loaded by a host that sets no plugin root, and without the fallback the
+dogfooding install would break in exchange for fixing the plugin.
+
+**Not verified.** Codex reads the same `.mcp.json`, declared by
+`.codex-plugin/plugin.json`, and whether Codex performs the same placeholder
+expansion was not measured — no Codex install was available to this change. What
+is measured is that the Claude Code plugin path now launches from a foreign cwd
+and answers `initialize`. If Codex does not expand, its launch is no worse than
+the relative path it had before, and that is a claim about the shape of the
+change, not a test result.
+
## 1.2.1
A security release, and one line that had been asserting something nobody checked.
diff --git a/README.ja.md b/README.ja.md
index 54a57d98..b443698a 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -47,18 +47,18 @@
```bash
-curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.sh | sh -s v1.2.1
+curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.sh | sh -s v1.2.2
```
先にインストーラーを読みたいですか?
```bash
-curl -fsSLO https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.sh
-sh install.sh v1.2.1
+curl -fsSLO https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.sh
+sh install.sh v1.2.2
# あるいはスクリプトを使わずに。スクリプトが作るチェックアウトは自分でも作れます。
-git clone --depth 1 --branch v1.2.1 https://github.com/MongLong0214/commitlore
+git clone --depth 1 --branch v1.2.2 https://github.com/MongLong0214/commitlore
node commitlore/dist/commitlore.mjs --version
```
@@ -107,13 +107,13 @@ CommitLore はその判断をコードのそばに残します。
macOS と Linux:
```bash
-curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.sh | sh -s v1.2.1
+curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.sh | sh -s v1.2.2
```
Windows:
```powershell
-& ([scriptblock]::Create((irm https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.ps1))) v1.2.1
+& ([scriptblock]::Create((irm https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.ps1))) v1.2.2
```
Node.js 22.23.2+ と Git が必要です。スクリプトは何かを書き込む前に両方を確認します。
diff --git a/README.ko.md b/README.ko.md
index 4a31cdaa..396becd9 100644
--- a/README.ko.md
+++ b/README.ko.md
@@ -47,18 +47,18 @@
```bash
-curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.sh | sh -s v1.2.1
+curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.sh | sh -s v1.2.2
```
먼저 설치기를 읽어 보고 싶나요?
```bash
-curl -fsSLO https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.sh
-sh install.sh v1.2.1
+curl -fsSLO https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.sh
+sh install.sh v1.2.2
# 또는 스크립트를 건너뜁니다. 스크립트가 만드는 체크아웃은 직접 만들 수 있습니다.
-git clone --depth 1 --branch v1.2.1 https://github.com/MongLong0214/commitlore
+git clone --depth 1 --branch v1.2.2 https://github.com/MongLong0214/commitlore
node commitlore/dist/commitlore.mjs --version
```
@@ -107,13 +107,13 @@ CommitLore는 그 판단을 코드 곁에 보관합니다.
macOS와 Linux:
```bash
-curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.sh | sh -s v1.2.1
+curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.sh | sh -s v1.2.2
```
Windows:
```powershell
-& ([scriptblock]::Create((irm https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.ps1))) v1.2.1
+& ([scriptblock]::Create((irm https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.ps1))) v1.2.2
```
Node.js 22.23.2+와 Git이 필요합니다. 스크립트는 무엇이든 쓰기 전에 둘을 확인합니다.
diff --git a/README.md b/README.md
index e3ad22b1..ce9331e3 100644
--- a/README.md
+++ b/README.md
@@ -48,18 +48,18 @@
```bash
-curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.sh | sh -s v1.2.1
+curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.sh | sh -s v1.2.2
```
Prefer to read the installer first?
```bash
-curl -fsSLO https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.sh
-sh install.sh v1.2.1
+curl -fsSLO https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.sh
+sh install.sh v1.2.2
# Or skip the script: the checkout it makes is one you can make yourself.
-git clone --depth 1 --branch v1.2.1 https://github.com/MongLong0214/commitlore
+git clone --depth 1 --branch v1.2.2 https://github.com/MongLong0214/commitlore
node commitlore/dist/commitlore.mjs --version
```
@@ -109,13 +109,13 @@ preserve, not for narrating every change.
macOS and Linux:
```bash
-curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.sh | sh -s v1.2.1
+curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.sh | sh -s v1.2.2
```
Windows:
```powershell
-& ([scriptblock]::Create((irm https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.ps1))) v1.2.1
+& ([scriptblock]::Create((irm https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.ps1))) v1.2.2
```
Requires Node.js 22.23.2+ and Git. The script checks both before it writes anything.
diff --git a/README.zh-CN.md b/README.zh-CN.md
index fb2f22b6..8448a86b 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -47,18 +47,18 @@
```bash
-curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.sh | sh -s v1.2.1
+curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.sh | sh -s v1.2.2
```
想先阅读安装器吗?
```bash
-curl -fsSLO https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.sh
-sh install.sh v1.2.1
+curl -fsSLO https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.sh
+sh install.sh v1.2.2
# 或者跳过脚本:它创建的检出,你自己也能创建。
-git clone --depth 1 --branch v1.2.1 https://github.com/MongLong0214/commitlore
+git clone --depth 1 --branch v1.2.2 https://github.com/MongLong0214/commitlore
node commitlore/dist/commitlore.mjs --version
```
@@ -105,13 +105,13 @@ CommitLore 把那份判断留在代码旁边。
macOS 和 Linux:
```bash
-curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.sh | sh -s v1.2.1
+curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.sh | sh -s v1.2.2
```
Windows:
```powershell
-& ([scriptblock]::Create((irm https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.ps1))) v1.2.1
+& ([scriptblock]::Create((irm https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.ps1))) v1.2.2
```
需要 Node.js 22.23.2+ 和 Git。脚本会在写入任何内容前检查两者。
diff --git a/install.ps1 b/install.ps1
index 30b85c06..0e7bc0fd 100644
--- a/install.ps1
+++ b/install.ps1
@@ -1,8 +1,8 @@
<#
Installs commitlore from source on Windows, for any agent that is not Claude Code.
- irm https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.ps1 | iex
- & ([scriptblock]::Create((irm https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.ps1))) v1.2.1
+ irm https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.ps1 | iex
+ & ([scriptblock]::Create((irm https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.ps1))) v1.2.2
Claude Code users do not need this script. The repository is itself a plugin
marketplace (ADR-0011), so two /plugin commands register the MCP server, the
diff --git a/install.sh b/install.sh
index d279dd53..c05ff382 100755
--- a/install.sh
+++ b/install.sh
@@ -1,8 +1,8 @@
#!/bin/sh
# Installs commitlore from source, for any agent that is not Claude Code.
#
-# curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.sh | sh
-# curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.sh | sh -s v1.2.1
+# curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.sh | sh
+# curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.sh | sh -s v1.2.2
#
# **Claude Code users do not need this script.** The repository is itself a
# plugin marketplace (ADR-0011), so two `/plugin` commands register the MCP
diff --git a/package-lock.json b/package-lock.json
index 99a685e8..24b1d386 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "commitlore",
- "version": "1.2.1",
+ "version": "1.2.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "commitlore",
- "version": "1.2.1",
+ "version": "1.2.2",
"license": "MIT",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.30.0",
diff --git a/package.json b/package.json
index f5f49075..b2e96388 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "commitlore",
- "version": "1.2.1",
+ "version": "1.2.2",
"description": "Git-native, lifecycle-aware decision memory for coding agents",
"license": "MIT",
"private": true,
diff --git a/server.json b/server.json
index a3bdc2cf..f597fef4 100644
--- a/server.json
+++ b/server.json
@@ -8,7 +8,7 @@
"source": "github"
},
"websiteUrl": "https://github.com/MongLong0214/commitlore#readme",
- "version": "1.2.1",
+ "version": "1.2.2",
"_meta": {
"io.modelcontextprotocol.registry/publisher-provided": {
"registryFit": "Distribution is a tagged git checkout plus a Claude Code plugin marketplace (ADR-0011 registry-free git distribution, ADR-0026 no compiled executables and no uploaded release asset), so no official package type applies and this record relies on websiteUrl plus publisher metadata.",
@@ -18,8 +18,8 @@
"/plugin marketplace add MongLong0214/commitlore",
"/plugin install commitlore@commitlore"
],
- "installer": "curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.1/install.sh | sh -s v1.2.1",
- "release": "https://github.com/MongLong0214/commitlore/releases/tag/v1.2.1"
+ "installer": "curl -fsSL https://raw.githubusercontent.com/MongLong0214/commitlore/v1.2.2/install.sh | sh -s v1.2.2",
+ "release": "https://github.com/MongLong0214/commitlore/releases/tag/v1.2.2"
},
"runtime": {
"transport": "stdio",