delimit-cli 4.1.48 → 4.1.49
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/CHANGELOG.md +18 -0
- package/bin/delimit-setup.js +5 -2
- package/lib/cross-model-hooks.js +30 -6
- package/lib/hooks-installer.js +19 -11
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [4.1.49] - 2026-04-09
|
|
4
|
+
|
|
5
|
+
### Fixed (full preservation audit follow-up to 4.1.48)
|
|
6
|
+
- **Project `.claude/settings.json` hooks clobber** — `installClaudeHooks` was replacing the project-level `.claude/settings.json` hooks object with the merged-with-global config, propagating global hooks into every project file and wiping any project-local hooks the user had set. Now merges only Delimit-owned hook groups (entries whose command contains `delimit`) into existing project hooks; project-specific user hooks survive.
|
|
7
|
+
- **Gemini `general.defaultApprovalMode` clobber** — `delimit-cli setup` was force-setting Gemini's `defaultApprovalMode` to `auto_edit` on every run, overwriting whatever the user had chosen (e.g. `manual`). Now only sets it when missing.
|
|
8
|
+
- **`~/.claude.json` MCP hooks replacement** — `lib/hooks-installer.js` (opt-in via `delimit-cli hooks install`) replaced `preCommand` / `postCommand` / `authentication` / `audit` keys on every install. Now only fills in missing keys, preserving any user-chosen MCP hook commands.
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **`tests/setup-no-clobber.test.js`** — dedicated regression suite that runs setup helpers against synthetic fresh-user HOME directories with pre-populated user customizations (project hooks, Gemini approval mode, custom MCP hook commands) and asserts none get clobbered. 5 tests, all passing.
|
|
12
|
+
|
|
13
|
+
### Audit results
|
|
14
|
+
- Audited every `fs.writeFileSync` in `bin/delimit-setup.js`, `lib/cross-model-hooks.js`, `lib/hooks-installer.js`, `adapters/cursor-rules.js`, and `scripts/postinstall.js`.
|
|
15
|
+
- All remaining writes are either delimit-owned (shims, hook scripts, generated `delimit.md`), guarded by `!fs.existsSync` (models.json, social_target_config.json, codex empty file), or surgical merges that preserve user content (`.mcp.json` mcpServers, `.claude/settings.json` allowList, `.codex/config.toml` mcp_servers.delimit block, `.cursor/mcp.json` mcpServers, rc-file PATH append).
|
|
16
|
+
- The full preservation contract is now: `delimit-cli setup` may safely run on any user machine, including via the shim auto-update flow, without destroying user state. New installs and upgrades are equivalent for everything except delimit-owned files.
|
|
17
|
+
|
|
18
|
+
### Tests
|
|
19
|
+
- 129/129 passing (was 124).
|
|
20
|
+
|
|
3
21
|
## [4.1.48] - 2026-04-09
|
|
4
22
|
|
|
5
23
|
### Fixed
|
package/bin/delimit-setup.js
CHANGED
|
@@ -430,9 +430,12 @@ async function main() {
|
|
|
430
430
|
cwd: path.join(DELIMIT_HOME, 'server'),
|
|
431
431
|
env: { PYTHONPATH: path.join(DELIMIT_HOME, 'server') }
|
|
432
432
|
};
|
|
433
|
-
// Auto-approve all tools — users should not be prompted for every Delimit call
|
|
433
|
+
// Auto-approve all tools — users should not be prompted for every Delimit call.
|
|
434
|
+
// Only set if missing — never clobber the user's chosen approval mode on upgrade.
|
|
434
435
|
if (!geminiConfig.general) geminiConfig.general = {};
|
|
435
|
-
geminiConfig.general.defaultApprovalMode
|
|
436
|
+
if (!geminiConfig.general.defaultApprovalMode) {
|
|
437
|
+
geminiConfig.general.defaultApprovalMode = 'auto_edit';
|
|
438
|
+
}
|
|
436
439
|
fs.writeFileSync(GEMINI_CONFIG, JSON.stringify(geminiConfig, null, 2));
|
|
437
440
|
if (geminiExisted) {
|
|
438
441
|
await logp(` ${green('✓')} Updated Delimit paths in Gemini CLI config`);
|
package/lib/cross-model-hooks.js
CHANGED
|
@@ -577,14 +577,38 @@ echo ""
|
|
|
577
577
|
const configJson = JSON.stringify(config, null, 2);
|
|
578
578
|
for (const target of writeTargets) {
|
|
579
579
|
try {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
const existing = JSON.parse(fs.readFileSync(target, 'utf-8'));
|
|
583
|
-
existing.hooks = config.hooks;
|
|
584
|
-
fs.writeFileSync(target, JSON.stringify(existing, null, 2));
|
|
585
|
-
} else {
|
|
580
|
+
if (target === configPath) {
|
|
581
|
+
// Global ~/.claude/settings.json: write the merged config we built
|
|
586
582
|
fs.writeFileSync(target, configJson);
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Project settings (.claude/settings.json in cwd): merge ONLY the
|
|
587
|
+
// Delimit-added hook entries into existing project hooks. Never
|
|
588
|
+
// overwrite the project's own hook entries with global ones.
|
|
589
|
+
// Previous behavior (`existing.hooks = config.hooks`) propagated
|
|
590
|
+
// every global hook into project files, wiping project-local hooks
|
|
591
|
+
// and leaking unrelated user customizations across repos.
|
|
592
|
+
let existing = {};
|
|
593
|
+
if (fs.existsSync(target)) {
|
|
594
|
+
try { existing = JSON.parse(fs.readFileSync(target, 'utf-8')); } catch { existing = {}; }
|
|
595
|
+
}
|
|
596
|
+
if (!existing.hooks) existing.hooks = {};
|
|
597
|
+
|
|
598
|
+
for (const [event, groups] of Object.entries(config.hooks || {})) {
|
|
599
|
+
if (!Array.isArray(groups)) continue;
|
|
600
|
+
if (!existing.hooks[event]) existing.hooks[event] = [];
|
|
601
|
+
for (const group of groups) {
|
|
602
|
+
const cmds = (group.hooks || []).map(h => h.command || '');
|
|
603
|
+
// Only propagate Delimit-owned hook groups to project files
|
|
604
|
+
if (!cmds.some(c => c.includes('delimit'))) continue;
|
|
605
|
+
const alreadyHas = existing.hooks[event].some(eg =>
|
|
606
|
+
(eg.hooks || []).some(h => cmds.includes(h.command))
|
|
607
|
+
);
|
|
608
|
+
if (!alreadyHas) existing.hooks[event].push(group);
|
|
609
|
+
}
|
|
587
610
|
}
|
|
611
|
+
fs.writeFileSync(target, JSON.stringify(existing, null, 2));
|
|
588
612
|
} catch {}
|
|
589
613
|
}
|
|
590
614
|
return changes;
|
package/lib/hooks-installer.js
CHANGED
|
@@ -185,29 +185,37 @@ class DelimitHooksInstaller {
|
|
|
185
185
|
|
|
186
186
|
async configureClaudeCode() {
|
|
187
187
|
const claudeConfigPath = path.join(process.env.HOME, '.claude.json');
|
|
188
|
-
|
|
188
|
+
|
|
189
189
|
if (fs.existsSync(claudeConfigPath)) {
|
|
190
190
|
try {
|
|
191
191
|
const config = JSON.parse(fs.readFileSync(claudeConfigPath, 'utf8'));
|
|
192
|
-
|
|
193
|
-
//
|
|
192
|
+
|
|
193
|
+
// Preserve any existing hooks the user has set. Only fill in
|
|
194
|
+
// Delimit MCP hooks if those specific keys are missing —
|
|
195
|
+
// never overwrite a user-chosen preCommand/postCommand.
|
|
194
196
|
if (!config.hooks) {
|
|
195
197
|
config.hooks = {};
|
|
196
198
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
199
|
+
const delimitHooks = {
|
|
200
|
+
preCommand: path.join(this.mcpHooksDir, 'pre-mcp-call'),
|
|
201
|
+
postCommand: path.join(this.mcpHooksDir, 'post-mcp-call'),
|
|
202
|
+
authentication: path.join(this.mcpHooksDir, 'mcp-auth'),
|
|
203
|
+
audit: path.join(this.mcpHooksDir, 'mcp-audit'),
|
|
204
|
+
};
|
|
205
|
+
for (const [key, value] of Object.entries(delimitHooks)) {
|
|
206
|
+
if (!config.hooks[key]) {
|
|
207
|
+
config.hooks[key] = value;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Add Delimit governance settings (own namespace, safe to set)
|
|
204
212
|
config.delimitGovernance = {
|
|
205
213
|
enabled: true,
|
|
206
214
|
agent: 'http://127.0.0.1:7823',
|
|
207
215
|
mode: 'auto',
|
|
208
216
|
hooks: this.mcpHooks.map(h => path.join(this.mcpHooksDir, h))
|
|
209
217
|
};
|
|
210
|
-
|
|
218
|
+
|
|
211
219
|
fs.writeFileSync(claudeConfigPath, JSON.stringify(config, null, 2));
|
|
212
220
|
console.log(chalk.green(' ✓ Claude Code configuration updated'));
|
|
213
221
|
} catch (e) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "delimit-cli",
|
|
3
3
|
"mcpName": "io.github.delimit-ai/delimit-mcp-server",
|
|
4
|
-
"version": "4.1.
|
|
4
|
+
"version": "4.1.49",
|
|
5
5
|
"description": "Unify Claude Code, Codex, Cursor, and Gemini CLI with persistent context, governance, and multi-model debate.",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"files": [
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"postinstall": "node scripts/postinstall.js",
|
|
36
36
|
"sync-gateway": "bash scripts/sync-gateway.sh",
|
|
37
37
|
"prepublishOnly": "bash scripts/publish-ci-guard.sh && npm run sync-gateway && bash scripts/security-check.sh",
|
|
38
|
-
"test": "node --test tests/setup-onboarding.test.js tests/setup-matrix.test.js tests/config-export-import.test.js tests/cross-model-hooks.test.js tests/golden-path.test.js tests/v420-features.test.js"
|
|
38
|
+
"test": "node --test tests/setup-onboarding.test.js tests/setup-matrix.test.js tests/setup-no-clobber.test.js tests/config-export-import.test.js tests/cross-model-hooks.test.js tests/golden-path.test.js tests/v420-features.test.js"
|
|
39
39
|
},
|
|
40
40
|
"keywords": [
|
|
41
41
|
"openapi",
|