claude-devkit-cli 1.2.3 → 1.2.5
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/package.json
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"use strict";
|
|
12
12
|
|
|
13
13
|
const fs = require("fs");
|
|
14
|
+
const path = require("path");
|
|
14
15
|
|
|
15
16
|
// Patterns that indicate lazy placeholder comments (case-insensitive)
|
|
16
17
|
const PLACEHOLDER_PATTERNS = [
|
|
@@ -63,6 +64,14 @@ function main() {
|
|
|
63
64
|
process.exit(0);
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
// Skip files outside the project directory (e.g. ~/.claude/plans/)
|
|
68
|
+
const filePath = payload.tool_input?.file_path;
|
|
69
|
+
if (filePath) {
|
|
70
|
+
const projectDir = process.cwd() + path.sep;
|
|
71
|
+
const resolved = path.resolve(filePath);
|
|
72
|
+
if (!resolved.startsWith(projectDir) && resolved !== process.cwd()) process.exit(0);
|
|
73
|
+
}
|
|
74
|
+
|
|
66
75
|
const oldStr = payload.tool_input?.old_string;
|
|
67
76
|
const newStr = payload.tool_input?.new_string;
|
|
68
77
|
|
|
@@ -59,6 +59,11 @@ function main() {
|
|
|
59
59
|
const filePath = payload.tool_input?.file_path;
|
|
60
60
|
if (!filePath) process.exit(0);
|
|
61
61
|
|
|
62
|
+
// Skip files outside the project directory (e.g. ~/.claude/plans/)
|
|
63
|
+
const projectDir = process.cwd() + path.sep;
|
|
64
|
+
const resolvedFile = path.resolve(filePath);
|
|
65
|
+
if (!resolvedFile.startsWith(projectDir) && resolvedFile !== process.cwd()) process.exit(0);
|
|
66
|
+
|
|
62
67
|
// Skip excluded patterns
|
|
63
68
|
if (matchesExclude(filePath)) process.exit(0);
|
|
64
69
|
|
|
@@ -76,7 +81,6 @@ function main() {
|
|
|
76
81
|
try {
|
|
77
82
|
const stat = fs.statSync(filePath);
|
|
78
83
|
if (stat.size > MAX_BYTES) {
|
|
79
|
-
// >1MB = definitely over threshold, warn without exact count
|
|
80
84
|
const rel = path.relative(process.cwd(), filePath);
|
|
81
85
|
process.stdout.write(JSON.stringify({
|
|
82
86
|
continue: true,
|
|
@@ -84,7 +88,7 @@ function main() {
|
|
|
84
88
|
hookEventName: "PostToolUse",
|
|
85
89
|
additionalContext: `Warning: ${rel} is ${Math.round(stat.size / 1024)}KB. Consider splitting into smaller modules.`,
|
|
86
90
|
},
|
|
87
|
-
}));
|
|
91
|
+
}) + "\n");
|
|
88
92
|
process.exit(0);
|
|
89
93
|
}
|
|
90
94
|
const buf = fs.readFileSync(filePath);
|
|
@@ -108,7 +112,7 @@ function main() {
|
|
|
108
112
|
hookEventName: "PostToolUse",
|
|
109
113
|
additionalContext: warning,
|
|
110
114
|
},
|
|
111
|
-
})
|
|
115
|
+
}) + "\n"
|
|
112
116
|
);
|
|
113
117
|
}
|
|
114
118
|
|
|
@@ -19,42 +19,44 @@ set -euo pipefail
|
|
|
19
19
|
INPUT=$(cat)
|
|
20
20
|
[[ -z "$INPUT" ]] && exit 0
|
|
21
21
|
|
|
22
|
-
#
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
# Extract command from JSON — try node first, fall back to grep/sed
|
|
23
|
+
extract_command() {
|
|
24
|
+
if command -v node &>/dev/null; then
|
|
25
|
+
printf '%s' "$1" | node -e "
|
|
26
|
+
try {
|
|
27
|
+
const d = JSON.parse(require('fs').readFileSync(0, 'utf-8'));
|
|
28
|
+
const cmd = d.tool_input?.command;
|
|
29
|
+
if (typeof cmd === 'string') process.stdout.write(cmd);
|
|
30
|
+
} catch {}
|
|
31
|
+
" 2>/dev/null
|
|
32
|
+
else
|
|
33
|
+
# Lightweight fallback: extract "command":"..." from JSON
|
|
34
|
+
printf '%s' "$1" | grep -o '"command":"[^"]*"' | head -1 | sed 's/^"command":"//;s/"$//' 2>/dev/null
|
|
35
|
+
fi
|
|
36
|
+
}
|
|
27
37
|
|
|
28
|
-
|
|
29
|
-
COMMAND=$(printf '%s' "$INPUT" | node -e "
|
|
30
|
-
try {
|
|
31
|
-
const d = JSON.parse(require('fs').readFileSync(0, 'utf-8'));
|
|
32
|
-
const cmd = d.tool_input?.command;
|
|
33
|
-
if (typeof cmd === 'string') process.stdout.write(cmd);
|
|
34
|
-
else process.exit(0);
|
|
35
|
-
} catch { process.exit(0); }
|
|
36
|
-
" 2>/dev/null) || exit 0
|
|
38
|
+
COMMAND=$(extract_command "$INPUT") || exit 0
|
|
37
39
|
|
|
38
40
|
[[ -z "$COMMAND" ]] && exit 0
|
|
39
41
|
|
|
40
42
|
# ─── Blocked directory patterns ─────────────────────────────────────
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
BLOCKED
|
|
45
|
-
BLOCKED+="
|
|
46
|
-
BLOCKED+="|
|
|
47
|
-
BLOCKED+="|
|
|
44
|
+
# Use word boundaries (\b) and explicit path separators to avoid substring false positives
|
|
45
|
+
# e.g. "build/" should not match "rebuild/src" or "my-build-tool"
|
|
46
|
+
BLOCKED="(^|[ /])node_modules(/|$| )"
|
|
47
|
+
BLOCKED+="|(__pycache__)"
|
|
48
|
+
BLOCKED+="|\.git/(objects|refs)"
|
|
49
|
+
BLOCKED+="|(^|[ /])dist(/|$| )"
|
|
50
|
+
BLOCKED+="|(^|[ /])build(/|$| )"
|
|
48
51
|
BLOCKED+="|\.next/"
|
|
49
|
-
BLOCKED+="|vendor
|
|
50
|
-
BLOCKED+="|Pods
|
|
52
|
+
BLOCKED+="|(^|[ /])vendor(/|$| )"
|
|
53
|
+
BLOCKED+="|(^|[ /])Pods(/|$| )"
|
|
51
54
|
BLOCKED+="|\.build/"
|
|
52
55
|
BLOCKED+="|DerivedData"
|
|
53
56
|
BLOCKED+="|\.gradle/"
|
|
54
|
-
BLOCKED+="|target/debug"
|
|
55
|
-
BLOCKED+="|target/release"
|
|
57
|
+
BLOCKED+="|target/(debug|release)(/|$| )"
|
|
56
58
|
BLOCKED+="|\.nuget"
|
|
57
|
-
BLOCKED+="|\.cache"
|
|
59
|
+
BLOCKED+="|\.cache(/|$| )"
|
|
58
60
|
|
|
59
61
|
# Append project-specific patterns from env
|
|
60
62
|
if [[ -n "${PATH_GUARD_EXTRA:-}" ]]; then
|
|
@@ -57,13 +57,9 @@ is_sensitive() {
|
|
|
57
57
|
return 0 ;;
|
|
58
58
|
serviceAccountKey.json|service-account*.json)
|
|
59
59
|
return 0 ;;
|
|
60
|
-
|
|
61
|
-
# config.json only sensitive inside .docker/
|
|
62
|
-
|
|
63
|
-
[[ "$filepath" == *".docker/config.json"* ]] && return 0
|
|
64
|
-
else
|
|
65
|
-
return 0
|
|
66
|
-
fi
|
|
60
|
+
config.json)
|
|
61
|
+
# config.json only sensitive inside .docker/
|
|
62
|
+
[[ "$filepath" == *".docker/config.json"* ]] && return 0
|
|
67
63
|
;;
|
|
68
64
|
esac
|
|
69
65
|
|
|
@@ -151,11 +147,28 @@ warn_with_message() {
|
|
|
151
147
|
exit 0
|
|
152
148
|
}
|
|
153
149
|
|
|
150
|
+
# ─── Fast-path: skip obviously safe files ──────────────────────────
|
|
151
|
+
|
|
152
|
+
fast_path_safe() {
|
|
153
|
+
local ext="${1##*.}"
|
|
154
|
+
case "$ext" in
|
|
155
|
+
md|ts|tsx|js|jsx|css|scss|html|svg|json|yaml|yml|toml|xml|txt|sh|py|rb|rs|go|java|kt|swift|c|cpp|h|hpp|cs|vue|svelte|astro)
|
|
156
|
+
# But json could be sensitive — check name
|
|
157
|
+
if [[ "$ext" == "json" ]]; then
|
|
158
|
+
return 1 # not fast-path safe, need full check
|
|
159
|
+
fi
|
|
160
|
+
return 0 ;;
|
|
161
|
+
esac
|
|
162
|
+
return 1
|
|
163
|
+
}
|
|
164
|
+
|
|
154
165
|
# ─── Check direct file access (Read/Write/Edit) → BLOCK ────────────
|
|
155
166
|
|
|
156
167
|
if [[ -n "$FILE_PATH" ]]; then
|
|
157
|
-
if
|
|
158
|
-
|
|
168
|
+
if ! fast_path_safe "$FILE_PATH"; then
|
|
169
|
+
if is_sensitive "$FILE_PATH" || check_agentignore "$FILE_PATH"; then
|
|
170
|
+
block_with_message "$FILE_PATH"
|
|
171
|
+
fi
|
|
159
172
|
fi
|
|
160
173
|
fi
|
|
161
174
|
|