@ryuenn3123/agentic-senior-core 6.16.2 → 6.16.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/.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/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 +20 -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 +6 -1
- package/.agents/plugins/agentic-senior-core/skills/asc/SKILL.md +5 -2
- package/.agents/plugins/agentic-senior-core/skills/asc-develop/SKILL.md +3 -3
- 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 +2 -2
- package/.agents/plugins/agentic-senior-core/skills/asc-test/SKILL.md +8 -1
- 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-ui/SKILL.md +17 -14
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/copy-i18n.md +56 -8
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/craft/color-surfaces.md +25 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/craft/themes-catalog.md +198 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/divergence.md +34 -2
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/modes/genre-calibration.md +89 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/modes/operate.md +26 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/modes/persuade.md +10 -9
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/redesign-audit.md +4 -9
- 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 +18 -9
- package/package.json +1 -1
- package/plugin.yaml +3 -1
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Run the adversarial penetration testing, DAST, and API security verification standards from skills/asc-pentest/SKILL.md. Execute non-destructive reconnaissance, dynamic request fuzzing, BOLA/IDOR dual-identity verification, and generate deterministic proof-of-exploit ledgers with safe remediation.
|
|
2
|
+
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
description = "Adversarial penetration testing, DAST, API security fuzzing, and BOLA/IDOR verification"
|
|
2
|
+
prompt = "Load and follow the asc-pentest skill. Execute defensive penetration testing against local/staging targets using the Pentesting Task Tree, run DAST baseline scans, probe BOLA/IDOR with dual-identity replay, fuzz input boundaries safely, and output deterministic proof-of-exploit curl ledgers."
|
|
3
|
+
|
|
@@ -14,6 +14,11 @@ const {
|
|
|
14
14
|
LOC_DELTA_THRESHOLD,
|
|
15
15
|
NEW_FILE_LINE_THRESHOLD,
|
|
16
16
|
} = require('./constants.cjs');
|
|
17
|
+
const {
|
|
18
|
+
isAntigravityPayload,
|
|
19
|
+
resolveWorkspaceRoot,
|
|
20
|
+
normalizeEditToolCall,
|
|
21
|
+
} = require('./path-util.cjs');
|
|
17
22
|
|
|
18
23
|
const JSCPD_TIMEOUT_MS = 10000;
|
|
19
24
|
|
|
@@ -77,133 +82,136 @@ function isFrameworkConventional(basename) {
|
|
|
77
82
|
return FRAMEWORK_CONVENTIONAL_BASENAMES.has(basename) || hasConventionalSuffix(basename);
|
|
78
83
|
}
|
|
79
84
|
|
|
85
|
+
let handled = false;
|
|
80
86
|
let inputBuffer = '';
|
|
81
|
-
process.stdin.setEncoding('utf8');
|
|
82
|
-
process.stdin.on('data', chunk => {
|
|
83
|
-
inputBuffer += chunk;
|
|
84
|
-
try {
|
|
85
|
-
const data = JSON.parse(inputBuffer);
|
|
86
87
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (data.toolCall) {
|
|
92
|
-
// Antigravity PreToolUse shape (has toolCall directly)
|
|
93
|
-
isAntigravity = true;
|
|
94
|
-
toolName = data.toolCall.name;
|
|
95
|
-
toolInput = data.toolCall.args || {};
|
|
96
|
-
} else if (data.stepIdx !== undefined && data.transcriptPath) {
|
|
97
|
-
// Antigravity PostToolUse shape — no toolCall in payload,
|
|
98
|
-
// read the tool call details from the transcript at stepIdx
|
|
99
|
-
isAntigravity = true;
|
|
100
|
-
var extracted = extractToolCallFromTranscript(data.transcriptPath, data.stepIdx);
|
|
101
|
-
if (!extracted) { process.exit(0); return; }
|
|
102
|
-
toolName = extracted.toolName;
|
|
103
|
-
toolInput = extracted.toolInput;
|
|
104
|
-
} else {
|
|
105
|
-
// Claude Code / Codex / Copilot path
|
|
106
|
-
toolName = data.tool_name || data.toolName || '';
|
|
107
|
-
toolInput = data.tool_input || data.toolInput || {};
|
|
108
|
-
}
|
|
88
|
+
function handleDedupPayload(data) {
|
|
89
|
+
if (handled) return;
|
|
90
|
+
handled = true;
|
|
109
91
|
|
|
110
|
-
|
|
111
|
-
|
|
92
|
+
const isAntigravity = isAntigravityPayload(data);
|
|
93
|
+
if (isAntigravity && data && data.stepIdx !== undefined) {
|
|
94
|
+
// Antigravity PostToolUse strictly expects {} on stdout
|
|
95
|
+
process.stdout.write('{}\n');
|
|
96
|
+
process.exit(0);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
112
99
|
|
|
113
|
-
|
|
114
|
-
|
|
100
|
+
const workspaceRoot = resolveWorkspaceRoot(data);
|
|
101
|
+
let rawTool = '';
|
|
102
|
+
let rawArgs = {};
|
|
115
103
|
|
|
116
|
-
|
|
117
|
-
|
|
104
|
+
if (data && data.toolCall) {
|
|
105
|
+
rawTool = data.toolCall.name || '';
|
|
106
|
+
rawArgs = data.toolCall.args || {};
|
|
107
|
+
} else if (data) {
|
|
108
|
+
rawTool = data.tool_name || data.toolName || '';
|
|
109
|
+
rawArgs = data.tool_input || data.toolInput || {};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const norm = normalizeEditToolCall(rawTool, rawArgs);
|
|
113
|
+
if (!norm || !norm.filePath) {
|
|
114
|
+
if (isAntigravity) process.stdout.write('{}\n');
|
|
115
|
+
process.exit(0);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
const resolvedFile = path.isAbsolute(norm.filePath)
|
|
120
|
+
? norm.filePath
|
|
121
|
+
: path.resolve(workspaceRoot, norm.filePath);
|
|
122
|
+
const ext = path.extname(resolvedFile).slice(1);
|
|
123
|
+
if (!SOURCE_EXTENSIONS.has(ext)) {
|
|
124
|
+
if (isAntigravity) process.stdout.write('{}\n');
|
|
125
|
+
process.exit(0);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
120
128
|
|
|
121
|
-
|
|
122
|
-
|
|
129
|
+
const config = loadDedupConfig(workspaceRoot);
|
|
130
|
+
const mappedInput = norm.canonicalTool === 'Edit'
|
|
131
|
+
? { new_string: norm.newContent, old_string: norm.oldContent }
|
|
132
|
+
: { content: norm.newContent };
|
|
123
133
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
134
|
+
if (!isQualifyingEdit(norm.canonicalTool, mappedInput, config)) {
|
|
135
|
+
if (isAntigravity) process.stdout.write('{}\n');
|
|
136
|
+
process.exit(0);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
128
139
|
|
|
129
|
-
|
|
130
|
-
if (
|
|
140
|
+
if (hasValidInlineIgnore(resolvedFile)) {
|
|
141
|
+
if (isAntigravity) process.stdout.write('{}\n');
|
|
142
|
+
process.exit(0);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
131
145
|
|
|
132
|
-
|
|
133
|
-
|
|
146
|
+
const scanDir = resolveScanDir(resolvedFile, config, workspaceRoot);
|
|
147
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'asc-dedup-'));
|
|
134
148
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
149
|
+
const ignoreFlags = (config.ignoreDirs || []).map(function (d) { return '--ignore "' + d + '"'; }).join(' ');
|
|
150
|
+
const minTokens = config.minTokens || 30;
|
|
151
|
+
const scanCmd = ' "' + scanDir + '" --min-tokens ' + minTokens
|
|
152
|
+
+ ' --reporters json --silent --output "' + tmpDir + '" ' + ignoreFlags;
|
|
139
153
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
154
|
+
const report = runJscpdScan(scanCmd, tmpDir, workspaceRoot);
|
|
155
|
+
if (!report) {
|
|
156
|
+
cleanup(tmpDir);
|
|
157
|
+
if (isAntigravity) process.stdout.write('{}\n');
|
|
158
|
+
process.exit(0);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
145
161
|
|
|
162
|
+
const finding = checkForDuplicates(report, resolvedFile, config, workspaceRoot);
|
|
163
|
+
if (!finding) {
|
|
146
164
|
cleanup(tmpDir);
|
|
165
|
+
if (isAntigravity) process.stdout.write('{}\n');
|
|
147
166
|
process.exit(0);
|
|
148
|
-
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const nudge = '[ASC Dedup] ' + path.basename(resolvedFile) + ' looks similar to '
|
|
171
|
+
+ finding.matchedFile + ' (' + finding.percent + '% overlap, '
|
|
172
|
+
+ finding.lines + ' lines). Ladder step 2: does the codebase already have this? '
|
|
173
|
+
+ 'Reuse it, or confirm this is intentional.';
|
|
174
|
+
|
|
175
|
+
cleanup(tmpDir);
|
|
176
|
+
if (config.mode === 'block') {
|
|
177
|
+
emitDeny(nudge, isAntigravity);
|
|
178
|
+
} else {
|
|
179
|
+
emitAdvisory(nudge, isAntigravity);
|
|
180
|
+
}
|
|
181
|
+
process.exit(0);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (process.stdin.isTTY) {
|
|
185
|
+
process.exit(0);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
process.stdin.setEncoding('utf8');
|
|
189
|
+
process.stdin.on('data', chunk => {
|
|
190
|
+
inputBuffer += chunk;
|
|
191
|
+
try {
|
|
192
|
+
const data = JSON.parse(inputBuffer);
|
|
193
|
+
handleDedupPayload(data);
|
|
194
|
+
} catch (_) {
|
|
149
195
|
// incomplete json, wait for more chunks
|
|
150
196
|
}
|
|
151
197
|
});
|
|
152
198
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
199
|
+
process.stdin.on('end', () => {
|
|
200
|
+
if (handled) return;
|
|
201
|
+
if (!inputBuffer.trim()) {
|
|
202
|
+
process.exit(0);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
157
205
|
try {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
// Find the step with matching step_index that contains tool_calls
|
|
162
|
-
for (var i = lines.length - 1; i >= 0; i--) {
|
|
163
|
-
try {
|
|
164
|
-
var step = JSON.parse(lines[i]);
|
|
165
|
-
if (step.step_index !== stepIdx || !step.tool_calls) continue;
|
|
166
|
-
|
|
167
|
-
for (var j = 0; j < step.tool_calls.length; j++) {
|
|
168
|
-
var tc = step.tool_calls[j];
|
|
169
|
-
if (tc.name === 'replace_file_content' || tc.name === 'multi_replace_file_content') {
|
|
170
|
-
return {
|
|
171
|
-
toolName: 'Edit',
|
|
172
|
-
toolInput: {
|
|
173
|
-
file_path: tc.args.TargetFile || '',
|
|
174
|
-
new_string: tc.args.ReplacementContent || '',
|
|
175
|
-
old_string: tc.args.TargetContent || '',
|
|
176
|
-
},
|
|
177
|
-
};
|
|
178
|
-
} else if (tc.name === 'write_to_file') {
|
|
179
|
-
return {
|
|
180
|
-
toolName: 'Write',
|
|
181
|
-
toolInput: {
|
|
182
|
-
file_path: tc.args.TargetFile || '',
|
|
183
|
-
content: tc.args.CodeContent || '',
|
|
184
|
-
},
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
} catch (lineErr) {
|
|
189
|
-
if (process.env.ASC_DEBUG) {
|
|
190
|
-
console.error('[ASC Debug] Transcript line parse failed line ' + i + ':', lineErr.message);
|
|
191
|
-
}
|
|
192
|
-
// Skip malformed line and keep scanning
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
return null;
|
|
196
|
-
} catch (err) {
|
|
197
|
-
if (process.env.ASC_DEBUG) {
|
|
198
|
-
console.error('[ASC Debug] extractToolCallFromTranscript failed:', err.message);
|
|
199
|
-
}
|
|
200
|
-
return null;
|
|
206
|
+
handleDedupPayload(JSON.parse(inputBuffer));
|
|
207
|
+
} catch (_) {
|
|
208
|
+
process.exit(0);
|
|
201
209
|
}
|
|
202
|
-
}
|
|
210
|
+
});
|
|
203
211
|
|
|
204
212
|
function isQualifyingEdit(toolName, toolInput, config) {
|
|
205
213
|
var isWrite = ['Write', 'write_to_file', 'write_file'].indexOf(toolName) !== -1;
|
|
206
|
-
var isEdit = ['Edit', 'replace_file_content', 'multi_replace_file_content'].indexOf(toolName) !== -1;
|
|
214
|
+
var isEdit = ['Edit', 'edit_file', 'replace_file_content', 'multi_replace_file_content'].indexOf(toolName) !== -1;
|
|
207
215
|
|
|
208
216
|
var newFileThreshold = (config && typeof config.NEW_FILE_LINE_THRESHOLD === 'number')
|
|
209
217
|
? config.NEW_FILE_LINE_THRESHOLD
|
|
@@ -225,8 +233,8 @@ function isQualifyingEdit(toolName, toolInput, config) {
|
|
|
225
233
|
return false;
|
|
226
234
|
}
|
|
227
235
|
|
|
228
|
-
function resolveScanDir(filePath, config) {
|
|
229
|
-
var cwd = process.cwd();
|
|
236
|
+
function resolveScanDir(filePath, config, workspaceRoot) {
|
|
237
|
+
var cwd = workspaceRoot || process.cwd();
|
|
230
238
|
|
|
231
239
|
// User override takes priority
|
|
232
240
|
if (config.scanRoot) {
|
|
@@ -248,10 +256,11 @@ function resolveScanDir(filePath, config) {
|
|
|
248
256
|
return cwd;
|
|
249
257
|
}
|
|
250
258
|
|
|
251
|
-
function loadDedupConfig() {
|
|
259
|
+
function loadDedupConfig(workspaceRoot) {
|
|
260
|
+
var root = workspaceRoot || process.cwd();
|
|
252
261
|
var candidates = [
|
|
253
|
-
path.join(
|
|
254
|
-
path.join(
|
|
262
|
+
path.join(root, '.asc', 'dedup-config.json'),
|
|
263
|
+
path.join(root, '.agents', 'dedup-config.json'),
|
|
255
264
|
];
|
|
256
265
|
for (var i = 0; i < candidates.length; i++) {
|
|
257
266
|
try {
|
|
@@ -273,18 +282,20 @@ function loadDedupConfig() {
|
|
|
273
282
|
|
|
274
283
|
// minimal: attempt-then-fallback — try scan directly, fall back on failure.
|
|
275
284
|
// No separate --version probes. Upgrade if jscpd provides a stable JS API.
|
|
276
|
-
function runJscpdScan(scanCmd, tmpDir) {
|
|
277
|
-
var
|
|
285
|
+
function runJscpdScan(scanCmd, tmpDir, workspaceRoot) {
|
|
286
|
+
var cwd = workspaceRoot || process.cwd();
|
|
287
|
+
var binaries = ['bunx jscpd', 'npx --yes jscpd@5', 'npx --yes jscpd'];
|
|
278
288
|
for (var i = 0; i < binaries.length; i++) {
|
|
279
289
|
try {
|
|
280
290
|
execSync(binaries[i] + scanCmd, {
|
|
281
291
|
timeout: JSCPD_TIMEOUT_MS,
|
|
282
292
|
stdio: 'pipe',
|
|
283
|
-
cwd:
|
|
293
|
+
cwd: cwd,
|
|
284
294
|
});
|
|
285
295
|
return loadReport(tmpDir);
|
|
286
296
|
} catch (_) {
|
|
287
|
-
|
|
297
|
+
var partial = loadReport(tmpDir);
|
|
298
|
+
if (partial) return partial;
|
|
288
299
|
}
|
|
289
300
|
}
|
|
290
301
|
return null;
|
|
@@ -333,7 +344,7 @@ function isAllowedDuplicate(fileA, fileB, allowedDuplicates) {
|
|
|
333
344
|
return false;
|
|
334
345
|
}
|
|
335
346
|
|
|
336
|
-
function checkForDuplicates(report, filePath, config) {
|
|
347
|
+
function checkForDuplicates(report, filePath, config, workspaceRoot) {
|
|
337
348
|
var duplicates = report.duplicates || [];
|
|
338
349
|
if (duplicates.length === 0) return null;
|
|
339
350
|
|
|
@@ -354,17 +365,13 @@ function checkForDuplicates(report, filePath, config) {
|
|
|
354
365
|
continue;
|
|
355
366
|
}
|
|
356
367
|
|
|
357
|
-
// Skip framework-conventional filenames in different directories — identical names
|
|
358
|
-
// are mandated by the framework (e.g. Next.js page.tsx, Angular *.component.ts),
|
|
359
|
-
// not copy-paste duplication.
|
|
360
368
|
if (isFrameworkConventional(targetBasename)
|
|
361
369
|
&& isFrameworkConventional(otherBasename)
|
|
362
370
|
&& path.dirname(path.resolve(filePath)) !== path.dirname(path.resolve(otherRaw))) {
|
|
363
371
|
continue;
|
|
364
372
|
}
|
|
365
373
|
|
|
366
|
-
|
|
367
|
-
var cwd = process.cwd();
|
|
374
|
+
var cwd = workspaceRoot || process.cwd();
|
|
368
375
|
var matchedFile = path.relative(cwd, path.resolve(otherRaw)).replace(/\\/g, '/');
|
|
369
376
|
var lines = dup.lines || 0;
|
|
370
377
|
var totalFileLines = 1;
|
|
@@ -375,7 +382,6 @@ function checkForDuplicates(report, filePath, config) {
|
|
|
375
382
|
}
|
|
376
383
|
var percent = Math.round((lines / totalFileLines) * 100);
|
|
377
384
|
|
|
378
|
-
// Skip trivial matches (import boilerplate, small overlaps)
|
|
379
385
|
if (lines < 10 && percent < 10) {
|
|
380
386
|
continue;
|
|
381
387
|
}
|
|
@@ -408,18 +414,21 @@ function emitAdvisory(nudge, isAntigravity) {
|
|
|
408
414
|
|
|
409
415
|
function emitDeny(reason, isAntigravity) {
|
|
410
416
|
if (isAntigravity) {
|
|
411
|
-
process.stdout.write(JSON.stringify({ decision: 'deny', reason: reason }) + '\n');
|
|
412
|
-
} else {
|
|
413
417
|
process.stdout.write(JSON.stringify({
|
|
414
|
-
|
|
415
|
-
deny_reason: reason,
|
|
416
|
-
hookSpecificOutput: {
|
|
417
|
-
hookEventName: 'PostToolUse',
|
|
418
|
-
permissionDecision: 'deny',
|
|
419
|
-
permissionDecisionReason: reason,
|
|
420
|
-
},
|
|
418
|
+
injectSteps: [{ ephemeralMessage: reason }],
|
|
421
419
|
}) + '\n');
|
|
420
|
+
process.exit(0);
|
|
421
|
+
return;
|
|
422
422
|
}
|
|
423
|
+
process.stdout.write(JSON.stringify({
|
|
424
|
+
allow_tool: false,
|
|
425
|
+
deny_reason: reason,
|
|
426
|
+
hookSpecificOutput: {
|
|
427
|
+
hookEventName: 'PostToolUse',
|
|
428
|
+
permissionDecision: 'deny',
|
|
429
|
+
permissionDecisionReason: reason,
|
|
430
|
+
},
|
|
431
|
+
}) + '\n');
|
|
423
432
|
process.exit(2);
|
|
424
433
|
}
|
|
425
434
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "startup|resume|clear|compact",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "node hooks/session-start.js",
|
|
10
|
+
"commandWindows": "node hooks/session-start.js",
|
|
11
|
+
"timeout": 5,
|
|
12
|
+
"statusMessage": "Loading ASC rules..."
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"SubagentStart": [
|
|
18
|
+
{
|
|
19
|
+
"hooks": [
|
|
20
|
+
{
|
|
21
|
+
"type": "command",
|
|
22
|
+
"command": "node hooks/subagent-start.js",
|
|
23
|
+
"commandWindows": "node hooks/subagent-start.js",
|
|
24
|
+
"timeout": 5,
|
|
25
|
+
"statusMessage": "Loading ASC rules..."
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"PreToolUse": [
|
|
31
|
+
{
|
|
32
|
+
"matcher": "Edit|Write|edit_file|replace_file_content|write_to_file|write_file|multi_replace_file_content",
|
|
33
|
+
"hooks": [
|
|
34
|
+
{
|
|
35
|
+
"type": "command",
|
|
36
|
+
"if": "Edit(**/package.json)|Edit(**/requirements.txt)|Edit(**/pyproject.toml)|Edit(**/go.mod)|Edit(**/Cargo.toml)|Edit(**/Gemfile)",
|
|
37
|
+
"command": "node hooks/pre-tool-dependency-gate.js",
|
|
38
|
+
"commandWindows": "node hooks/pre-tool-dependency-gate.js",
|
|
39
|
+
"timeout": 5,
|
|
40
|
+
"statusMessage": "ASC Pre-tool dependency check (Edit)..."
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"type": "command",
|
|
44
|
+
"if": "Write(**/package.json)|Write(**/requirements.txt)|Write(**/pyproject.toml)|Write(**/go.mod)|Write(**/Cargo.toml)|Write(**/Gemfile)",
|
|
45
|
+
"command": "node hooks/pre-tool-dependency-gate.js",
|
|
46
|
+
"commandWindows": "node hooks/pre-tool-dependency-gate.js",
|
|
47
|
+
"timeout": 5,
|
|
48
|
+
"statusMessage": "ASC Pre-tool dependency check (Write)..."
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"matcher": "Bash|run_command|run_shell_command|terminal|execute_command",
|
|
54
|
+
"hooks": [
|
|
55
|
+
{
|
|
56
|
+
"type": "command",
|
|
57
|
+
"command": "node hooks/pre-tool-dependency-gate.js",
|
|
58
|
+
"commandWindows": "node hooks/pre-tool-dependency-gate.js",
|
|
59
|
+
"timeout": 5,
|
|
60
|
+
"statusMessage": "ASC Pre-tool dependency check (Terminal)..."
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
"PostToolUse": [
|
|
66
|
+
{
|
|
67
|
+
"matcher": "Edit|Write|edit_file|replace_file_content|write_to_file|write_file|multi_replace_file_content",
|
|
68
|
+
"hooks": [
|
|
69
|
+
{
|
|
70
|
+
"type": "command",
|
|
71
|
+
"command": "node hooks/post-edit-enforce.js",
|
|
72
|
+
"commandWindows": "node hooks/post-edit-enforce.js",
|
|
73
|
+
"timeout": 5,
|
|
74
|
+
"statusMessage": "ASC ladder & spec gate check..."
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"type": "command",
|
|
78
|
+
"command": "node hooks/dedup-gate.js",
|
|
79
|
+
"commandWindows": "node hooks/dedup-gate.js",
|
|
80
|
+
"timeout": 15,
|
|
81
|
+
"statusMessage": "ASC duplicate-code scan..."
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
"PreCompact": [
|
|
87
|
+
{
|
|
88
|
+
"type": "command",
|
|
89
|
+
"command": "node hooks/pre-compact-pin.js",
|
|
90
|
+
"commandWindows": "node hooks/pre-compact-pin.js",
|
|
91
|
+
"timeout": 5,
|
|
92
|
+
"statusMessage": "ASC constraint pinning..."
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
@@ -1,48 +1,65 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Agentic Senior Core — PreInvocation ladder persistence hook
|
|
3
|
-
// Counters context rot by re-injecting a
|
|
4
|
-
// periodically
|
|
5
|
-
// that sits at the start of the conversation.
|
|
6
|
-
// Antigravity-only: PreInvocation fires before every model call with
|
|
7
|
-
// invocationNum in the input payload. For other hosts, the equivalent
|
|
8
|
-
// pulse is integrated into post-edit-enforce.js via the sourceEditCount counter.
|
|
3
|
+
// Counters context rot by re-injecting a concise decision-ladder + security pin
|
|
4
|
+
// periodically during long Antigravity sessions.
|
|
9
5
|
|
|
10
|
-
const {
|
|
6
|
+
const { getThresholds } = require('./constants.cjs');
|
|
7
|
+
const { resolveWorkspaceRoot } = require('./path-util.cjs');
|
|
11
8
|
|
|
12
|
-
const LADDER_REMINDER = '[ASC] Ladder check: (1) needed? (2) exists already \u2014 reuse? '
|
|
13
|
-
+ '(3) stdlib/native? (4) existing dep? (5) one function? Then minimal code.';
|
|
14
|
-
|
|
15
|
-
// Security constraints are negation-type ("never do X") — most vulnerable to context rot
|
|
16
|
-
// per arXiv:2604.20911. Reinject verbatim alongside ladder pulse.
|
|
17
9
|
const SECURITY_REMINDER = '[ASC SECURITY PIN — verbatim, do not paraphrase] '
|
|
18
10
|
+ 'NEVER: interpolate input into SQL/shell · commit secrets/tokens/credentials '
|
|
19
11
|
+ '· store plaintext passwords · leak stack traces/internals/PII in responses. '
|
|
20
12
|
+ 'ALWAYS: parameterize queries · enforce resource-level authz · rate-limit public endpoints.';
|
|
21
13
|
|
|
14
|
+
if (process.stdin.isTTY) {
|
|
15
|
+
process.stdout.write('{}\n');
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let handled = false;
|
|
22
20
|
let inputBuffer = '';
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
process.exit(0);
|
|
45
|
-
} catch (e) {
|
|
46
|
-
// wait for more chunks
|
|
21
|
+
|
|
22
|
+
function finish(payload) {
|
|
23
|
+
if (handled) return;
|
|
24
|
+
handled = true;
|
|
25
|
+
clearTimeout(timer);
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const workspaceRoot = resolveWorkspaceRoot(payload);
|
|
29
|
+
const thresholds = getThresholds(workspaceRoot);
|
|
30
|
+
const interval = thresholds.LADDER_PULSE_INTERVAL || 3;
|
|
31
|
+
const invNum = payload && typeof payload.invocationNum === 'number' ? payload.invocationNum : 0;
|
|
32
|
+
|
|
33
|
+
if (invNum > 0 && invNum % interval === 0) {
|
|
34
|
+
process.stdout.write(JSON.stringify({
|
|
35
|
+
injectSteps: [{
|
|
36
|
+
ephemeralMessage: '**ASC LADDER PULSE**: Review the 1-6 decision ladder before writing new abstractions. Document deferred debt if taking shortcuts.\n\n' + SECURITY_REMINDER,
|
|
37
|
+
}],
|
|
38
|
+
}) + '\n');
|
|
39
|
+
} else {
|
|
40
|
+
process.stdout.write('{}\n');
|
|
47
41
|
}
|
|
42
|
+
} catch (_) {
|
|
43
|
+
process.stdout.write('{}\n');
|
|
44
|
+
}
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const timer = setTimeout(function () { finish(null); }, 2000);
|
|
49
|
+
|
|
50
|
+
process.stdin.setEncoding('utf8');
|
|
51
|
+
process.stdin.on('data', function (chunk) {
|
|
52
|
+
inputBuffer += chunk;
|
|
53
|
+
try {
|
|
54
|
+
const payload = JSON.parse(inputBuffer);
|
|
55
|
+
finish(payload);
|
|
56
|
+
} catch (_) {}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
process.stdin.on('end', function () {
|
|
60
|
+
let payload = null;
|
|
61
|
+
if (inputBuffer.trim()) {
|
|
62
|
+
try { payload = JSON.parse(inputBuffer); } catch (_) {}
|
|
63
|
+
}
|
|
64
|
+
finish(payload);
|
|
48
65
|
});
|
|
@@ -139,6 +139,26 @@
|
|
|
139
139
|
"id": "ui-agency-micro-meta",
|
|
140
140
|
"regex": "(?:\\b\\d{1,2}\\.\\d+°\\s*[NSEW]\\b|©\\s*20\\d\\d\\s*—\\s*Scroll)",
|
|
141
141
|
"message": "note: performative agency metadata (geographic coordinates, fake version/scroll stamps) detected. Remove ornamental meta."
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"id": "copy-ai-buzzwords",
|
|
145
|
+
"regex": "\\b(seamlessly?|robust(?:ness)?|empowers?|supercharg(?:ed?|ing)|cutting-edge|next-generation|blazing-fast|effortlessly?)\\b",
|
|
146
|
+
"message": "note: generic AI buzzword detected ('seamless', 'robust', 'supercharge', 'cutting-edge'). Use plain operational verbs and concrete metrics."
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"id": "copy-cadence-tricolon",
|
|
150
|
+
"regex": "\\b[A-Z][a-z]+\\.\\s+[A-Z][a-z]+\\.\\s+[A-Z][a-z]+\\.",
|
|
151
|
+
"message": "note: tricolon cadence detected ('Word. Word. Word.'). State the capability or outcome directly instead of using advertising triplets."
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"id": "copy-cadence-rebuttal",
|
|
155
|
+
"regex": "\\b(Not (?:just )?a|More than a) \\w+[,.] (?:it's|a) \\w+\\b",
|
|
156
|
+
"message": "note: synthetic rebuttal cadence ('Not just a X, it\\'s a Y') detected. State what the system does directly without the false dichotomy."
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"id": "copy-filler-preamble",
|
|
160
|
+
"regex": "\\b(In today's (?:fast-paced|digital)|Built for (?:the )?modern teams?|Welcome to the)\\b",
|
|
161
|
+
"message": "note: AI preamble/filler detected ('In today\\'s fast-paced...', 'Built for modern teams'). Delete the introductory throat-clearing and lead with action."
|
|
142
162
|
}
|
|
143
163
|
]
|
|
144
164
|
}
|