@ryuenn3123/agentic-senior-core 6.16.3 → 6.16.6
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/.codex-plugin/plugin.json +1 -1
- package/.agents/plugins/agentic-senior-core/commands/asc-pentest.md +2 -0
- package/.agents/plugins/agentic-senior-core/commands/asc-pentest.toml +3 -0
- package/.agents/plugins/agentic-senior-core/commands/asc-test.md +1 -1
- package/.agents/plugins/agentic-senior-core/commands/asc-ui.md +1 -1
- package/.agents/plugins/agentic-senior-core/hooks/dedup-gate.js +137 -128
- package/.agents/plugins/agentic-senior-core/hooks/hooks.json +97 -0
- package/.agents/plugins/agentic-senior-core/hooks/ladder-pulse.js +53 -36
- package/.agents/plugins/agentic-senior-core/hooks/lib/known-ui-slop-patterns.json +5 -0
- package/.agents/plugins/agentic-senior-core/hooks/path-util.cjs +118 -4
- package/.agents/plugins/agentic-senior-core/hooks/post-edit-enforce.js +98 -81
- package/.agents/plugins/agentic-senior-core/hooks/pre-tool-dependency-gate.js +168 -191
- package/.agents/plugins/agentic-senior-core/hooks/session-pulse.js +48 -35
- package/.agents/plugins/agentic-senior-core/hooks/session-start.js +62 -11
- package/.agents/plugins/agentic-senior-core/hooks.json +4 -94
- package/.agents/plugins/agentic-senior-core/plugin.json +5 -13
- package/.agents/plugins/agentic-senior-core/rules/agentic-senior-core.md +3 -1
- package/.agents/plugins/agentic-senior-core/skills/asc/SKILL.md +5 -2
- package/.agents/plugins/agentic-senior-core/skills/asc/references/writing-skills.md +1 -1
- package/.agents/plugins/agentic-senior-core/skills/asc-develop/SKILL.md +2 -2
- package/.agents/plugins/agentic-senior-core/skills/asc-pentest/SKILL.md +97 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-pentest/references/api-security-idor.md +105 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-pentest/references/dast-baseline.md +78 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-pentest/references/fuzzing-dsl.md +66 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-pentest/references/pentest-task-tree.md +70 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-review/SKILL.md +1 -1
- package/.agents/plugins/agentic-senior-core/skills/asc-test/SKILL.md +10 -3
- package/.agents/plugins/agentic-senior-core/skills/asc-test/references/mutation-testing.md +55 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-test/references/performance-budgets.md +67 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-test/references/property-testing.md +72 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-test/references/regression-proof.md +2 -2
- package/.agents/plugins/agentic-senior-core/skills/asc-test/references/tdd-cycle.md +2 -2
- package/.agents/plugins/agentic-senior-core/skills/asc-test/references/uat.md +1 -1
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/SKILL.md +31 -34
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/accessibility.md +2 -2
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/copy-i18n.md +24 -8
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/craft/color-surfaces.md +32 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/craft/composition-patterns.md +299 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/craft/themes-catalog.md +199 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/divergence.md +65 -24
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/modes/experience.md +4 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/modes/genre-calibration.md +93 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/modes/operate.md +31 -1
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/modes/persuade.md +33 -23
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/modes/read.md +4 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/redesign-audit.md +5 -10
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/states-feedback.md +2 -2
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-attio-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-attio.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-cal-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-cal.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-clerk-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-clerk.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-dub-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-dub.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-linear-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-linear.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-marketplace-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-marketplace.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-mintlify-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-mintlify.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-raycast-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-raycast.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-resend-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-resend.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-stripe-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-stripe.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-supabase-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-supabase.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-tldraw-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-tldraw.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-vercel-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-vercel.png +0 -0
- package/bin/agentic-senior-core.js +19 -4
- package/gemini-extension.json +2 -2
- package/lib/cli/ascx/runtime.mjs +5 -27
- package/lib/cli/ascx/windows-spawn.mjs +348 -0
- package/lib/cli/commands/bootstrap.mjs +56 -0
- package/lib/cli/commands/dedup.mjs +500 -0
- package/lib/cli/commands/git-hook-generator.mjs +3 -2
- package/lib/cli/commands/global.mjs +109 -53
- package/lib/cli/commands/preview.mjs +34 -20
- package/package.json +1 -1
- package/plugin.yaml +3 -1
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Agentic Senior Core — PreToolUse dependency gate hook
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// Supports
|
|
2
|
+
// Agentic Senior Core — PreToolUse dependency & safety gate hook
|
|
3
|
+
// Gates edits/writes to dependency manifests AND shell commands (npm install/yarn add/ascx,
|
|
4
|
+
// git commit/push, unverified/insecure git clone).
|
|
5
|
+
// Supports Antigravity (App, CLI, IDE), Claude Code, Codex CLI, and Copilot CLI.
|
|
6
6
|
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
|
+
const {
|
|
10
|
+
isAntigravityPayload,
|
|
11
|
+
resolveWorkspaceRoot,
|
|
12
|
+
normalizeEditToolCall,
|
|
13
|
+
} = require('./path-util.cjs');
|
|
9
14
|
|
|
10
15
|
let knownDuplicates = new Set();
|
|
11
16
|
let reviewDuplicates = new Set();
|
|
@@ -14,8 +19,6 @@ try {
|
|
|
14
19
|
const knownPath = path.join(__dirname, 'lib', 'known-duplicates.json');
|
|
15
20
|
if (fs.existsSync(knownPath)) {
|
|
16
21
|
const raw = JSON.parse(fs.readFileSync(knownPath, 'utf8'));
|
|
17
|
-
// Tiered structure (v5): 'block' = always redundant, 'review' = context-dependent.
|
|
18
|
-
// Legacy flat 'duplicates' arrays are treated as review-tier for backward compatibility.
|
|
19
22
|
if (Array.isArray(raw.block) || Array.isArray(raw.review)) {
|
|
20
23
|
knownDuplicates = new Set(raw.block || []);
|
|
21
24
|
reviewDuplicates = new Set(raw.review || []);
|
|
@@ -25,193 +28,184 @@ try {
|
|
|
25
28
|
}
|
|
26
29
|
}
|
|
27
30
|
} catch (_) {
|
|
28
|
-
// Fallback if JSON loading fails: block only the always-redundant set
|
|
29
31
|
knownDuplicates = new Set(['left-pad', 'is-odd', 'mkdirp', 'rimraf', 'node-fetch', 'underscore']);
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
process.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const isAntigravity = !!data.toolCall;
|
|
39
|
-
const toolName = isAntigravity ? data.toolCall.name : (data.tool_name || data.toolName || '');
|
|
40
|
-
const toolInput = isAntigravity ? data.toolCall.args : (data.tool_input || data.toolInput || {});
|
|
41
|
-
let added = [];
|
|
42
|
-
|
|
43
|
-
const isTerminal = ['Bash', 'run_command', 'run_shell_command', 'terminal', 'execute_command'].includes(toolName);
|
|
44
|
-
const isFileEdit = ['Edit', 'Write', 'replace_file_content', 'write_to_file', 'write_file', 'multi_replace_file_content'].includes(toolName);
|
|
45
|
-
|
|
46
|
-
if (isTerminal) {
|
|
47
|
-
const command = toolInput.command || toolInput.CommandLine || toolInput.cmd || toolInput.commandLine || '';
|
|
48
|
-
|
|
49
|
-
// Hard-block git commit/push unless explicitly allowed
|
|
50
|
-
if (isGitCommitOrPush(command)) {
|
|
51
|
-
const reason = '[ASC Hard-Block] git commit/push detected. Never run git commit, git push, or git push --force unless the user explicitly requests it this turn.';
|
|
52
|
-
let output;
|
|
53
|
-
if (isAntigravity) {
|
|
54
|
-
output = { decision: "deny", reason: reason };
|
|
55
|
-
} else {
|
|
56
|
-
output = {
|
|
57
|
-
allow_tool: false,
|
|
58
|
-
deny_reason: reason,
|
|
59
|
-
hookSpecificOutput: {
|
|
60
|
-
hookEventName: 'PreToolUse',
|
|
61
|
-
permissionDecision: 'deny',
|
|
62
|
-
permissionDecisionReason: reason
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
process.stdout.write(JSON.stringify(output) + '\n');
|
|
67
|
-
process.exit(2);
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
34
|
+
function emitAllow(isAntigravity) {
|
|
35
|
+
if (isAntigravity) {
|
|
36
|
+
process.stdout.write(JSON.stringify({ decision: 'allow' }) + '\n');
|
|
37
|
+
}
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
70
40
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
} else {
|
|
81
|
-
output = {
|
|
82
|
-
allow_tool: false,
|
|
83
|
-
deny_reason: reason,
|
|
84
|
-
hookSpecificOutput: {
|
|
85
|
-
hookEventName: 'PreToolUse',
|
|
86
|
-
permissionDecision: 'deny',
|
|
87
|
-
permissionDecisionReason: reason
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
process.stdout.write(JSON.stringify(output) + '\n');
|
|
92
|
-
process.exit(2);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
41
|
+
function emitGateDecision(antigravityDecision, reason, isAntigravity) {
|
|
42
|
+
if (isAntigravity) {
|
|
43
|
+
process.stdout.write(JSON.stringify({
|
|
44
|
+
decision: antigravityDecision,
|
|
45
|
+
reason: reason,
|
|
46
|
+
}) + '\n');
|
|
47
|
+
process.exit(0);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
95
50
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
51
|
+
const output = {
|
|
52
|
+
allow_tool: false,
|
|
53
|
+
deny_reason: reason,
|
|
54
|
+
hookSpecificOutput: {
|
|
55
|
+
hookEventName: 'PreToolUse',
|
|
56
|
+
permissionDecision: 'deny',
|
|
57
|
+
permissionDecisionReason: reason,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
process.stdout.write(JSON.stringify(output) + '\n');
|
|
61
|
+
process.exit(2);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function handlePayload(data) {
|
|
65
|
+
const isAntigravity = isAntigravityPayload(data);
|
|
66
|
+
if (!data || typeof data !== 'object') {
|
|
67
|
+
emitAllow(isAntigravity);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const workspaceRoot = resolveWorkspaceRoot(data);
|
|
72
|
+
const toolName = data.toolCall ? (data.toolCall.name || '') : (data.tool_name || data.toolName || '');
|
|
73
|
+
const toolInput = data.toolCall ? (data.toolCall.args || {}) : (data.tool_input || data.toolInput || {});
|
|
74
|
+
let added = [];
|
|
75
|
+
|
|
76
|
+
const isTerminal = ['Bash', 'run_command', 'run_shell_command', 'terminal', 'execute_command'].includes(toolName);
|
|
77
|
+
const normalizedEdit = !isTerminal ? normalizeEditToolCall(toolName, toolInput) : null;
|
|
78
|
+
|
|
79
|
+
if (isTerminal) {
|
|
80
|
+
const command = toolInput.command || toolInput.CommandLine || toolInput.cmd || toolInput.commandLine || '';
|
|
118
81
|
|
|
119
|
-
if (
|
|
120
|
-
|
|
82
|
+
if (isGitCommitOrPush(command)) {
|
|
83
|
+
const reason = isAntigravity
|
|
84
|
+
? '[ASC Gate] Confirm git commit/push was explicitly requested by the user this turn.'
|
|
85
|
+
: '[ASC Hard-Block] git commit/push detected. Never run git commit, git push, or git push --force unless the user explicitly requests it this turn.';
|
|
86
|
+
emitGateDecision('force_ask', reason, isAntigravity);
|
|
121
87
|
return;
|
|
122
88
|
}
|
|
123
89
|
|
|
124
|
-
const allowlist = loadAllowlist();
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
return knownDuplicates.has(dep) || reviewDuplicates.has(dep);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
const blocked = forbidden.filter(function (dep) {
|
|
133
|
-
return knownDuplicates.has(dep);
|
|
134
|
-
});
|
|
135
|
-
const reviewOnly = forbidden.filter(function (dep) {
|
|
136
|
-
return !knownDuplicates.has(dep) && reviewDuplicates.has(dep);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
if (blocked.length > 0) {
|
|
140
|
-
const reason = '[ASC Hard-Block] Dependency ' + blocked.map(function (d) { return "'" + d + "'"; }).join(', ')
|
|
141
|
-
+ ' duplicates standard library or native platform features. '
|
|
142
|
-
+ 'Ladder step 3: use stdlib/native features instead, or add to .asc/dependency-allowlist.json to override.';
|
|
143
|
-
|
|
144
|
-
let output;
|
|
145
|
-
if (isAntigravity) {
|
|
146
|
-
output = {
|
|
147
|
-
decision: "deny",
|
|
148
|
-
reason: reason
|
|
149
|
-
};
|
|
150
|
-
} else {
|
|
151
|
-
output = {
|
|
152
|
-
allow_tool: false,
|
|
153
|
-
deny_reason: reason,
|
|
154
|
-
hookSpecificOutput: {
|
|
155
|
-
hookEventName: 'PreToolUse',
|
|
156
|
-
permissionDecision: 'deny',
|
|
157
|
-
permissionDecisionReason: reason
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
process.stdout.write(JSON.stringify(output) + '\n');
|
|
162
|
-
process.exit(2);
|
|
90
|
+
const allowlist = loadAllowlist(workspaceRoot);
|
|
91
|
+
|
|
92
|
+
const cloneCheck = checkGitClone(command, allowlist);
|
|
93
|
+
if (cloneCheck && cloneCheck.blocked) {
|
|
94
|
+
emitGateDecision('deny', cloneCheck.reason, isAntigravity);
|
|
163
95
|
return;
|
|
164
96
|
}
|
|
165
97
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const reason = '[ASC Review] Dependency ' + reviewOnly.join(', ')
|
|
174
|
-
+ ' may duplicate stdlib/native features. ' + reasons
|
|
175
|
-
+ ' If the need is real, add it to .asc/dependency-allowlist.json and retry.';
|
|
176
|
-
|
|
177
|
-
let output;
|
|
178
|
-
if (isAntigravity) {
|
|
179
|
-
output = {
|
|
180
|
-
decision: "deny",
|
|
181
|
-
reason: reason
|
|
182
|
-
};
|
|
183
|
-
} else {
|
|
184
|
-
output = {
|
|
185
|
-
allow_tool: false,
|
|
186
|
-
deny_reason: reason,
|
|
187
|
-
hookSpecificOutput: {
|
|
188
|
-
hookEventName: 'PreToolUse',
|
|
189
|
-
permissionDecision: 'deny',
|
|
190
|
-
permissionDecisionReason: reason
|
|
191
|
-
}
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
process.stdout.write(JSON.stringify(output) + '\n');
|
|
195
|
-
process.exit(2);
|
|
98
|
+
added = extractCommandDeps(command);
|
|
99
|
+
} else if (normalizedEdit) {
|
|
100
|
+
const filePath = normalizedEdit.filePath || '';
|
|
101
|
+
const manifestFiles = ['package.json', 'requirements.txt', 'pyproject.toml', 'go.mod', 'Cargo.toml', 'Gemfile'];
|
|
102
|
+
const isManifest = manifestFiles.some(function (m) { return filePath.endsWith(m); });
|
|
103
|
+
if (!isManifest) {
|
|
104
|
+
emitAllow(isAntigravity);
|
|
196
105
|
return;
|
|
197
106
|
}
|
|
107
|
+
|
|
108
|
+
const depPattern = /"([^"]+)"\s*:\s*"[~^>=<*]?\d/g;
|
|
109
|
+
const newDeps = extractDeps(normalizedEdit.newContent, depPattern);
|
|
110
|
+
const oldDeps = extractDeps(normalizedEdit.oldContent, depPattern);
|
|
111
|
+
|
|
112
|
+
added = newDeps.filter(function (d) { return oldDeps.indexOf(d) === -1; });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (added.length === 0) {
|
|
116
|
+
emitAllow(isAntigravity);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const allowlist = loadAllowlist(workspaceRoot);
|
|
121
|
+
const notAllowed = added.filter(function (dep) {
|
|
122
|
+
return !allowlist.has(dep);
|
|
123
|
+
});
|
|
124
|
+
const forbidden = notAllowed.filter(function (dep) {
|
|
125
|
+
return knownDuplicates.has(dep) || reviewDuplicates.has(dep);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const blocked = forbidden.filter(function (dep) {
|
|
129
|
+
return knownDuplicates.has(dep);
|
|
130
|
+
});
|
|
131
|
+
const reviewOnly = forbidden.filter(function (dep) {
|
|
132
|
+
return !knownDuplicates.has(dep) && reviewDuplicates.has(dep);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
if (blocked.length > 0) {
|
|
136
|
+
const reason = '[ASC Hard-Block] Dependency ' + blocked.map(function (d) { return "'" + d + "'"; }).join(', ')
|
|
137
|
+
+ ' duplicates standard library or native platform features. '
|
|
138
|
+
+ 'Ladder step 3: use stdlib/native features instead, or add to .asc/dependency-allowlist.json to override.';
|
|
139
|
+
emitGateDecision('deny', reason, isAntigravity);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (reviewOnly.length > 0) {
|
|
144
|
+
const reasons = reviewOnly.map(function (dep) {
|
|
145
|
+
return "'" + dep + "': " + (reviewReasons[dep] || 'native alternative exists; confirm the package-specific need');
|
|
146
|
+
}).join(' ');
|
|
147
|
+
const reason = '[ASC Review] Dependency ' + reviewOnly.join(', ')
|
|
148
|
+
+ ' may duplicate stdlib/native features. ' + reasons
|
|
149
|
+
+ ' If the need is real, add it to .asc/dependency-allowlist.json and retry.';
|
|
150
|
+
emitGateDecision('deny', reason, isAntigravity);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
emitAllow(isAntigravity);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (process.stdin.isTTY) {
|
|
158
|
+
process.exit(0);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let handled = false;
|
|
162
|
+
let inputBuffer = '';
|
|
163
|
+
const stdinTimer = setTimeout(function () {
|
|
164
|
+
if (!handled) {
|
|
165
|
+
handled = true;
|
|
166
|
+
process.exit(0);
|
|
167
|
+
}
|
|
168
|
+
}, 3000);
|
|
169
|
+
|
|
170
|
+
process.stdin.setEncoding('utf8');
|
|
171
|
+
process.stdin.on('data', function (chunk) {
|
|
172
|
+
inputBuffer += chunk;
|
|
173
|
+
try {
|
|
174
|
+
const data = JSON.parse(inputBuffer);
|
|
175
|
+
if (!handled) {
|
|
176
|
+
handled = true;
|
|
177
|
+
clearTimeout(stdinTimer);
|
|
178
|
+
handlePayload(data);
|
|
179
|
+
}
|
|
180
|
+
} catch (_) {
|
|
181
|
+
// Wait for remaining chunks
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
process.stdin.on('end', function () {
|
|
186
|
+
if (handled) return;
|
|
187
|
+
handled = true;
|
|
188
|
+
clearTimeout(stdinTimer);
|
|
189
|
+
if (!inputBuffer.trim()) {
|
|
190
|
+
process.exit(0);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
handlePayload(JSON.parse(inputBuffer));
|
|
195
|
+
} catch (_) {
|
|
198
196
|
process.exit(0);
|
|
199
|
-
} catch (e) {
|
|
200
|
-
// incomplete json, wait for more chunks
|
|
201
197
|
}
|
|
202
198
|
});
|
|
203
199
|
|
|
204
200
|
function isGitCommitOrPush(command) {
|
|
205
201
|
if (!command || typeof command !== 'string') return false;
|
|
206
202
|
|
|
207
|
-
// Split multi-command strings (chained by &&, ||, ;, \n, |)
|
|
208
203
|
const subcommands = command.split(/[\r\n;&|]+/);
|
|
209
204
|
|
|
210
205
|
for (let i = 0; i < subcommands.length; i++) {
|
|
211
206
|
const sub = subcommands[i].trim();
|
|
212
207
|
if (!sub || !/\b(?:git|git\.exe)\b/i.test(sub)) continue;
|
|
213
208
|
|
|
214
|
-
// Tokenize sub-command handling quotes
|
|
215
209
|
const tokens = sub.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || [];
|
|
216
210
|
let gitIdx = -1;
|
|
217
211
|
|
|
@@ -225,21 +219,17 @@ function isGitCommitOrPush(command) {
|
|
|
225
219
|
|
|
226
220
|
if (gitIdx === -1) continue;
|
|
227
221
|
|
|
228
|
-
// Parse tokens after 'git' to find the git subcommand
|
|
229
222
|
for (let k = gitIdx + 1; k < tokens.length; k++) {
|
|
230
223
|
let tok = tokens[k].replace(/^['"]|['"]$/g, '');
|
|
231
224
|
if (!tok) continue;
|
|
232
225
|
|
|
233
|
-
// If token is a flag (-C, --git-dir, -c, --no-pager, etc.)
|
|
234
226
|
if (tok.startsWith('-')) {
|
|
235
|
-
// Skip standalone argument for options expecting a value after whitespace
|
|
236
227
|
if (['-C', '-c', '--git-dir', '--work-tree', '--namespace', '--exec-path'].includes(tok)) {
|
|
237
228
|
k++;
|
|
238
229
|
}
|
|
239
230
|
continue;
|
|
240
231
|
}
|
|
241
232
|
|
|
242
|
-
// First non-flag token after git global options is the git subcommand
|
|
243
233
|
const gitSub = tok.toLowerCase();
|
|
244
234
|
if (gitSub === 'commit' || gitSub === 'push' || gitSub === 'commit-tree') {
|
|
245
235
|
return true;
|
|
@@ -255,14 +245,12 @@ function isGitCommitOrPush(command) {
|
|
|
255
245
|
function checkGitClone(command, allowlist) {
|
|
256
246
|
if (!command || typeof command !== 'string') return null;
|
|
257
247
|
|
|
258
|
-
// Split multi-command strings (chained by &&, ||, ;, \n, |)
|
|
259
248
|
const subcommands = command.split(/[\r\n;&|]+/);
|
|
260
249
|
|
|
261
250
|
for (let i = 0; i < subcommands.length; i++) {
|
|
262
251
|
const sub = subcommands[i].trim();
|
|
263
252
|
if (!sub || !/\b(?:git|git\.exe)\b/i.test(sub)) continue;
|
|
264
253
|
|
|
265
|
-
// Tokenize sub-command handling quotes
|
|
266
254
|
const tokens = sub.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || [];
|
|
267
255
|
let gitIdx = -1;
|
|
268
256
|
|
|
@@ -276,7 +264,6 @@ function checkGitClone(command, allowlist) {
|
|
|
276
264
|
|
|
277
265
|
if (gitIdx === -1) continue;
|
|
278
266
|
|
|
279
|
-
// Parse tokens after 'git' to find the git subcommand
|
|
280
267
|
let isClone = false;
|
|
281
268
|
let nextIdx = gitIdx + 1;
|
|
282
269
|
for (; nextIdx < tokens.length; nextIdx++) {
|
|
@@ -299,14 +286,12 @@ function checkGitClone(command, allowlist) {
|
|
|
299
286
|
|
|
300
287
|
if (!isClone) continue;
|
|
301
288
|
|
|
302
|
-
// Find the target repository token (skip clone options like --depth, -b, --branch, etc.)
|
|
303
289
|
let repoArg = '';
|
|
304
290
|
for (let k = nextIdx; k < tokens.length; k++) {
|
|
305
291
|
let tok = tokens[k].replace(/^['"]|['"]$/g, '');
|
|
306
292
|
if (!tok) continue;
|
|
307
293
|
|
|
308
294
|
if (tok.startsWith('-')) {
|
|
309
|
-
// Flags that take an argument after whitespace
|
|
310
295
|
if (['-b', '--branch', '--depth', '--origin', '-o', '--reference', '--filter', '-u', '--upload-pack', '--template'].includes(tok)) {
|
|
311
296
|
k++;
|
|
312
297
|
}
|
|
@@ -319,19 +304,16 @@ function checkGitClone(command, allowlist) {
|
|
|
319
304
|
|
|
320
305
|
if (!repoArg) continue;
|
|
321
306
|
|
|
322
|
-
// 1. Block insecure protocols (git://, http://)
|
|
323
307
|
if (repoArg.startsWith('git://') || repoArg.startsWith('http://')) {
|
|
324
308
|
return {
|
|
325
309
|
blocked: true,
|
|
326
310
|
reason: '[ASC Hard-Block] Insecure git clone protocol detected (' + repoArg + '). '
|
|
327
|
-
+ 'Use secure HTTPS or SSH (git@) with verified TLS.'
|
|
311
|
+
+ 'Use secure HTTPS or SSH (git@) with verified TLS.',
|
|
328
312
|
};
|
|
329
313
|
}
|
|
330
314
|
|
|
331
|
-
// 2. Allow checking against allowlist (exact URL, owner/repo, or repo name)
|
|
332
315
|
const normalized = repoArg.toLowerCase().replace(/\.git$/, '');
|
|
333
316
|
|
|
334
|
-
// Extract owner/repo if possible (e.g. from https://github.com/owner/repo or git@github.com:owner/repo)
|
|
335
317
|
let ownerRepo = '';
|
|
336
318
|
const ghMatch = repoArg.match(/(?:github\.com|gitlab\.com|bitbucket\.org)[:\/]([^\/\s]+\/[^\/\s#?]+)/i);
|
|
337
319
|
if (ghMatch) {
|
|
@@ -348,7 +330,7 @@ function checkGitClone(command, allowlist) {
|
|
|
348
330
|
blocked: true,
|
|
349
331
|
reason: '[ASC Hard-Block] Unverified git clone target: \'' + repoArg + '\'. '
|
|
350
332
|
+ 'Verify repository provenance against trusted sources (mitigate HalluSquatting) '
|
|
351
|
-
+ 'or add to allowedRepositories in .asc/dependency-allowlist.json to allow.'
|
|
333
|
+
+ 'or add to allowedRepositories in .asc/dependency-allowlist.json to allow.',
|
|
352
334
|
};
|
|
353
335
|
}
|
|
354
336
|
}
|
|
@@ -366,11 +348,6 @@ function extractDeps(text, pattern) {
|
|
|
366
348
|
}
|
|
367
349
|
|
|
368
350
|
function extractCommandDeps(command) {
|
|
369
|
-
// JS: npm/yarn/pnpm/bun/ascx install/add
|
|
370
|
-
// Python: pip/pip3/uv install, poetry add
|
|
371
|
-
// Go: go get
|
|
372
|
-
// Rust: cargo add
|
|
373
|
-
// Ruby: gem install, bundle add
|
|
374
351
|
const installRegex = /(?:npm|yarn|pnpm|bun|ascx|pip3?|uv|poetry|cargo|gem|bundle)\s+(?:install|i|add|get)(?:\s+[^\s]+)*/i;
|
|
375
352
|
if (!installRegex.test(command)) return [];
|
|
376
353
|
|
|
@@ -380,11 +357,10 @@ function extractCommandDeps(command) {
|
|
|
380
357
|
|
|
381
358
|
const rawArgs = parts.slice(pkgIndex + 1);
|
|
382
359
|
const deps = [];
|
|
383
|
-
for (
|
|
384
|
-
if (arg.startsWith('-')) continue;
|
|
360
|
+
for (const arg of rawArgs) {
|
|
361
|
+
if (arg.startsWith('-')) continue;
|
|
385
362
|
if (arg.startsWith('http://') || arg.startsWith('https://') || arg.startsWith('git+')) continue;
|
|
386
363
|
|
|
387
|
-
// Remove scope/version if any, e.g. lodash@^4.17 -> lodash, @types/node@18 -> @types/node
|
|
388
364
|
let name = arg;
|
|
389
365
|
if (name.startsWith('@')) {
|
|
390
366
|
const slashIndex = name.indexOf('/');
|
|
@@ -401,12 +377,13 @@ function extractCommandDeps(command) {
|
|
|
401
377
|
return deps;
|
|
402
378
|
}
|
|
403
379
|
|
|
404
|
-
function loadAllowlist() {
|
|
380
|
+
function loadAllowlist(workspaceRoot) {
|
|
381
|
+
const root = workspaceRoot || process.cwd();
|
|
405
382
|
const allowed = new Set();
|
|
406
383
|
allowed.repositories = new Set();
|
|
407
384
|
const candidates = [
|
|
408
|
-
path.join(
|
|
409
|
-
path.join(
|
|
385
|
+
path.join(root, '.asc', 'dependency-allowlist.json'),
|
|
386
|
+
path.join(root, '.agents', 'dependency-allowlist.json'),
|
|
410
387
|
];
|
|
411
388
|
|
|
412
389
|
for (let i = 0; i < candidates.length; i++) {
|
|
@@ -1,45 +1,58 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Agentic Senior Core — PreInvocation
|
|
3
|
-
// Antigravity
|
|
4
|
-
//
|
|
5
|
-
// role session-start.js fills for Claude Code / Codex / Copilot via their
|
|
6
|
-
// real SessionStart hook.
|
|
7
|
-
// Does NOT replace session-start.js — that file still serves other hosts.
|
|
2
|
+
// Agentic Senior Core — Optional SessionStart / PreInvocation pulse helper.
|
|
3
|
+
// Note: Native Antigravity plugins already load rules/agentic-senior-core.md via always_on rules,
|
|
4
|
+
// so this helper safely emits {} unless standalone rule injection is explicitly requested.
|
|
8
5
|
|
|
9
6
|
const fs = require('fs');
|
|
10
7
|
const path = require('path');
|
|
11
8
|
|
|
12
9
|
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || path.resolve(__dirname, '..');
|
|
13
10
|
|
|
11
|
+
if (process.stdin.isTTY) {
|
|
12
|
+
process.stdout.write('{}\n');
|
|
13
|
+
process.exit(0);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let handled = false;
|
|
14
17
|
let inputBuffer = '';
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const agentsPath = path.join(pluginRoot, 'rules', 'agentic-senior-core.md');
|
|
29
|
-
let content;
|
|
30
|
-
try {
|
|
31
|
-
content = fs.readFileSync(agentsPath, 'utf8');
|
|
32
|
-
} catch (_) {
|
|
33
|
-
process.stdout.write(JSON.stringify({}) + '\n');
|
|
34
|
-
process.exit(0);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
process.stdout.write(JSON.stringify({
|
|
39
|
-
injectSteps: [{ ephemeralMessage: content }],
|
|
40
|
-
}) + '\n');
|
|
41
|
-
process.exit(0);
|
|
42
|
-
} catch (e) {
|
|
43
|
-
// wait for more chunks
|
|
18
|
+
|
|
19
|
+
function finish(data) {
|
|
20
|
+
if (handled) return;
|
|
21
|
+
handled = true;
|
|
22
|
+
clearTimeout(timer);
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
if (!data || data.invocationNum !== 0 || !process.env.ASC_STANDALONE_PULSE) {
|
|
26
|
+
process.stdout.write('{}\n');
|
|
27
|
+
process.exit(0);
|
|
28
|
+
return;
|
|
44
29
|
}
|
|
30
|
+
|
|
31
|
+
const agentsPath = path.join(pluginRoot, 'rules', 'agentic-senior-core.md');
|
|
32
|
+
const content = fs.readFileSync(agentsPath, 'utf8');
|
|
33
|
+
process.stdout.write(JSON.stringify({
|
|
34
|
+
injectSteps: [{ ephemeralMessage: content }],
|
|
35
|
+
}) + '\n');
|
|
36
|
+
} catch (_) {
|
|
37
|
+
process.stdout.write('{}\n');
|
|
38
|
+
}
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const timer = setTimeout(function () { finish(null); }, 2000);
|
|
43
|
+
|
|
44
|
+
process.stdin.setEncoding('utf8');
|
|
45
|
+
process.stdin.on('data', function (chunk) {
|
|
46
|
+
inputBuffer += chunk;
|
|
47
|
+
try {
|
|
48
|
+
finish(JSON.parse(inputBuffer));
|
|
49
|
+
} catch (_) {}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
process.stdin.on('end', function () {
|
|
53
|
+
let parsed = null;
|
|
54
|
+
if (inputBuffer.trim()) {
|
|
55
|
+
try { parsed = JSON.parse(inputBuffer); } catch (_) {}
|
|
56
|
+
}
|
|
57
|
+
finish(parsed);
|
|
45
58
|
});
|