Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
2 changes: 1 addition & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
5 changes: 2 additions & 3 deletions .mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
"commitlore": {
"command": "node",
"args": [
"./dist/commitlore.mjs",
"${CLAUDE_PLUGIN_ROOT:-.}/dist/commitlore.mjs",
"mcp"
],
"cwd": "."
]
}
}
}
50 changes: 50 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
`<session-cwd>/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.
Expand Down
12 changes: 6 additions & 6 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,18 @@
</p>

```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
```

<details>
<summary>先にインストーラーを読みたいですか?</summary>

```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
```

Expand Down Expand Up @@ -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 が必要です。スクリプトは何かを書き込む前に両方を確認します。
Expand Down
12 changes: 6 additions & 6 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,18 @@
</p>

```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
```

<details>
<summary>먼저 설치기를 읽어 보고 싶나요?</summary>

```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
```

Expand Down Expand Up @@ -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이 필요합니다. 스크립트는 무엇이든 쓰기 전에 둘을 확인합니다.
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,18 @@
</p>

```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
```

<details>
<summary>Prefer to read the installer first?</summary>

```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
```

Expand Down Expand Up @@ -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.
Expand Down
12 changes: 6 additions & 6 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,18 @@
</p>

```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
```

<details>
<summary>想先阅读安装器吗?</summary>

```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
```

Expand Down Expand Up @@ -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。脚本会在写入任何内容前检查两者。
Expand Down
4 changes: 2 additions & 2 deletions docs/COMPATIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |

Expand All @@ -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 |
Expand Down
4 changes: 2 additions & 2 deletions install.ps1
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 2 additions & 2 deletions install.sh
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
6 changes: 3 additions & 3 deletions server.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand All @@ -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",
Expand Down
38 changes: 36 additions & 2 deletions src/core/mcp-registration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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;
Expand All @@ -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),
};
};

/**
Expand Down
Loading
Loading