jun-claude-code 0.2.1 → 0.2.3
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/dist/__tests__/merge-settings.test.js +98 -0
- package/dist/copy.js +4 -0
- package/package.json +1 -1
- package/templates/global/hooks/dangerous-command-blocker.sh +1 -1
- package/templates/global/hooks/skill-forced-subagent.sh +1 -1
- package/templates/global/hooks/skill-forced.sh +1 -1
- package/templates/global/hooks/workflow-enforced.sh +1 -1
|
@@ -335,6 +335,104 @@ function writeJson(filePath, data) {
|
|
|
335
335
|
const result = readJson(path.join(destDir, 'settings.json'));
|
|
336
336
|
(0, vitest_1.expect)(result.hooks.UserPromptSubmit[0].hooks[0].command).toBe('~/.claude/hooks/skill-forced.sh');
|
|
337
337
|
});
|
|
338
|
+
(0, vitest_1.it)('should not duplicate hooks when project=true and run 3 times (idempotency)', () => {
|
|
339
|
+
const sourceSettings = {
|
|
340
|
+
hooks: {
|
|
341
|
+
UserPromptSubmit: [
|
|
342
|
+
{
|
|
343
|
+
hooks: [
|
|
344
|
+
{ type: 'command', command: '~/.claude/hooks/skill-forced.sh' },
|
|
345
|
+
],
|
|
346
|
+
},
|
|
347
|
+
],
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
writeJson(path.join(sourceDir, 'settings.json'), sourceSettings);
|
|
351
|
+
(0, copy_1.mergeSettingsJson)(sourceDir, destDir, { project: true });
|
|
352
|
+
(0, copy_1.mergeSettingsJson)(sourceDir, destDir, { project: true });
|
|
353
|
+
(0, copy_1.mergeSettingsJson)(sourceDir, destDir, { project: true });
|
|
354
|
+
const result = readJson(path.join(destDir, 'settings.json'));
|
|
355
|
+
(0, vitest_1.expect)(result.hooks.UserPromptSubmit).toHaveLength(1);
|
|
356
|
+
(0, vitest_1.expect)(result.hooks.UserPromptSubmit[0].hooks[0].command).toBe('.claude/hooks/skill-forced.sh');
|
|
357
|
+
});
|
|
358
|
+
(0, vitest_1.it)('should not duplicate hooks across multiple events in project mode', () => {
|
|
359
|
+
const sourceSettings = {
|
|
360
|
+
hooks: {
|
|
361
|
+
SubagentStart: [
|
|
362
|
+
{ hooks: [{ type: 'command', command: '~/.claude/hooks/subagent.sh' }] },
|
|
363
|
+
],
|
|
364
|
+
UserPromptSubmit: [
|
|
365
|
+
{ hooks: [{ type: 'command', command: '~/.claude/hooks/skill-forced.sh' }] },
|
|
366
|
+
],
|
|
367
|
+
PreToolUse: [
|
|
368
|
+
{
|
|
369
|
+
matcher: 'Bash',
|
|
370
|
+
hooks: [{ type: 'command', command: '~/.claude/hooks/blocker.sh' }],
|
|
371
|
+
},
|
|
372
|
+
],
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
writeJson(path.join(sourceDir, 'settings.json'), sourceSettings);
|
|
376
|
+
(0, copy_1.mergeSettingsJson)(sourceDir, destDir, { project: true });
|
|
377
|
+
(0, copy_1.mergeSettingsJson)(sourceDir, destDir, { project: true });
|
|
378
|
+
const result = readJson(path.join(destDir, 'settings.json'));
|
|
379
|
+
(0, vitest_1.expect)(result.hooks.SubagentStart).toHaveLength(1);
|
|
380
|
+
(0, vitest_1.expect)(result.hooks.UserPromptSubmit).toHaveLength(1);
|
|
381
|
+
(0, vitest_1.expect)(result.hooks.PreToolUse).toHaveLength(1);
|
|
382
|
+
});
|
|
383
|
+
(0, vitest_1.it)('should not duplicate matcher hooks in project mode on re-run', () => {
|
|
384
|
+
const sourceSettings = {
|
|
385
|
+
hooks: {
|
|
386
|
+
PreToolUse: [
|
|
387
|
+
{
|
|
388
|
+
matcher: 'Bash',
|
|
389
|
+
hooks: [{ type: 'command', command: '~/.claude/hooks/blocker.sh' }],
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
matcher: 'Write',
|
|
393
|
+
hooks: [{ type: 'command', command: '~/.claude/hooks/blocker.sh' }],
|
|
394
|
+
},
|
|
395
|
+
],
|
|
396
|
+
},
|
|
397
|
+
};
|
|
398
|
+
writeJson(path.join(sourceDir, 'settings.json'), sourceSettings);
|
|
399
|
+
(0, copy_1.mergeSettingsJson)(sourceDir, destDir, { project: true });
|
|
400
|
+
(0, copy_1.mergeSettingsJson)(sourceDir, destDir, { project: true });
|
|
401
|
+
const result = readJson(path.join(destDir, 'settings.json'));
|
|
402
|
+
(0, vitest_1.expect)(result.hooks.PreToolUse).toHaveLength(2);
|
|
403
|
+
(0, vitest_1.expect)(result.hooks.PreToolUse[0].matcher).toBe('Bash');
|
|
404
|
+
(0, vitest_1.expect)(result.hooks.PreToolUse[1].matcher).toBe('Write');
|
|
405
|
+
});
|
|
406
|
+
(0, vitest_1.it)('should dedup when dest has .claude/ paths and source has ~/.claude/ paths', () => {
|
|
407
|
+
const sourceSettings = {
|
|
408
|
+
hooks: {
|
|
409
|
+
UserPromptSubmit: [
|
|
410
|
+
{
|
|
411
|
+
hooks: [
|
|
412
|
+
{ type: 'command', command: '~/.claude/hooks/skill-forced.sh' },
|
|
413
|
+
],
|
|
414
|
+
},
|
|
415
|
+
],
|
|
416
|
+
},
|
|
417
|
+
};
|
|
418
|
+
writeJson(path.join(sourceDir, 'settings.json'), sourceSettings);
|
|
419
|
+
// Simulate dest from a previous project install (already converted)
|
|
420
|
+
const destSettings = {
|
|
421
|
+
hooks: {
|
|
422
|
+
UserPromptSubmit: [
|
|
423
|
+
{
|
|
424
|
+
hooks: [
|
|
425
|
+
{ type: 'command', command: '.claude/hooks/skill-forced.sh' },
|
|
426
|
+
],
|
|
427
|
+
},
|
|
428
|
+
],
|
|
429
|
+
},
|
|
430
|
+
};
|
|
431
|
+
writeJson(path.join(destDir, 'settings.json'), destSettings);
|
|
432
|
+
(0, copy_1.mergeSettingsJson)(sourceDir, destDir, { project: true });
|
|
433
|
+
const result = readJson(path.join(destDir, 'settings.json'));
|
|
434
|
+
(0, vitest_1.expect)(result.hooks.UserPromptSubmit).toHaveLength(1);
|
|
435
|
+
});
|
|
338
436
|
});
|
|
339
437
|
// ─── init-project.ts mergeSettingsJson (project) ───
|
|
340
438
|
(0, vitest_1.describe)('init-project.ts mergeSettingsJson', () => {
|
package/dist/copy.js
CHANGED
|
@@ -177,6 +177,10 @@ function mergeSettingsJson(sourceDir, destDir, options) {
|
|
|
177
177
|
destSettings = {};
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
|
+
// Convert ~/.claude/ → .claude/ paths in source BEFORE merge (for dedup key matching)
|
|
181
|
+
if (options?.project) {
|
|
182
|
+
sourceSettings = replaceClaudePaths(sourceSettings);
|
|
183
|
+
}
|
|
180
184
|
// Merge top-level keys (source fills in missing keys, dest's existing keys preserved)
|
|
181
185
|
for (const key of Object.keys(sourceSettings)) {
|
|
182
186
|
if (key === 'hooks' || key === 'statusLine') {
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# Dangerous Command Blocker - PreToolUse Hook
|
|
5
5
|
# Bash 도구 실행 전 위험한 명령어를 감지하여 차단합니다.
|
|
6
6
|
|
|
7
|
-
# Dedup: project
|
|
7
|
+
# Dedup: project 우선, global은 project 버전 존재 시 양보
|
|
8
8
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
9
|
source "$SCRIPT_DIR/_dedup.sh"
|
|
10
10
|
_hook_dedup_check "${BASH_SOURCE[0]}" || exit 0
|
|
@@ -9,7 +9,7 @@ CLAUDE_DIR="$(dirname "$SCRIPT_DIR")"
|
|
|
9
9
|
GLOBAL_CLAUDE_DIR="$HOME/.claude"
|
|
10
10
|
PROJECT_CLAUDE_DIR="$(pwd)/.claude"
|
|
11
11
|
|
|
12
|
-
# Dedup: project
|
|
12
|
+
# Dedup: project 우선, global은 project 버전 존재 시 양보
|
|
13
13
|
source "$SCRIPT_DIR/_dedup.sh"
|
|
14
14
|
_hook_dedup_check "${BASH_SOURCE[0]}" || exit 0
|
|
15
15
|
|
|
@@ -9,7 +9,7 @@ CLAUDE_DIR="$(dirname "$SCRIPT_DIR")"
|
|
|
9
9
|
GLOBAL_CLAUDE_DIR="$HOME/.claude"
|
|
10
10
|
PROJECT_CLAUDE_DIR="$(pwd)/.claude"
|
|
11
11
|
|
|
12
|
-
# Dedup: project
|
|
12
|
+
# Dedup: project 우선, global은 project 버전 존재 시 양보
|
|
13
13
|
source "$SCRIPT_DIR/_dedup.sh"
|
|
14
14
|
_hook_dedup_check "${BASH_SOURCE[0]}" || exit 0
|
|
15
15
|
|
|
@@ -9,7 +9,7 @@ CLAUDE_DIR="$(dirname "$SCRIPT_DIR")"
|
|
|
9
9
|
GLOBAL_CLAUDE_DIR="$HOME/.claude"
|
|
10
10
|
PROJECT_CLAUDE_DIR="$(pwd)/.claude"
|
|
11
11
|
|
|
12
|
-
# Dedup: project
|
|
12
|
+
# Dedup: project 우선, global은 project 버전 존재 시 양보
|
|
13
13
|
source "$SCRIPT_DIR/_dedup.sh"
|
|
14
14
|
_hook_dedup_check "${BASH_SOURCE[0]}" || exit 0
|
|
15
15
|
|