jun-claude-code 0.1.2 → 0.1.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 +3 -3
- package/dist/copy.d.ts +1 -1
- package/dist/copy.js +14 -6
- package/package.json +1 -1
- package/templates/global/hooks/_dedup.sh +32 -0
- package/templates/global/hooks/dangerous-command-blocker.sh +5 -0
- package/templates/global/hooks/skill-forced-subagent.sh +4 -0
- package/templates/global/hooks/skill-forced.sh +4 -0
- package/templates/global/hooks/workflow-enforced.sh +4 -0
|
@@ -292,7 +292,7 @@ function writeJson(filePath, data) {
|
|
|
292
292
|
const result = readJson(path.join(destDir, 'settings.json'));
|
|
293
293
|
(0, vitest_1.expect)(result.statusLine.command).toBe('dest-status');
|
|
294
294
|
});
|
|
295
|
-
(0, vitest_1.it)('should convert ~/.claude/ to
|
|
295
|
+
(0, vitest_1.it)('should convert ~/.claude/ to .claude/ in command fields when project=true', () => {
|
|
296
296
|
const sourceSettings = {
|
|
297
297
|
hooks: {
|
|
298
298
|
UserPromptSubmit: [
|
|
@@ -315,8 +315,8 @@ function writeJson(filePath, data) {
|
|
|
315
315
|
writeJson(path.join(sourceDir, 'settings.json'), sourceSettings);
|
|
316
316
|
(0, copy_1.mergeSettingsJson)(sourceDir, destDir, { project: true });
|
|
317
317
|
const result = readJson(path.join(destDir, 'settings.json'));
|
|
318
|
-
(0, vitest_1.expect)(result.hooks.UserPromptSubmit[0].hooks[0].command).toBe('
|
|
319
|
-
(0, vitest_1.expect)(result.hooks.PreToolUse[0].hooks[0].command).toBe('
|
|
318
|
+
(0, vitest_1.expect)(result.hooks.UserPromptSubmit[0].hooks[0].command).toBe('.claude/hooks/skill-forced.sh');
|
|
319
|
+
(0, vitest_1.expect)(result.hooks.PreToolUse[0].hooks[0].command).toBe('.claude/hooks/blocker.sh');
|
|
320
320
|
});
|
|
321
321
|
(0, vitest_1.it)('should keep ~/.claude/ paths unchanged when project option is not set', () => {
|
|
322
322
|
const sourceSettings = {
|
package/dist/copy.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export interface CopyOptions {
|
|
|
8
8
|
* Hooks are merged per event key; duplicate hook entries (by deep equality) are skipped.
|
|
9
9
|
* Non-hook keys are shallow-merged (source wins for new keys, dest preserved for existing).
|
|
10
10
|
* statusLine is always excluded from merge (personal environment setting).
|
|
11
|
-
* When project=true, ~/.claude/ paths in command fields are converted to
|
|
11
|
+
* When project=true, ~/.claude/ paths in command fields are converted to .claude/.
|
|
12
12
|
*/
|
|
13
13
|
export declare function mergeSettingsJson(sourceDir: string, destDir: string, options?: {
|
|
14
14
|
project?: boolean;
|
package/dist/copy.js
CHANGED
|
@@ -126,13 +126,13 @@ function getDestClaudeDir() {
|
|
|
126
126
|
* Non-hook keys are shallow-merged (source wins for new keys, dest preserved for existing).
|
|
127
127
|
*/
|
|
128
128
|
/**
|
|
129
|
-
* Replace ~/.claude/ paths with
|
|
129
|
+
* Replace ~/.claude/ paths with .claude/ in all command fields (deep traverse).
|
|
130
130
|
*/
|
|
131
131
|
function replaceClaudePaths(obj) {
|
|
132
132
|
const result = {};
|
|
133
133
|
for (const [key, value] of Object.entries(obj)) {
|
|
134
134
|
if (key === 'command' && typeof value === 'string') {
|
|
135
|
-
result[key] = value.replace(/~\/\.claude\//g, '
|
|
135
|
+
result[key] = value.replace(/~\/\.claude\//g, '.claude/');
|
|
136
136
|
}
|
|
137
137
|
else if (Array.isArray(value)) {
|
|
138
138
|
result[key] = value.map((item) => typeof item === 'object' && item !== null ? replaceClaudePaths(item) : item);
|
|
@@ -151,7 +151,7 @@ function replaceClaudePaths(obj) {
|
|
|
151
151
|
* Hooks are merged per event key; duplicate hook entries (by deep equality) are skipped.
|
|
152
152
|
* Non-hook keys are shallow-merged (source wins for new keys, dest preserved for existing).
|
|
153
153
|
* statusLine is always excluded from merge (personal environment setting).
|
|
154
|
-
* When project=true, ~/.claude/ paths in command fields are converted to
|
|
154
|
+
* When project=true, ~/.claude/ paths in command fields are converted to .claude/.
|
|
155
155
|
*/
|
|
156
156
|
function mergeSettingsJson(sourceDir, destDir, options) {
|
|
157
157
|
const sourcePath = path.join(sourceDir, 'settings.json');
|
|
@@ -232,15 +232,23 @@ async function copyClaudeFiles(options = {}) {
|
|
|
232
232
|
console.error(chalk_1.default.red('Error:'), 'Source templates/global directory not found');
|
|
233
233
|
process.exit(1);
|
|
234
234
|
}
|
|
235
|
-
// Files to exclude from
|
|
236
|
-
const
|
|
235
|
+
// Files to exclude from all copies (merge-handled separately)
|
|
236
|
+
const EXCLUDE_ALWAYS = [
|
|
237
237
|
'settings.json',
|
|
238
238
|
'statusline-command.sh',
|
|
239
239
|
];
|
|
240
|
+
// Files to exclude only when installing to project
|
|
241
|
+
const EXCLUDE_FROM_PROJECT = [
|
|
242
|
+
'hooks/_dedup.sh',
|
|
243
|
+
];
|
|
240
244
|
// Get all files to copy
|
|
241
245
|
const allFiles = getAllFiles(sourceDir);
|
|
242
246
|
const files = allFiles.filter((file) => {
|
|
243
|
-
|
|
247
|
+
if (EXCLUDE_ALWAYS.includes(file))
|
|
248
|
+
return false;
|
|
249
|
+
if (project && EXCLUDE_FROM_PROJECT.includes(file))
|
|
250
|
+
return false;
|
|
251
|
+
return true;
|
|
244
252
|
});
|
|
245
253
|
if (files.length === 0) {
|
|
246
254
|
console.log(chalk_1.default.yellow('No files found in templates/global directory'));
|
package/package.json
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# _dedup.sh - Hook deduplication guard
|
|
3
|
+
# global과 project 양쪽에 hooks가 설치된 경우 중복 실행을 방지합니다.
|
|
4
|
+
# project-level hook이 우선이며, global hook은 project 버전이 존재하면 양보합니다.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# source "$(dirname "${BASH_SOURCE[0]}")/_dedup.sh"
|
|
8
|
+
# _hook_dedup_check "${BASH_SOURCE[0]}" || exit 0
|
|
9
|
+
|
|
10
|
+
_hook_dedup_check() {
|
|
11
|
+
local caller_path="$1"
|
|
12
|
+
local hook_name
|
|
13
|
+
hook_name="$(basename "$caller_path")"
|
|
14
|
+
|
|
15
|
+
local self_dir
|
|
16
|
+
self_dir="$(cd "$(dirname "$caller_path")" && pwd)"
|
|
17
|
+
|
|
18
|
+
local global_dir
|
|
19
|
+
global_dir="$(cd "$HOME/.claude/hooks" 2>/dev/null && pwd 2>/dev/null)"
|
|
20
|
+
|
|
21
|
+
# 내가 global 버전인지 확인
|
|
22
|
+
if [ "$self_dir" = "$global_dir" ]; then
|
|
23
|
+
# project-level 동일 hook이 존재하면 → global은 skip
|
|
24
|
+
local project_hook
|
|
25
|
+
project_hook="$(pwd)/.claude/hooks/$hook_name"
|
|
26
|
+
if [ -f "$project_hook" ]; then
|
|
27
|
+
return 1 # global yields to project → skip
|
|
28
|
+
fi
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
return 0 # project version 또는 global-only → proceed
|
|
32
|
+
}
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
# Dangerous Command Blocker - PreToolUse Hook
|
|
5
5
|
# Bash 도구 실행 전 위험한 명령어를 감지하여 차단합니다.
|
|
6
6
|
|
|
7
|
+
# Dedup: project-level 복사본이면 global에 양보
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
source "$SCRIPT_DIR/_dedup.sh"
|
|
10
|
+
_hook_dedup_check "${BASH_SOURCE[0]}" || exit 0
|
|
11
|
+
|
|
7
12
|
INPUT=$(cat)
|
|
8
13
|
|
|
9
14
|
# jq가 있으면 jq로, 없으면 grep/sed로 command 추출
|
|
@@ -9,6 +9,10 @@ CLAUDE_DIR="$(dirname "$SCRIPT_DIR")"
|
|
|
9
9
|
GLOBAL_CLAUDE_DIR="$HOME/.claude"
|
|
10
10
|
PROJECT_CLAUDE_DIR="$(pwd)/.claude"
|
|
11
11
|
|
|
12
|
+
# Dedup: project-level 복사본이면 global에 양보
|
|
13
|
+
source "$SCRIPT_DIR/_dedup.sh"
|
|
14
|
+
_hook_dedup_check "${BASH_SOURCE[0]}" || exit 0
|
|
15
|
+
|
|
12
16
|
echo "✅ [Hook] Subagent Skill 평가 프로토콜 실행됨"
|
|
13
17
|
|
|
14
18
|
cat << 'EOF'
|
|
@@ -9,6 +9,10 @@ CLAUDE_DIR="$(dirname "$SCRIPT_DIR")"
|
|
|
9
9
|
GLOBAL_CLAUDE_DIR="$HOME/.claude"
|
|
10
10
|
PROJECT_CLAUDE_DIR="$(pwd)/.claude"
|
|
11
11
|
|
|
12
|
+
# Dedup: project-level 복사본이면 global에 양보
|
|
13
|
+
source "$SCRIPT_DIR/_dedup.sh"
|
|
14
|
+
_hook_dedup_check "${BASH_SOURCE[0]}" || exit 0
|
|
15
|
+
|
|
12
16
|
echo "✅ [Hook] Skill/Agent 평가 프로토콜 실행됨"
|
|
13
17
|
|
|
14
18
|
cat << 'EOF'
|
|
@@ -9,6 +9,10 @@ CLAUDE_DIR="$(dirname "$SCRIPT_DIR")"
|
|
|
9
9
|
GLOBAL_CLAUDE_DIR="$HOME/.claude"
|
|
10
10
|
PROJECT_CLAUDE_DIR="$(pwd)/.claude"
|
|
11
11
|
|
|
12
|
+
# Dedup: project-level 복사본이면 global에 양보
|
|
13
|
+
source "$SCRIPT_DIR/_dedup.sh"
|
|
14
|
+
_hook_dedup_check "${BASH_SOURCE[0]}" || exit 0
|
|
15
|
+
|
|
12
16
|
echo "✅ [Hook] 워크플로우 순서 강제 프로토콜 실행됨"
|
|
13
17
|
|
|
14
18
|
cat << 'EOF'
|