@ryuenn3123/agentic-senior-core 6.2.4 → 6.4.0

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.
Files changed (31) hide show
  1. package/.agents/plugins/agentic-senior-core/hooks/constants.cjs +31 -3
  2. package/.agents/plugins/agentic-senior-core/hooks/dedup-gate.js +45 -28
  3. package/.agents/plugins/agentic-senior-core/hooks/ladder-pulse.js +8 -1
  4. package/.agents/plugins/agentic-senior-core/hooks/lib/known-security-patterns.json +67 -4
  5. package/.agents/plugins/agentic-senior-core/hooks/post-edit-enforce.js +9 -2
  6. package/.agents/plugins/agentic-senior-core/hooks/pre-compact-pin.js +37 -0
  7. package/.agents/plugins/agentic-senior-core/hooks/pre-tool-dependency-gate.js +9 -2
  8. package/.agents/plugins/agentic-senior-core/hooks.json +11 -2
  9. package/.agents/plugins/agentic-senior-core/plugin.json +1 -1
  10. package/.agents/plugins/agentic-senior-core/rules/agentic-senior-core.md +8 -4
  11. package/.agents/plugins/agentic-senior-core/skills/asc/SKILL.md +1 -1
  12. package/.agents/plugins/agentic-senior-core/skills/asc-adapter/SKILL.md +3 -1
  13. package/.agents/plugins/agentic-senior-core/skills/asc-audit/SKILL.md +18 -2
  14. package/.agents/plugins/agentic-senior-core/skills/asc-bootstrap/SKILL.md +1 -1
  15. package/.agents/plugins/agentic-senior-core/skills/asc-dedup/SKILL.md +4 -1
  16. package/.agents/plugins/agentic-senior-core/skills/asc-reference/SKILL.md +1 -1
  17. package/.agents/rules/agentic-senior-core.md +1 -0
  18. package/AGENTS.md +8 -4
  19. package/bin/agentic-senior-core.js +15 -8
  20. package/gemini-extension.json +1 -1
  21. package/lib/cli/commands/adapter.mjs +9 -1
  22. package/lib/cli/commands/git-hook-generator.mjs +273 -0
  23. package/lib/cli/commands/git-hook.mjs +24 -0
  24. package/lib/cli/commands/global.mjs +23 -1
  25. package/lib/cli/commands/uninstall.mjs +51 -1
  26. package/lib/core/adaptive-preferences.mjs +178 -0
  27. package/lib/core/bootstrap-wizard.mjs +64 -0
  28. package/lib/core/revert-detector.mjs +35 -0
  29. package/lib/core/rule-compiler.mjs +105 -0
  30. package/package.json +3 -9
  31. package/plugin.yaml +1 -1
@@ -1,6 +1,5 @@
1
- // Agentic Senior Core — shared constants for hook modules
2
- // Single source of truth for thresholds and extension sets used by
3
- // post-edit-enforce.js and dedup-gate.js.
1
+ const fs = require('fs');
2
+ const path = require('path');
4
3
 
5
4
  const SOURCE_EXTENSIONS = new Set([
6
5
  'js', 'ts', 'mjs', 'cjs', 'jsx', 'tsx',
@@ -12,10 +11,39 @@ const NEW_FILE_LINE_THRESHOLD = 50;
12
11
  const SESSION_DRIFT_THRESHOLD = 4;
13
12
  const LADDER_PULSE_INTERVAL = 3;
14
13
 
14
+ function loadDedupConfig(cwd = process.cwd()) {
15
+ const candidates = [
16
+ path.join(cwd, '.asc', 'dedup-config.json'),
17
+ path.join(cwd, '.agents', 'dedup-config.json'),
18
+ ];
19
+ for (let i = 0; i < candidates.length; i++) {
20
+ try {
21
+ if (fs.existsSync(candidates[i])) {
22
+ return JSON.parse(fs.readFileSync(candidates[i], 'utf8'));
23
+ }
24
+ } catch (_) {}
25
+ }
26
+ return {};
27
+ }
28
+
29
+ function getThresholds(cwd = process.cwd()) {
30
+ const config = loadDedupConfig(cwd);
31
+ return {
32
+ NEW_FILE_LINE_THRESHOLD: typeof config.NEW_FILE_LINE_THRESHOLD === 'number'
33
+ ? config.NEW_FILE_LINE_THRESHOLD
34
+ : NEW_FILE_LINE_THRESHOLD,
35
+ LOC_DELTA_THRESHOLD: typeof config.LOC_DELTA_THRESHOLD === 'number'
36
+ ? config.LOC_DELTA_THRESHOLD
37
+ : LOC_DELTA_THRESHOLD,
38
+ };
39
+ }
40
+
15
41
  module.exports = {
16
42
  SOURCE_EXTENSIONS,
17
43
  LOC_DELTA_THRESHOLD,
18
44
  NEW_FILE_LINE_THRESHOLD,
19
45
  SESSION_DRIFT_THRESHOLD,
20
46
  LADDER_PULSE_INTERVAL,
47
+ getThresholds,
48
+ loadDedupConfig,
21
49
  };
@@ -56,9 +56,9 @@ process.stdin.on('data', chunk => {
56
56
  const ext = path.extname(filePath).slice(1);
57
57
  if (!SOURCE_EXTENSIONS.has(ext)) { process.exit(0); return; }
58
58
 
59
- if (!isQualifyingEdit(toolName, toolInput)) { process.exit(0); return; }
60
-
61
59
  const config = loadDedupConfig();
60
+ if (!isQualifyingEdit(toolName, toolInput, config)) { process.exit(0); return; }
61
+
62
62
  const scanDir = resolveScanDir(filePath, config);
63
63
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'asc-dedup-'));
64
64
 
@@ -101,50 +101,67 @@ function extractToolCallFromTranscript(transcriptPath, stepIdx) {
101
101
 
102
102
  // Find the step with matching step_index that contains tool_calls
103
103
  for (var i = lines.length - 1; i >= 0; i--) {
104
- var step = JSON.parse(lines[i]);
105
- if (step.step_index !== stepIdx || !step.tool_calls) continue;
106
-
107
- for (var j = 0; j < step.tool_calls.length; j++) {
108
- var tc = step.tool_calls[j];
109
- if (tc.name === 'replace_file_content' || tc.name === 'multi_replace_file_content') {
110
- return {
111
- toolName: 'Edit',
112
- toolInput: {
113
- file_path: tc.args.TargetFile || '',
114
- new_string: tc.args.ReplacementContent || '',
115
- old_string: tc.args.TargetContent || '',
116
- },
117
- };
118
- } else if (tc.name === 'write_to_file') {
119
- return {
120
- toolName: 'Write',
121
- toolInput: {
122
- file_path: tc.args.TargetFile || '',
123
- content: tc.args.CodeContent || '',
124
- },
125
- };
104
+ try {
105
+ var step = JSON.parse(lines[i]);
106
+ if (step.step_index !== stepIdx || !step.tool_calls) continue;
107
+
108
+ for (var j = 0; j < step.tool_calls.length; j++) {
109
+ var tc = step.tool_calls[j];
110
+ if (tc.name === 'replace_file_content' || tc.name === 'multi_replace_file_content') {
111
+ return {
112
+ toolName: 'Edit',
113
+ toolInput: {
114
+ file_path: tc.args.TargetFile || '',
115
+ new_string: tc.args.ReplacementContent || '',
116
+ old_string: tc.args.TargetContent || '',
117
+ },
118
+ };
119
+ } else if (tc.name === 'write_to_file') {
120
+ return {
121
+ toolName: 'Write',
122
+ toolInput: {
123
+ file_path: tc.args.TargetFile || '',
124
+ content: tc.args.CodeContent || '',
125
+ },
126
+ };
127
+ }
128
+ }
129
+ } catch (lineErr) {
130
+ if (process.env.ASC_DEBUG) {
131
+ console.error('[ASC Debug] Transcript line parse failed line ' + i + ':', lineErr.message);
126
132
  }
133
+ // Skip malformed line and keep scanning
127
134
  }
128
135
  }
129
136
  return null;
130
- } catch (_) {
137
+ } catch (err) {
138
+ if (process.env.ASC_DEBUG) {
139
+ console.error('[ASC Debug] extractToolCallFromTranscript failed:', err.message);
140
+ }
131
141
  return null;
132
142
  }
133
143
  }
134
144
 
135
- function isQualifyingEdit(toolName, toolInput) {
145
+ function isQualifyingEdit(toolName, toolInput, config) {
136
146
  var isWrite = ['Write', 'write_to_file', 'write_file'].indexOf(toolName) !== -1;
137
147
  var isEdit = ['Edit', 'replace_file_content', 'multi_replace_file_content'].indexOf(toolName) !== -1;
138
148
 
149
+ var newFileThreshold = (config && typeof config.NEW_FILE_LINE_THRESHOLD === 'number')
150
+ ? config.NEW_FILE_LINE_THRESHOLD
151
+ : NEW_FILE_LINE_THRESHOLD;
152
+ var locDeltaThreshold = (config && typeof config.LOC_DELTA_THRESHOLD === 'number')
153
+ ? config.LOC_DELTA_THRESHOLD
154
+ : LOC_DELTA_THRESHOLD;
155
+
139
156
  if (isWrite) {
140
157
  var content = toolInput.content || toolInput.CodeContent || '';
141
- return content.split('\n').length > NEW_FILE_LINE_THRESHOLD;
158
+ return content.split('\n').length > newFileThreshold;
142
159
  }
143
160
  if (isEdit) {
144
161
  var newStr = toolInput.new_string || toolInput.ReplacementContent || toolInput.content || '';
145
162
  var oldStr = toolInput.old_string || toolInput.TargetContent || '';
146
163
  var delta = newStr.split('\n').length - oldStr.split('\n').length;
147
- return delta > LOC_DELTA_THRESHOLD;
164
+ return delta > locDeltaThreshold;
148
165
  }
149
166
  return false;
150
167
  }
@@ -12,6 +12,13 @@ const { LADDER_PULSE_INTERVAL } = require('./constants.cjs');
12
12
  const LADDER_REMINDER = '[ASC] Ladder check: (1) needed? (2) exists already \u2014 reuse? '
13
13
  + '(3) stdlib/native? (4) existing dep? (5) one function? Then minimal code.';
14
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
+ const SECURITY_REMINDER = '[ASC SECURITY PIN — verbatim, do not paraphrase] '
18
+ + 'NEVER: interpolate input into SQL/shell · commit secrets/tokens/credentials '
19
+ + '· store plaintext passwords · leak stack traces/internals/PII in responses. '
20
+ + 'ALWAYS: parameterize queries · enforce resource-level authz · rate-limit public endpoints.';
21
+
15
22
  let inputBuffer = '';
16
23
  process.stdin.setEncoding('utf8');
17
24
  process.stdin.on('data', chunk => {
@@ -28,7 +35,7 @@ process.stdin.on('data', chunk => {
28
35
  if (shouldInject) {
29
36
  process.stdout.write(JSON.stringify({
30
37
  injectSteps: [{
31
- ephemeralMessage: `**ASC LADDER PULSE**: You have completed several steps. Remember to review the 1-6 decision ladder. Document deferred debt if you take shortcuts.`
38
+ ephemeralMessage: `**ASC LADDER PULSE**: You have completed several steps. Remember to review the 1-6 decision ladder. Document deferred debt if you take shortcuts.\n\n${SECURITY_REMINDER}`
32
39
  }]
33
40
  }) + '\n');
34
41
  } else {
@@ -1,20 +1,83 @@
1
1
  {
2
- "description": "Regex patterns for recurring security anti-patterns",
2
+ "description": "Regex patterns for recurring security anti-patterns, grouped by language. Universal patterns run on all files; language-specific patterns run only when the file extension matches.",
3
3
  "patterns": [
4
4
  {
5
5
  "id": "insecure-redirect",
6
6
  "regex": "location\\.href\\s*=\\s*(?!['\"`])([^;\\n]+)",
7
- "message": "Unvalidated redirect target assigned to location.href. Ensure the variable is sanitized or use a safe routing method."
7
+ "message": "Unvalidated redirect target assigned to location.href. Ensure the variable is sanitized or use a safe routing method.",
8
+ "languages": ["js", "ts", "jsx", "tsx", "mjs", "cjs"]
8
9
  },
9
10
  {
10
11
  "id": "timing-unsafe-compare",
11
12
  "regex": "(password|secret|token|key)\\s*(===|!==|==|!=)",
12
- "message": "Non-timing-safe string comparison on a secret variable. Use crypto.timingSafeEqual instead."
13
+ "message": "Non-timing-safe string comparison on a secret variable. Use crypto.timingSafeEqual instead.",
14
+ "languages": ["js", "ts", "jsx", "tsx", "mjs", "cjs"]
13
15
  },
14
16
  {
15
17
  "id": "user-input-http",
16
18
  "regex": "(axios|fetch|got|superagent)\\s*\\(\\s*.*?(req\\.(query|body|params)|process\\.env)",
17
- "message": "Potentially unsafe input passed directly into an HTTP client. Validate and sanitize URL parameters first."
19
+ "message": "Potentially unsafe input passed directly into an HTTP client. Validate and sanitize URL parameters first.",
20
+ "languages": ["js", "ts", "jsx", "tsx", "mjs", "cjs"]
21
+ },
22
+ {
23
+ "id": "py-eval",
24
+ "regex": "\\beval\\s*\\(",
25
+ "message": "eval() on untrusted input enables arbitrary code execution. Use ast.literal_eval() for data parsing or eliminate eval entirely.",
26
+ "languages": ["py"]
27
+ },
28
+ {
29
+ "id": "py-shell-injection",
30
+ "regex": "subprocess.*shell\\s*=\\s*True",
31
+ "message": "subprocess with shell=True is vulnerable to shell injection. Use shell=False with a list of arguments instead.",
32
+ "languages": ["py"]
33
+ },
34
+ {
35
+ "id": "py-unsafe-pickle",
36
+ "regex": "pickle\\.loads?\\s*\\(",
37
+ "message": "pickle.load/loads on untrusted data enables arbitrary code execution. Use a safe format (JSON, msgpack) or validate the source.",
38
+ "languages": ["py"]
39
+ },
40
+ {
41
+ "id": "py-unsafe-yaml",
42
+ "regex": "yaml\\.load\\s*\\((?!.*Loader)",
43
+ "message": "yaml.load without SafeLoader/FullLoader enables arbitrary code execution. Use yaml.safe_load() or specify Loader=yaml.SafeLoader.",
44
+ "languages": ["py"]
45
+ },
46
+ {
47
+ "id": "go-sql-interpolation",
48
+ "regex": "fmt\\.Sprintf\\s*\\(.*(?:SELECT|INSERT|UPDATE|DELETE)",
49
+ "message": "SQL query built with fmt.Sprintf is vulnerable to SQL injection. Use parameterized queries with database/sql placeholders.",
50
+ "languages": ["go"]
51
+ },
52
+ {
53
+ "id": "go-exec-interpolation",
54
+ "regex": "exec\\.Command\\s*\\(.*\\+",
55
+ "message": "exec.Command with string concatenation is vulnerable to command injection. Use separate arguments instead of building a command string.",
56
+ "languages": ["go"]
57
+ },
58
+ {
59
+ "id": "rust-unsafe-no-comment",
60
+ "regex": "unsafe\\s*\\{",
61
+ "message": "unsafe block detected. Document the safety invariant with a // SAFETY: comment explaining why this is sound.",
62
+ "languages": ["rs"]
63
+ },
64
+ {
65
+ "id": "hardcoded-aws-key",
66
+ "regex": "AKIA[0-9A-Z]{16}",
67
+ "message": "Hardcoded AWS access key detected. Remove and inject via environment variable or secrets manager.",
68
+ "languages": ["universal"]
69
+ },
70
+ {
71
+ "id": "hardcoded-private-key",
72
+ "regex": "-----BEGIN.*PRIVATE KEY-----",
73
+ "message": "Private key material in source code. Remove immediately and load from a secure secrets store.",
74
+ "languages": ["universal"]
75
+ },
76
+ {
77
+ "id": "hardcoded-credential",
78
+ "regex": "(password|secret|api_key|token|apikey|api_secret)\\s*=\\s*['\"][^'\"]{8,}['\"]",
79
+ "message": "Possible hardcoded credential. Inject via environment variable or secrets manager instead.",
80
+ "languages": ["universal"]
18
81
  }
19
82
  ],
20
83
  "fileSpecific": {
@@ -276,12 +276,19 @@ function logPatternCheck(checkType, patternId, isMatch) {
276
276
  }
277
277
 
278
278
  function checkSecurityPatterns(toolName, toolInput, filePath, findings) {
279
- var target = toolName === 'Edit' ? (toolInput.new_string || '') : (toolInput.content || '');
279
+ var target = toolName === 'Edit' ? (toolInput.new_string || toolInput.ReplacementContent || '') : (toolInput.content || toolInput.CodeContent || '');
280
280
  if (!target) return;
281
-
281
+
282
+ var ext = path.extname(filePath).slice(1);
283
+
282
284
  if (SECURITY_PATTERNS.patterns) {
283
285
  SECURITY_PATTERNS.patterns.forEach(function (p) {
284
286
  try {
287
+ // Language-aware filtering: skip patterns that don't apply to this file type
288
+ var langs = p.languages || [];
289
+ var isUniversal = langs.length === 0 || langs.indexOf('universal') !== -1;
290
+ if (!isUniversal && langs.indexOf(ext) === -1) return;
291
+
285
292
  var regex = new RegExp(p.regex, 'ig');
286
293
  var isMatch = regex.test(target);
287
294
  logPatternCheck('security', p.id || 'sec-pattern', isMatch);
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ // Agentic Senior Core — PreCompact constraint pinning hook
3
+ // Reinjects critical security constraints and decision ladder verbatim
4
+ // before context compaction, preventing silent erasure of governance rules.
5
+ // Based on: arXiv:2606.22528 "Governance Decay" — Constraint Pinning pattern.
6
+ // Cost: ~80 tokens per injection, well under 0.5% of typical compaction threshold.
7
+
8
+ const SECURITY_PIN = '[ASC SECURITY PIN — verbatim, do not paraphrase]\n'
9
+ + 'NEVER: interpolate input into SQL/shell · commit secrets/tokens/credentials '
10
+ + '· store plaintext passwords (use Argon2/bcrypt) · leak stack traces/internals/PII in responses '
11
+ + '· skip input validation at trust boundaries.\n'
12
+ + 'ALWAYS: parameterize queries · enforce resource-level authz · rate-limit public endpoints '
13
+ + '· encode user-controlled output (XSS) · inject secrets via env vars only.';
14
+
15
+ const LADDER_PIN = '[ASC LADDER PIN]\n'
16
+ + 'Before writing code: (1) needed? (2) exists — reuse? (3) stdlib/native? '
17
+ + '(4) existing dep? (5) one function? (6) minimal code.';
18
+
19
+ let inputBuffer = '';
20
+ process.stdin.setEncoding('utf8');
21
+ process.stdin.on('data', chunk => {
22
+ inputBuffer += chunk;
23
+ try {
24
+ JSON.parse(inputBuffer); // validate complete JSON received
25
+
26
+ const pinContent = SECURITY_PIN + '\n' + LADDER_PIN;
27
+
28
+ process.stdout.write(JSON.stringify({
29
+ injectSteps: [{
30
+ ephemeralMessage: pinContent
31
+ }]
32
+ }) + '\n');
33
+ process.exit(0);
34
+ } catch (e) {
35
+ // wait for more chunks
36
+ }
37
+ });
@@ -63,7 +63,9 @@ process.stdin.on('data', chunk => {
63
63
  added = extractCommandDeps(command);
64
64
  } else if (isFileEdit) {
65
65
  const filePath = toolInput.file_path || toolInput.TargetFile || toolInput.path || toolInput.target_file || '';
66
- if (!filePath.endsWith('package.json')) {
66
+ const manifestFiles = ['package.json', 'requirements.txt', 'pyproject.toml', 'go.mod', 'Cargo.toml', 'Gemfile'];
67
+ const isManifest = manifestFiles.some(function(m) { return filePath.endsWith(m); });
68
+ if (!isManifest) {
67
69
  process.exit(0);
68
70
  return;
69
71
  }
@@ -139,7 +141,12 @@ function extractDeps(text, pattern) {
139
141
  }
140
142
 
141
143
  function extractCommandDeps(command) {
142
- const installRegex = /(?:npm|yarn|pnpm|bun|ascx)\s+(?:install|i|add)(?:\s+[^\s]+)*/i;
144
+ // JS: npm/yarn/pnpm/bun/ascx install/add
145
+ // Python: pip/pip3/uv install, poetry add
146
+ // Go: go get
147
+ // Rust: cargo add
148
+ // Ruby: gem install, bundle add
149
+ const installRegex = /(?:npm|yarn|pnpm|bun|ascx|pip3?|uv|poetry|cargo|gem|bundle)\s+(?:install|i|add|get)(?:\s+[^\s]+)*/i;
143
150
  if (!installRegex.test(command)) return [];
144
151
 
145
152
  const parts = command.split(/\s+/);
@@ -33,7 +33,7 @@
33
33
  "hooks": [
34
34
  {
35
35
  "type": "command",
36
- "if": "Edit(**/package.json)",
36
+ "if": "Edit(**/package.json)|Edit(**/requirements.txt)|Edit(**/pyproject.toml)|Edit(**/go.mod)|Edit(**/Cargo.toml)|Edit(**/Gemfile)",
37
37
  "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:g2));\"",
38
38
  "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\pre-tool-dependency-gate.js\" }",
39
39
  "timeout": 5,
@@ -41,7 +41,7 @@
41
41
  },
42
42
  {
43
43
  "type": "command",
44
- "if": "Write(**/package.json)",
44
+ "if": "Write(**/package.json)|Write(**/requirements.txt)|Write(**/pyproject.toml)|Write(**/go.mod)|Write(**/Cargo.toml)|Write(**/Gemfile)",
45
45
  "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:g2));\"",
46
46
  "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\pre-tool-dependency-gate.js\" }",
47
47
  "timeout": 5,
@@ -105,6 +105,15 @@
105
105
  "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','post-edit-enforce.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','post-edit-enforce.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','post-edit-enforce.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:g2));\"",
106
106
  "timeout": 15
107
107
  }
108
+ ],
109
+ "PreCompact": [
110
+ {
111
+ "type": "command",
112
+ "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','pre-compact-pin.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','pre-compact-pin.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','pre-compact-pin.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:g2));\"",
113
+ "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\pre-compact-pin.js\" }",
114
+ "timeout": 5,
115
+ "statusMessage": "ASC constraint pinning..."
116
+ }
108
117
  ]
109
118
  }
110
119
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-senior-core",
3
- "version": "6.2.4",
3
+ "version": "6.4.0",
4
4
  "description": "Universal AI coding rules. Because your AI writes code like it gets paid by the line.",
5
5
  "contextFileName": "rules/agentic-senior-core.md",
6
6
  "rules": [
@@ -38,6 +38,7 @@ When you pick the minimal option at step 5 or 6, and it isn't obviously trivial:
38
38
  - Delete code that carries no behavior, safety, or test value.
39
39
  - When brevity and readability conflict, readability wins.
40
40
  - Prefer named functions over closures/inline lambdas once logic exceeds a trivial expression.
41
+ - Arrow function shorthand (no braces, implicit return) must not return a void-typed expression — e.g. `onClick={() => setCount(count + 1)}` or `arr.forEach(item => sideEffect(item))`. This trips `@typescript-eslint/no-confusing-void-expression` under strict TS lint configs. Not JSX-specific — applies to any callback assignment in `.js`/`.ts`/`.jsx`/`.tsx` where the shorthand body calls a void-returning function. Use braces instead: `onClick={() => { setCount(count + 1); }}`.
41
42
 
42
43
  ## Architecture
43
44
 
@@ -75,10 +76,13 @@ When you pick the minimal option at step 5 or 6, and it isn't obviously trivial:
75
76
  Recognize the scenario and offer the matching command — user decides
76
77
  whether to invoke it. Skip this for trivial edits.
77
78
 
78
- - Domain-specific rules (Testing, API Design, Database, Frontend, Infrastructure, Resilience) → `/asc-reference`
79
- - New project from scratch → `/asc-new-project` (define/spec gate before implementation)
80
- - Non-trivial feature in an existing codebase → `/asc-add-feature` (research/plan gate before implementation)
81
- - Refactor spanning multiple files or changing architecture → `/asc-refactor` (classifies scope, gates on high-level changes)
79
+ When user intent matches these patterns, offer the corresponding command:
80
+ - **Security/audit** ("audit this", "is this secure", "check for XSS", "find vulnerabilities", "is this safe", "can someone hack this") → `/asc-audit`
81
+ - **Code review** ("review this", "check this PR", "any problems here", "does this look right", "is this production-ready") → `/asc-review`
82
+ - **New project** ("new project", "start from scratch", "scaffold", "build me an app", "I want to build") → `/asc-new-project` (define/spec gate before implementation)
83
+ - **Feature addition** ("add a feature", "implement this", "add this component", "wire up", "make it do X") → `/asc-add-feature` (research/plan gate before implementation)
84
+ - **Refactor** ("refactor this", "clean up", "simplify", "this is messy", "extract this into") → `/asc-refactor` (classifies scope, gates on high-level changes)
85
+ - **Domain reference** (Testing, API Design, Database, Frontend, Infrastructure, Resilience, "how should I test this", "it keeps failing") → `/asc-reference`
82
86
 
83
87
  ### Enforcement Fallbacks (For hosts without hook support)
84
88
  - **Duplicate-Code Check**: When creating new functions or components, actively check for existing near-duplicates across directories (not just siblings) before implementing. If a similar pattern exists, reuse it. Apply the Rule of Three: consolidate only if a pattern appears 3+ times.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: asc
3
3
  description: >
4
- Trigger this skill when the user says: "what are the rules", "coding guidelines", "best practices", "code quality standards", "how should I write this", "staff engineer approach", "senior developer rules", "what does ASC say about". Also trigger for any general request about coding standards, quality guidelines, or when the user asks the agent to follow senior/staff engineering practices.
4
+ Trigger this skill when the user says: "what are the rules", "coding guidelines", "best practices", "code quality standards", "how should I write this", "staff engineer approach", "senior developer rules", "what does ASC say about", "how do I write this properly", "what's the right way", "any guidelines for this", "apa aturannya", "panduan koding", "cara tulis yang bener". Also trigger for any general request about coding standards, quality guidelines, or when the user asks the agent to follow senior/staff engineering practices.
5
5
  ---
6
6
 
7
7
  # Agentic Senior Core
@@ -31,7 +31,8 @@ Adapter hosts (one file per project): Cursor, Devin Desktop, Cline, GitHub Copil
31
31
  asc status # Show detected hosts
32
32
  asc adapter --all # Generate all adapters
33
33
  asc adapter --cursor # Generate for specific host
34
- asc uninstall # Remove all ASC adapter files
34
+ asc install-git-hook # Install native Git pre-commit hook (recommended for all hosts)
35
+ asc uninstall # Remove all ASC adapter files and git hooks
35
36
  asc uninstall --dry-run # Preview what would be removed
36
37
  ```
37
38
 
@@ -41,3 +42,4 @@ asc uninstall --dry-run # Preview what would be removed
41
42
  - Cursor uses `.mdc` format with `alwaysApply: true` frontmatter.
42
43
  - Windsurf is now Devin Desktop. Use `--devin` for the preferred path, `--windsurf` for legacy.
43
44
  - Zed also reads `AGENTS.md` natively, so the adapter is optional.
45
+ - **Git Pre-Commit Hook (`asc install-git-hook`)**: Host plugin runtimes vary — adapter hosts and certain chat surfaces (e.g., Antigravity IDE / Antigravity 2.0 chat interface) do not run agent lifecycle hooks. Installing the native Git pre-commit hook ensures 100% deterministic duplicate code blocking and ESLint auto-fixing directly via Git on all hosts.
@@ -1,14 +1,14 @@
1
1
  ---
2
2
  name: asc-audit
3
3
  description: >
4
- Trigger this skill when the user says: "audit this", "security check", "find vulnerabilities", "is this secure", "check for XSS", "check for SQL injection", "threat model", "penetration test", "OWASP check", "architecture review", "is this safe", "check auth", "check permissions", "find security holes". Also trigger for any deep security audit, vulnerability scanning, or request to find structural anti-patterns in existing code. Also trigger when reviewing authentication, authorization, input validation, or encryption-related code.
4
+ Trigger this skill when the user says: "audit this", "security check", "find vulnerabilities", "is this secure", "check for XSS", "check for SQL injection", "threat model", "penetration test", "OWASP check", "architecture review", "is this safe", "check auth", "check permissions", "find security holes", "can someone hack this", "is my data safe", "can users see each other's data", "is the login secure", "audit ini", "cek keamanan", "cari celah keamanan", "apakah ini aman", "bisa di-hack ga". Also trigger for any deep security audit, vulnerability scanning, or request to find structural anti-patterns in existing code. Also trigger when reviewing authentication, authorization, input validation, or encryption-related code.
5
5
  ---
6
6
 
7
7
  # Audit Skill
8
8
 
9
9
  Security and architecture audit. Deeper than review, focused on finding vulnerabilities and structural anti-patterns.
10
10
 
11
- Grounded in: OWASP Top 10 (2021), OWASP ASVS v4, CVSS vulnerability report structure, CWE classification.
11
+ Grounded in: OWASP Top 10 (2025), OWASP ASVS v5.0, OWASP Top 10 for Agentic Applications (ASI01-ASI10, v2.01), CVSS vulnerability report structure, CWE classification.
12
12
 
13
13
  ## Audit Scope
14
14
 
@@ -19,6 +19,21 @@ Grounded in: OWASP Top 10 (2021), OWASP ASVS v4, CVSS vulnerability report struc
19
19
  5. **Dependency health**: Known vulnerabilities, unmaintained packages, excessive dependency surface.
20
20
  6. **Error exposure**: Stack traces, internal paths, or implementation details exposed to clients.
21
21
 
22
+ ## Agentic Risk Scope (OWASP Top 10 for Agentic Applications)
23
+
24
+ When the target is an AI agent system, MCP server, or plugin:
25
+
26
+ 7. **Agent Goal Hijack (ASI01)**: Content read by the agent that could override instructions.
27
+ 8. **Tool Misuse (ASI02)**: Tools callable without adequate validation of parameters.
28
+ 9. **Identity & Privilege Abuse (ASI03)**: Agent running with broader permissions than needed.
29
+ 10. **Agentic Supply Chain (ASI04)**: Untrusted plugins, MCP servers, or dependencies.
30
+ 11. **Unexpected Code Execution (ASI05)**: Agent-generated code running without sandbox.
31
+ 12. **Memory & Context Poisoning (ASI06)**: State files or persistent memory injectable by untrusted sources. Note: ASC's own `debt-ledger.json` and `workflow-gate.json` are potential targets — treat as untrusted input at load time.
32
+ 13. **Inter-Agent Communication (ASI07)**: Agent-to-agent messages without integrity checks.
33
+ 14. **Cascading Failures (ASI08)**: Multi-agent chains where one failure propagates.
34
+ 15. **Human-Agent Trust Exploitation (ASI09)**: UI/UX that misleads user about agent actions.
35
+ 16. **Rogue Agents (ASI10)**: Agent behavior diverging from intended purpose.
36
+
22
37
  ## For Every Finding
23
38
 
24
39
  ```
@@ -34,3 +49,4 @@ Validation: how to prove it is fixed
34
49
  ## Output
35
50
 
36
51
  Findings ordered by severity. If no findings, state that explicitly and describe audit coverage.
52
+
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: asc-bootstrap
3
3
  description: >
4
- Trigger this skill when the user says: "bootstrap preferences", "set up my preferences", "ui slop wizard", "seed my rules", "init design rules", "run preference onboarding", "onboard slop rules", "start cold start wizard".
4
+ Trigger this skill when the user says: "bootstrap preferences", "set up my preferences", "ui slop wizard", "seed my rules", "init design rules", "run preference onboarding", "onboard slop rules", "start cold start wizard", "set up my style preferences", "customize design rules", "configure how my UI should look", "atur preferensi desain", "konfigurasi gaya ui".
5
5
  ---
6
6
 
7
7
  # Preference Bootstrap Wizard (`asc-bootstrap`)
@@ -4,7 +4,10 @@ description: >
4
4
  Trigger this skill when the user says: "find duplicate code", "check
5
5
  for clones", "audit for duplication", "is this repeated elsewhere",
6
6
  "scan for copy-paste", "run jscpd", "dedup report", "consolidate
7
- duplicate logic". Use for whole-repo or whole-directory duplication
7
+ duplicate logic", "this looks the same as the other file",
8
+ "we already have this somewhere", "why is this code repeated",
9
+ "isn't this a copy of", "cari kode duplikat", "ini kok sama kayak yang itu",
10
+ "ini udah ada kan". Use for whole-repo or whole-directory duplication
8
11
  audits on demand — this is a deep, on-demand scan, distinct from the
9
12
  continuous per-edit check already enforced by the dedup-gate hook.
10
13
  ---
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: asc-reference
3
3
  description: >
4
- Trigger this skill when the user is working on: unit tests, integration tests, REST APIs, GraphQL APIs, SQL queries, database migrations, React components, frontend layouts, Docker configs, CI/CD pipelines, Kubernetes manifests, or service resilience (retries, circuit breakers, rate limiting). Also trigger when the user says: "how should I test this", "design this API", "optimize this query", "set up Docker", "add retry logic", "write a test", "add pagination", "handle errors", "add loading state", "set up CI". Also trigger when editing files matching: `*.test.*`, `*.spec.*`, `Dockerfile`, `docker-compose.*`, `.github/workflows/*`, `*.sql`, or migration files.
4
+ Trigger this skill when the user is working on: unit tests, integration tests, REST APIs, GraphQL APIs, SQL queries, database migrations, React components, frontend layouts, Docker configs, CI/CD pipelines, Kubernetes manifests, or service resilience (retries, circuit breakers, rate limiting). Also trigger when the user says: "how should I test this", "design this API", "optimize this query", "set up Docker", "add retry logic", "write a test", "add pagination", "handle errors", "add loading state", "set up CI", "it keeps failing", "make it try again if it fails", "it's too slow with lots of data", "looks broken on mobile", "how do I deploy this", "will this break anything", "bagaimana cara ngetes ini", "bikin tes", "kok lambat banget", "tampilan di hp rusak", "cara deploy ini". Also trigger when editing files matching: `*.test.*`, `*.spec.*`, `Dockerfile`, `docker-compose.*`, `.github/workflows/*`, `*.sql`, or migration files.
5
5
  ---
6
6
 
7
7
  # ASC Domain Reference
@@ -38,6 +38,7 @@ When you pick the minimal option at step 5 or 6, and it isn't obviously trivial:
38
38
  - Delete code that carries no behavior, safety, or test value.
39
39
  - When brevity and readability conflict, readability wins.
40
40
  - Prefer named functions over closures/inline lambdas once logic exceeds a trivial expression.
41
+ - Arrow function shorthand (no braces, implicit return) must not return a void-typed expression — e.g. `onClick={() => setCount(count + 1)}` or `arr.forEach(item => sideEffect(item))`. This trips `@typescript-eslint/no-confusing-void-expression` under strict TS lint configs. Not JSX-specific — applies to any callback assignment in `.js`/`.ts`/`.jsx`/`.tsx` where the shorthand body calls a void-returning function. Use braces instead: `onClick={() => { setCount(count + 1); }}`.
41
42
 
42
43
  ## Architecture
43
44
 
package/AGENTS.md CHANGED
@@ -33,6 +33,7 @@ When you pick the minimal option at step 5 or 6, and it isn't obviously trivial:
33
33
  - Delete code that carries no behavior, safety, or test value.
34
34
  - When brevity and readability conflict, readability wins.
35
35
  - Prefer named functions over closures/inline lambdas once logic exceeds a trivial expression.
36
+ - Arrow function shorthand (no braces, implicit return) must not return a void-typed expression — e.g. `onClick={() => setCount(count + 1)}` or `arr.forEach(item => sideEffect(item))`. This trips `@typescript-eslint/no-confusing-void-expression` under strict TS lint configs. Not JSX-specific — applies to any callback assignment in `.js`/`.ts`/`.jsx`/`.tsx` where the shorthand body calls a void-returning function. Use braces instead: `onClick={() => { setCount(count + 1); }}`.
36
37
 
37
38
  ## Architecture
38
39
 
@@ -70,10 +71,13 @@ When you pick the minimal option at step 5 or 6, and it isn't obviously trivial:
70
71
  Recognize the scenario and offer the matching command — user decides
71
72
  whether to invoke it. Skip this for trivial edits.
72
73
 
73
- - Domain-specific rules (Testing, API Design, Database, Frontend, Infrastructure, Resilience) → `/asc-reference`
74
- - New project from scratch → `/asc-new-project` (define/spec gate before implementation)
75
- - Non-trivial feature in an existing codebase → `/asc-add-feature` (research/plan gate before implementation)
76
- - Refactor spanning multiple files or changing architecture → `/asc-refactor` (classifies scope, gates on high-level changes)
74
+ When user intent matches these patterns, offer the corresponding command:
75
+ - **Security/audit** ("audit this", "is this secure", "check for XSS", "find vulnerabilities", "is this safe", "can someone hack this") → `/asc-audit`
76
+ - **Code review** ("review this", "check this PR", "any problems here", "does this look right", "is this production-ready") → `/asc-review`
77
+ - **New project** ("new project", "start from scratch", "scaffold", "build me an app", "I want to build") → `/asc-new-project` (define/spec gate before implementation)
78
+ - **Feature addition** ("add a feature", "implement this", "add this component", "wire up", "make it do X") → `/asc-add-feature` (research/plan gate before implementation)
79
+ - **Refactor** ("refactor this", "clean up", "simplify", "this is messy", "extract this into") → `/asc-refactor` (classifies scope, gates on high-level changes)
80
+ - **Domain reference** (Testing, API Design, Database, Frontend, Infrastructure, Resilience, "how should I test this", "it keeps failing") → `/asc-reference`
77
81
 
78
82
  ### Enforcement Fallbacks (For hosts without hook support)
79
83
  - **Duplicate-Code Check**: When creating new functions or components, actively check for existing near-duplicates across directories (not just siblings) before implementing. If a similar pattern exists, reuse it. Apply the Rule of Three: consolidate only if a pattern appears 3+ times.
@@ -31,14 +31,15 @@ function printUsage() {
31
31
  console.log('Adapter install (one file per project):');
32
32
  console.log(' asc adapter --cursor --devin --cline --copilot --kiro --continue --zed --aider --kilocode --roo --openhands --windsurf --all\n');
33
33
  console.log('Commands:');
34
- console.log(' adapter Generate instruction-tier adapter files');
35
- console.log(' global Install rules to user-level (global) locations');
36
- console.log(' uninstall Remove ASC adapter files from this project');
37
- console.log(' clean Remove v4 per-project artifacts');
38
- console.log(' status Show detected IDEs and install hints');
39
- console.log(' mcp Start MCP stdio server');
40
- console.log(' --version Show version');
41
- console.log(' --help Show this help');
34
+ console.log(' adapter Generate instruction-tier adapter files');
35
+ console.log(' global Install rules to user-level (global) locations');
36
+ console.log(' install-git-hook Install Git pre-commit hook for duplicate code enforcement');
37
+ console.log(' uninstall Remove ASC adapter files and git hooks from this project');
38
+ console.log(' clean Remove v4 per-project artifacts');
39
+ console.log(' status Show detected IDEs and install hints');
40
+ console.log(' mcp Start MCP stdio server');
41
+ console.log(' --version Show version');
42
+ console.log(' --help Show this help');
42
43
  }
43
44
 
44
45
  async function main() {
@@ -55,6 +56,12 @@ async function main() {
55
56
  return;
56
57
  }
57
58
 
59
+ if (commandArgument === 'install-git-hook' || commandArgument === 'git-hook') {
60
+ const { runGitHookCommand } = await import('../lib/cli/commands/git-hook.mjs');
61
+ await runGitHookCommand(commandArguments);
62
+ return;
63
+ }
64
+
58
65
  if (commandArgument === 'adapter') {
59
66
  const { runAdapterCommand } = await import('../lib/cli/commands/adapter.mjs');
60
67
  await runAdapterCommand(commandArguments);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-senior-core",
3
- "version": "6.2.4",
3
+ "version": "6.4.0",
4
4
  "description": "Universal AI coding rules. Write code like a staff engineer.",
5
5
  "author": "fatidaprilian",
6
6
  "license": "MIT",