@ryuenn3123/agentic-senior-core 5.8.14 → 5.8.15
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/.agents/plugins/agentic-senior-core/hooks/hooks.json +12 -0
- package/.agents/plugins/agentic-senior-core/hooks/pre-tool-dependency-gate.js +50 -12
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.devin-plugin/plugin.json +1 -1
- package/.github/plugin/plugin.json +1 -1
- package/gemini-extension.json +1 -1
- package/hooks/hooks.json +12 -0
- package/hooks/pre-tool-dependency-gate.js +50 -12
- package/package.json +1 -1
- package/plugin.yaml +1 -1
|
@@ -48,6 +48,18 @@
|
|
|
48
48
|
"statusMessage": "ASC Pre-tool dependency check (Write)..."
|
|
49
49
|
}
|
|
50
50
|
]
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"matcher": "Bash",
|
|
54
|
+
"hooks": [
|
|
55
|
+
{
|
|
56
|
+
"type": "command",
|
|
57
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/pre-tool-dependency-gate.js\"; exit 0",
|
|
58
|
+
"commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { node \"$env:CLAUDE_PLUGIN_ROOT\\hooks\\pre-tool-dependency-gate.js\" }",
|
|
59
|
+
"timeout": 5,
|
|
60
|
+
"statusMessage": "ASC Pre-tool dependency check (Bash)..."
|
|
61
|
+
}
|
|
62
|
+
]
|
|
51
63
|
}
|
|
52
64
|
],
|
|
53
65
|
"PostToolUse": [
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Agentic Senior Core — PreToolUse dependency gate hook
|
|
3
|
-
// Hard-blocks edits/writes to package.json
|
|
3
|
+
// Hard-blocks edits/writes to package.json AND shell commands (npm install/yarn add)
|
|
4
|
+
// if new dependencies duplicate stdlib/platform features.
|
|
4
5
|
// Supports Claude Code hookSpecificOutput permissionDecision deny response format.
|
|
5
6
|
|
|
6
7
|
const fs = require('fs');
|
|
@@ -28,21 +29,27 @@ process.stdin.on('end', function () {
|
|
|
28
29
|
const data = JSON.parse(inputBuffer);
|
|
29
30
|
const toolName = data.tool_name || '';
|
|
30
31
|
const toolInput = data.tool_input || {};
|
|
31
|
-
|
|
32
|
+
let added = [];
|
|
32
33
|
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
34
|
+
if (toolName === 'Bash') {
|
|
35
|
+
const command = toolInput.command || '';
|
|
36
|
+
added = extractCommandDeps(command);
|
|
37
|
+
} else if (toolName === 'Edit' || toolName === 'Write') {
|
|
38
|
+
const filePath = toolInput.file_path || '';
|
|
39
|
+
if (!filePath.endsWith('package.json')) {
|
|
40
|
+
process.exit(0);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const target = toolName === 'Edit' ? (toolInput.new_string || '') : (toolInput.content || '');
|
|
44
|
+
const baseline = toolName === 'Edit' ? (toolInput.old_string || '') : '';
|
|
37
45
|
|
|
38
|
-
|
|
39
|
-
|
|
46
|
+
const depPattern = /"([^"]+)"\s*:\s*"[~^>=<*]?\d/g;
|
|
47
|
+
const newDeps = extractDeps(target, depPattern);
|
|
48
|
+
const oldDeps = extractDeps(baseline, depPattern);
|
|
40
49
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const oldDeps = extractDeps(baseline, depPattern);
|
|
50
|
+
added = newDeps.filter(function (d) { return oldDeps.indexOf(d) === -1; });
|
|
51
|
+
}
|
|
44
52
|
|
|
45
|
-
const added = newDeps.filter(function (d) { return oldDeps.indexOf(d) === -1; });
|
|
46
53
|
if (added.length === 0) {
|
|
47
54
|
process.exit(0);
|
|
48
55
|
return;
|
|
@@ -84,6 +91,37 @@ function extractDeps(text, pattern) {
|
|
|
84
91
|
return matches;
|
|
85
92
|
}
|
|
86
93
|
|
|
94
|
+
function extractCommandDeps(command) {
|
|
95
|
+
const installRegex = /(?:npm|yarn|pnpm|bun|ascx)\s+(?:install|i|add)(?:\s+[^\s]+)*/i;
|
|
96
|
+
if (!installRegex.test(command)) return [];
|
|
97
|
+
|
|
98
|
+
const parts = command.split(/\s+/);
|
|
99
|
+
const pkgIndex = parts.findIndex(p => ['install', 'i', 'add'].includes(p.toLowerCase()));
|
|
100
|
+
if (pkgIndex === -1 || pkgIndex >= parts.length - 1) return [];
|
|
101
|
+
|
|
102
|
+
const rawArgs = parts.slice(pkgIndex + 1);
|
|
103
|
+
const deps = [];
|
|
104
|
+
for (let arg of rawArgs) {
|
|
105
|
+
if (arg.startsWith('-')) continue; // Skip flags like -D, --save-dev
|
|
106
|
+
if (arg.startsWith('http://') || arg.startsWith('https://') || arg.startsWith('git+')) continue;
|
|
107
|
+
|
|
108
|
+
// Remove scope/version if any, e.g. lodash@^4.17 -> lodash, @types/node@18 -> @types/node
|
|
109
|
+
let name = arg;
|
|
110
|
+
if (name.startsWith('@')) {
|
|
111
|
+
const slashIndex = name.indexOf('/');
|
|
112
|
+
if (slashIndex !== -1) {
|
|
113
|
+
const atIndex = name.indexOf('@', slashIndex);
|
|
114
|
+
if (atIndex !== -1) name = name.substring(0, atIndex);
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
const atIndex = name.indexOf('@');
|
|
118
|
+
if (atIndex !== -1) name = name.substring(0, atIndex);
|
|
119
|
+
}
|
|
120
|
+
if (name) deps.push(name);
|
|
121
|
+
}
|
|
122
|
+
return deps;
|
|
123
|
+
}
|
|
124
|
+
|
|
87
125
|
function loadAllowlist() {
|
|
88
126
|
const allowed = new Set();
|
|
89
127
|
const candidates = [
|
package/gemini-extension.json
CHANGED
package/hooks/hooks.json
CHANGED
|
@@ -48,6 +48,18 @@
|
|
|
48
48
|
"statusMessage": "ASC Pre-tool dependency check (Write)..."
|
|
49
49
|
}
|
|
50
50
|
]
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"matcher": "Bash",
|
|
54
|
+
"hooks": [
|
|
55
|
+
{
|
|
56
|
+
"type": "command",
|
|
57
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/pre-tool-dependency-gate.js\"; exit 0",
|
|
58
|
+
"commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { node \"$env:CLAUDE_PLUGIN_ROOT\\hooks\\pre-tool-dependency-gate.js\" }",
|
|
59
|
+
"timeout": 5,
|
|
60
|
+
"statusMessage": "ASC Pre-tool dependency check (Bash)..."
|
|
61
|
+
}
|
|
62
|
+
]
|
|
51
63
|
}
|
|
52
64
|
],
|
|
53
65
|
"PostToolUse": [
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Agentic Senior Core — PreToolUse dependency gate hook
|
|
3
|
-
// Hard-blocks edits/writes to package.json
|
|
3
|
+
// Hard-blocks edits/writes to package.json AND shell commands (npm install/yarn add)
|
|
4
|
+
// if new dependencies duplicate stdlib/platform features.
|
|
4
5
|
// Supports Claude Code hookSpecificOutput permissionDecision deny response format.
|
|
5
6
|
|
|
6
7
|
const fs = require('fs');
|
|
@@ -28,21 +29,27 @@ process.stdin.on('end', function () {
|
|
|
28
29
|
const data = JSON.parse(inputBuffer);
|
|
29
30
|
const toolName = data.tool_name || '';
|
|
30
31
|
const toolInput = data.tool_input || {};
|
|
31
|
-
|
|
32
|
+
let added = [];
|
|
32
33
|
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
34
|
+
if (toolName === 'Bash') {
|
|
35
|
+
const command = toolInput.command || '';
|
|
36
|
+
added = extractCommandDeps(command);
|
|
37
|
+
} else if (toolName === 'Edit' || toolName === 'Write') {
|
|
38
|
+
const filePath = toolInput.file_path || '';
|
|
39
|
+
if (!filePath.endsWith('package.json')) {
|
|
40
|
+
process.exit(0);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const target = toolName === 'Edit' ? (toolInput.new_string || '') : (toolInput.content || '');
|
|
44
|
+
const baseline = toolName === 'Edit' ? (toolInput.old_string || '') : '';
|
|
37
45
|
|
|
38
|
-
|
|
39
|
-
|
|
46
|
+
const depPattern = /"([^"]+)"\s*:\s*"[~^>=<*]?\d/g;
|
|
47
|
+
const newDeps = extractDeps(target, depPattern);
|
|
48
|
+
const oldDeps = extractDeps(baseline, depPattern);
|
|
40
49
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const oldDeps = extractDeps(baseline, depPattern);
|
|
50
|
+
added = newDeps.filter(function (d) { return oldDeps.indexOf(d) === -1; });
|
|
51
|
+
}
|
|
44
52
|
|
|
45
|
-
const added = newDeps.filter(function (d) { return oldDeps.indexOf(d) === -1; });
|
|
46
53
|
if (added.length === 0) {
|
|
47
54
|
process.exit(0);
|
|
48
55
|
return;
|
|
@@ -84,6 +91,37 @@ function extractDeps(text, pattern) {
|
|
|
84
91
|
return matches;
|
|
85
92
|
}
|
|
86
93
|
|
|
94
|
+
function extractCommandDeps(command) {
|
|
95
|
+
const installRegex = /(?:npm|yarn|pnpm|bun|ascx)\s+(?:install|i|add)(?:\s+[^\s]+)*/i;
|
|
96
|
+
if (!installRegex.test(command)) return [];
|
|
97
|
+
|
|
98
|
+
const parts = command.split(/\s+/);
|
|
99
|
+
const pkgIndex = parts.findIndex(p => ['install', 'i', 'add'].includes(p.toLowerCase()));
|
|
100
|
+
if (pkgIndex === -1 || pkgIndex >= parts.length - 1) return [];
|
|
101
|
+
|
|
102
|
+
const rawArgs = parts.slice(pkgIndex + 1);
|
|
103
|
+
const deps = [];
|
|
104
|
+
for (let arg of rawArgs) {
|
|
105
|
+
if (arg.startsWith('-')) continue; // Skip flags like -D, --save-dev
|
|
106
|
+
if (arg.startsWith('http://') || arg.startsWith('https://') || arg.startsWith('git+')) continue;
|
|
107
|
+
|
|
108
|
+
// Remove scope/version if any, e.g. lodash@^4.17 -> lodash, @types/node@18 -> @types/node
|
|
109
|
+
let name = arg;
|
|
110
|
+
if (name.startsWith('@')) {
|
|
111
|
+
const slashIndex = name.indexOf('/');
|
|
112
|
+
if (slashIndex !== -1) {
|
|
113
|
+
const atIndex = name.indexOf('@', slashIndex);
|
|
114
|
+
if (atIndex !== -1) name = name.substring(0, atIndex);
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
const atIndex = name.indexOf('@');
|
|
118
|
+
if (atIndex !== -1) name = name.substring(0, atIndex);
|
|
119
|
+
}
|
|
120
|
+
if (name) deps.push(name);
|
|
121
|
+
}
|
|
122
|
+
return deps;
|
|
123
|
+
}
|
|
124
|
+
|
|
87
125
|
function loadAllowlist() {
|
|
88
126
|
const allowed = new Set();
|
|
89
127
|
const candidates = [
|
package/package.json
CHANGED
package/plugin.yaml
CHANGED