@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.
Files changed (47) hide show
  1. package/.agents/plugins/agentic-senior-core/.codex-plugin/plugin.json +1 -1
  2. package/.agents/plugins/agentic-senior-core/commands/asc-pentest.md +2 -0
  3. package/.agents/plugins/agentic-senior-core/commands/asc-pentest.toml +3 -0
  4. package/.agents/plugins/agentic-senior-core/hooks/dedup-gate.js +137 -128
  5. package/.agents/plugins/agentic-senior-core/hooks/hooks.json +97 -0
  6. package/.agents/plugins/agentic-senior-core/hooks/ladder-pulse.js +53 -36
  7. package/.agents/plugins/agentic-senior-core/hooks/lib/known-ui-slop-patterns.json +20 -0
  8. package/.agents/plugins/agentic-senior-core/hooks/path-util.cjs +118 -4
  9. package/.agents/plugins/agentic-senior-core/hooks/post-edit-enforce.js +98 -81
  10. package/.agents/plugins/agentic-senior-core/hooks/pre-tool-dependency-gate.js +168 -191
  11. package/.agents/plugins/agentic-senior-core/hooks/session-pulse.js +48 -35
  12. package/.agents/plugins/agentic-senior-core/hooks/session-start.js +62 -11
  13. package/.agents/plugins/agentic-senior-core/hooks.json +4 -94
  14. package/.agents/plugins/agentic-senior-core/plugin.json +5 -13
  15. package/.agents/plugins/agentic-senior-core/rules/agentic-senior-core.md +6 -1
  16. package/.agents/plugins/agentic-senior-core/skills/asc/SKILL.md +5 -2
  17. package/.agents/plugins/agentic-senior-core/skills/asc-develop/SKILL.md +3 -3
  18. package/.agents/plugins/agentic-senior-core/skills/asc-pentest/SKILL.md +97 -0
  19. package/.agents/plugins/agentic-senior-core/skills/asc-pentest/references/api-security-idor.md +105 -0
  20. package/.agents/plugins/agentic-senior-core/skills/asc-pentest/references/dast-baseline.md +78 -0
  21. package/.agents/plugins/agentic-senior-core/skills/asc-pentest/references/fuzzing-dsl.md +66 -0
  22. package/.agents/plugins/agentic-senior-core/skills/asc-pentest/references/pentest-task-tree.md +70 -0
  23. package/.agents/plugins/agentic-senior-core/skills/asc-review/SKILL.md +2 -2
  24. package/.agents/plugins/agentic-senior-core/skills/asc-test/SKILL.md +8 -1
  25. package/.agents/plugins/agentic-senior-core/skills/asc-test/references/mutation-testing.md +55 -0
  26. package/.agents/plugins/agentic-senior-core/skills/asc-test/references/performance-budgets.md +67 -0
  27. package/.agents/plugins/agentic-senior-core/skills/asc-test/references/property-testing.md +72 -0
  28. package/.agents/plugins/agentic-senior-core/skills/asc-ui/SKILL.md +17 -14
  29. package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/copy-i18n.md +56 -8
  30. package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/craft/color-surfaces.md +25 -0
  31. package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/craft/themes-catalog.md +198 -0
  32. package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/divergence.md +34 -2
  33. package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/modes/genre-calibration.md +89 -0
  34. package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/modes/operate.md +26 -0
  35. package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/modes/persuade.md +10 -9
  36. package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/redesign-audit.md +4 -9
  37. package/bin/agentic-senior-core.js +19 -4
  38. package/gemini-extension.json +2 -2
  39. package/lib/cli/ascx/runtime.mjs +5 -27
  40. package/lib/cli/ascx/windows-spawn.mjs +348 -0
  41. package/lib/cli/commands/bootstrap.mjs +56 -0
  42. package/lib/cli/commands/dedup.mjs +500 -0
  43. package/lib/cli/commands/git-hook-generator.mjs +3 -2
  44. package/lib/cli/commands/global.mjs +109 -53
  45. package/lib/cli/commands/preview.mjs +18 -9
  46. package/package.json +1 -1
  47. package/plugin.yaml +3 -1
@@ -184,7 +184,7 @@ async function ensureCodexConfigEnabled() {
184
184
  async function installCodexGlobal(target) {
185
185
  const rulesSource = path.join(REPOSITORY_ROOT, target.rulesSourcePath);
186
186
  const pluginSource = path.join(REPOSITORY_ROOT, target.pluginSourcePath);
187
- const hooksSource = path.join(REPOSITORY_ROOT, '.agents', 'plugins', 'agentic-senior-core', 'hooks.json');
187
+ const hooksSource = path.join(REPOSITORY_ROOT, '.agents', 'plugins', 'agentic-senior-core', 'hooks', 'hooks.json');
188
188
  const singlePluginSource = path.join(REPOSITORY_ROOT, '.agents', 'plugins', 'agentic-senior-core');
189
189
  const rulesTarget = target.targetPath(); // ~/.codex/AGENTS.md
190
190
  const hooksTarget = path.join(HOME, '.codex', 'hooks.json');
@@ -372,9 +372,10 @@ async function installAntigravityIde(target) {
372
372
  'utf8'
373
373
  );
374
374
 
375
- // Safely update/merge "agentic-senior-core" group key into global hooks.json (~/.gemini/config/hooks.json)
376
- const masterHooksSource = path.join(REPOSITORY_ROOT, '.agents', 'plugins', 'agentic-senior-core', 'hooks.json');
377
- await updateGlobalAntigravityHooks(masterHooksSource);
375
+ // Remove legacy global hooks.json entries (~/.gemini/config/hooks.json & ~/.gemini/antigravity-cli/hooks.json)
376
+ // because Antigravity executes hooks with cwd set to the directory containing hooks.json, and automatically
377
+ // discovers hooks.json inside ~/.gemini/config/plugins/agentic-senior-core/hooks.json without duplicate firing.
378
+ await cleanupLegacyGlobalAntigravityHooks();
378
379
 
379
380
  // Automatically register the plugin with Antigravity CLI if agy binary is available
380
381
  try {
@@ -389,44 +390,29 @@ async function installAntigravityIde(target) {
389
390
  return true;
390
391
  }
391
392
 
392
- async function updateGlobalAntigravityHooks(hooksSource) {
393
- if (!(await pathExists(hooksSource))) return;
393
+ async function cleanupLegacyGlobalAntigravityHooks() {
394
+ const globalHooksPath = path.join(HOME, '.gemini', 'config', 'hooks.json');
395
+ const legacyCliHooksPath = path.join(HOME, '.gemini', 'antigravity-cli', 'hooks.json');
394
396
 
395
- try {
396
- const rawMaster = JSON.parse(await fs.readFile(hooksSource, 'utf8'));
397
- const masterHooks = rawMaster.hooks || rawMaster;
398
-
399
- const formattedAscHooks = {
400
- SessionStart: masterHooks.SessionStart || null,
401
- PreInvocation: masterHooks.PreInvocation || null,
402
- PostInvocation: masterHooks.PostInvocation || null,
403
- Stop: masterHooks.Stop || null,
404
- PreToolUse: masterHooks.PreToolUse || null,
405
- PostToolUse: masterHooks.PostToolUse || null,
406
- };
407
-
408
- const targetPaths = [
409
- path.join(HOME, '.gemini', 'config', 'hooks.json'),
410
- path.join(HOME, '.gemini', 'antigravity-cli', 'hooks.json'),
411
- ];
412
-
413
- for (const targetPath of targetPaths) {
414
- try {
415
- await fs.mkdir(path.dirname(targetPath), { recursive: true });
416
- let existingConfig = {};
417
- if (await pathExists(targetPath)) {
418
- try {
419
- existingConfig = JSON.parse(await fs.readFile(targetPath, 'utf8'));
420
- } catch (_) {}
421
- }
422
-
423
- // Safely merge "agentic-senior-core" key preserving other user keys (e.g. "well")
424
- existingConfig['agentic-senior-core'] = formattedAscHooks;
397
+ if (await pathExists(legacyCliHooksPath)) {
398
+ try {
399
+ await fs.rm(legacyCliHooksPath, { force: true });
400
+ } catch (_) {}
401
+ }
425
402
 
426
- await fs.writeFile(targetPath, JSON.stringify(existingConfig, null, 2) + '\n', 'utf8');
427
- } catch (_) {}
428
- }
429
- } catch (_) {}
403
+ if (await pathExists(globalHooksPath)) {
404
+ try {
405
+ const existingConfig = JSON.parse(await fs.readFile(globalHooksPath, 'utf8'));
406
+ if (existingConfig && typeof existingConfig === 'object' && 'agentic-senior-core' in existingConfig) {
407
+ delete existingConfig['agentic-senior-core'];
408
+ if (Object.keys(existingConfig).length === 0) {
409
+ await fs.rm(globalHooksPath, { force: true });
410
+ } else {
411
+ await fs.writeFile(globalHooksPath, JSON.stringify(existingConfig, null, 2) + '\n', 'utf8');
412
+ }
413
+ }
414
+ } catch (_) {}
415
+ }
430
416
  }
431
417
 
432
418
  async function findAgyBinaryPath() {
@@ -509,7 +495,78 @@ async function installGlobalTarget(targetKey) {
509
495
  return true;
510
496
  }
511
497
 
512
- export async function runGlobalCommand(commandArguments) {
498
+ export async function detectInstalledGlobalTargets() {
499
+ const isWin = process.platform === 'win32';
500
+ const { resolveWindowsExecutable } = await import('../ascx/windows-spawn.mjs');
501
+ const { execSync } = await import('node:child_process');
502
+
503
+ const hasBinary = (cmd) => {
504
+ if (isWin) {
505
+ return Boolean(resolveWindowsExecutable(cmd, process.env, process.cwd()));
506
+ }
507
+ try {
508
+ execSync(`command -v ${cmd}`, { stdio: 'ignore' });
509
+ return true;
510
+ } catch {
511
+ return false;
512
+ }
513
+ };
514
+
515
+ const checks = {
516
+ antigravity: async () => (await pathExists(path.join(HOME, '.gemini'))) || hasBinary('agy'),
517
+ codex: async () => (await pathExists(path.join(HOME, '.codex'))) || hasBinary('codex'),
518
+ opencode: async () => (await pathExists(path.join(HOME, '.config', 'opencode')))
519
+ || (await pathExists(path.join(HOME, '.opencode')))
520
+ || hasBinary('opencode'),
521
+ roo: async () => await pathExists(path.join(HOME, '.roo')),
522
+ kilocode: async () => (await pathExists(path.join(HOME, '.config', 'kilo')))
523
+ || (await pathExists(path.join(HOME, '.kilo')))
524
+ || (await pathExists(path.join(HOME, '.kilocode')))
525
+ || (process.env.APPDATA ? await pathExists(path.join(process.env.APPDATA, 'kilo')) : false)
526
+ || hasBinary('kilo'),
527
+ cline: async () => (await pathExists(path.join(HOME, '.cline')))
528
+ || (await pathExists(path.join(HOME, 'Documents', 'Cline'))),
529
+ kiro: async () => await pathExists(path.join(HOME, '.kiro')),
530
+ openhands: async () => (await pathExists(path.join(HOME, '.openhands'))) || hasBinary('openhands'),
531
+ windsurf: async () => (await pathExists(path.join(HOME, '.windsurf')))
532
+ || (await pathExists(path.join(HOME, '.codeium')))
533
+ || (isWin ? await pathExists(path.join(HOME, 'AppData', 'Roaming', 'Windsurf')) : false),
534
+ copilot: async () => (await pathExists(path.join(HOME, '.config', 'github-copilot')))
535
+ || (isWin ? await pathExists(path.join(HOME, 'AppData', 'Local', 'GitHub Copilot')) : false)
536
+ || (await pathExists(path.dirname(vscodeUserPromptsDirectory()))),
537
+ };
538
+
539
+ const detected = [];
540
+ for (const [key, check] of Object.entries(checks)) {
541
+ if (GLOBAL_TARGETS[key] && (await check())) {
542
+ detected.push(key);
543
+ }
544
+ }
545
+ return detected;
546
+ }
547
+
548
+ function printGlobalHelp() {
549
+ console.log('Agentic Senior Core -- Global Rules Installer\n');
550
+ console.log('Usage:');
551
+ console.log(' asc global Auto-detect installed AI tools and install globally');
552
+ console.log(' asc global --all Install to all supported global targets');
553
+ console.log(' asc global --<target> Install to specific target(s)\n');
554
+ console.log('Supported automatic global targets:');
555
+ for (const [key, target] of Object.entries(GLOBAL_TARGETS)) {
556
+ console.log(` --${key.padEnd(14)} ${target.label} (${target.note})`);
557
+ }
558
+ console.log('\nManual global setup tools:');
559
+ for (const manual of MANUAL_TARGETS) {
560
+ console.log(` ${manual.label.padEnd(16)} ${manual.hint}`);
561
+ }
562
+ }
563
+
564
+ export async function runGlobalCommand(commandArguments = []) {
565
+ if (commandArguments.includes('--help') || commandArguments.includes('-h')) {
566
+ printGlobalHelp();
567
+ return;
568
+ }
569
+
513
570
  const requestedTargets = [];
514
571
 
515
572
  for (const argument of commandArguments) {
@@ -521,7 +578,11 @@ export async function runGlobalCommand(commandArguments) {
521
578
  const targetKey = argument.replace(/^--/, '');
522
579
  if (GLOBAL_TARGETS[targetKey]) {
523
580
  requestedTargets.push(targetKey);
524
- } else if (argument.startsWith('--')) {
581
+ } else if (argument.startsWith('-')) {
582
+ console.error(`Unknown global target: ${argument}`);
583
+ console.log(`Available targets: ${Object.keys(GLOBAL_TARGETS).map(k => `--${k}`).join(', ')}, --all`);
584
+ process.exit(1);
585
+ } else {
525
586
  console.error(`Unknown global target: ${argument}`);
526
587
  console.log(`Available targets: ${Object.keys(GLOBAL_TARGETS).map(k => `--${k}`).join(', ')}, --all`);
527
588
  process.exit(1);
@@ -529,18 +590,13 @@ export async function runGlobalCommand(commandArguments) {
529
590
  }
530
591
 
531
592
  if (requestedTargets.length === 0) {
532
- console.log('Agentic Senior Core -- Global Rules Installer\n');
533
- console.log('Usage: asc global [--antigravity] [--codex] [--cline] [--roo] [--kilocode] [--opencode] [--kiro] [--openhands] [--windsurf] [--copilot] [--all]\n');
534
- console.log('Installs user-level global rules that apply to ALL projects.\n');
535
- console.log('Supported automatic global targets:');
536
- for (const [key, target] of Object.entries(GLOBAL_TARGETS)) {
537
- console.log(` --${key.padEnd(14)} ${target.label} (${target.note})`);
538
- }
539
- console.log('\nManual global setup tools:');
540
- for (const manual of MANUAL_TARGETS) {
541
- console.log(` ${manual.label.padEnd(16)} ${manual.hint}`);
593
+ const autoDetected = await detectInstalledGlobalTargets();
594
+ if (autoDetected.length === 0) {
595
+ printGlobalHelp();
596
+ return;
542
597
  }
543
- process.exit(0);
598
+ console.log(`Auto-detected ${autoDetected.length} installed AI host(s): ${autoDetected.join(', ')}\n`);
599
+ requestedTargets.push(...autoDetected);
544
600
  }
545
601
 
546
602
  console.log('Installing global rules...\n');
@@ -6,8 +6,19 @@
6
6
  import fs from 'node:fs';
7
7
  import path from 'node:path';
8
8
  import os from 'node:os';
9
+ import { pathToFileURL } from 'node:url';
9
10
  import { execSync } from 'node:child_process';
10
11
 
12
+ function openInDefaultBrowser(targetPath) {
13
+ if (process.platform === 'win32') {
14
+ execSync(`cmd.exe /d /s /c "start "" "${targetPath}""`, { stdio: 'ignore' });
15
+ } else if (process.platform === 'darwin') {
16
+ execSync(`open "${targetPath}"`, { stdio: 'ignore' });
17
+ } else {
18
+ execSync(`xdg-open "${targetPath}"`, { stdio: 'ignore' });
19
+ }
20
+ }
21
+
11
22
  export function getBrowserCandidates() {
12
23
  const platform = process.platform;
13
24
  const candidates = [];
@@ -161,9 +172,8 @@ export async function runPreviewCommand(args = []) {
161
172
 
162
173
  // Open in default OS browser if requested
163
174
  if (shouldOpen) {
164
- const opener = process.platform === 'win32' ? 'start' : process.platform === 'darwin' ? 'open' : 'xdg-open';
165
175
  try {
166
- execSync(`${opener} "" "${resolvedHtmlPath}"`, { stdio: 'ignore' });
176
+ openInDefaultBrowser(resolvedHtmlPath);
167
177
  console.log(`[asc preview] Opened in default browser: ${resolvedHtmlPath}`);
168
178
  return;
169
179
  } catch (err) {
@@ -175,20 +185,20 @@ export async function runPreviewCommand(args = []) {
175
185
  const browser = findBrowserExecutable();
176
186
  if (!browser) {
177
187
  console.warn('[asc preview] No supported headless browser detected (Brave, Edge, Chrome, Firefox). Falling back to OS opener.');
178
- const opener = process.platform === 'win32' ? 'start' : process.platform === 'darwin' ? 'open' : 'xdg-open';
179
- execSync(`${opener} "" "${resolvedHtmlPath}"`, { stdio: 'ignore' });
188
+ openInDefaultBrowser(resolvedHtmlPath);
180
189
  return;
181
190
  }
182
191
 
183
192
  try {
193
+ const fileUrl = pathToFileURL(resolvedHtmlPath).href;
184
194
  let command;
185
195
  if (browser.family === 'chromium') {
186
- command = `"${browser.path}" --headless --disable-gpu --screenshot="${outputPng}" --window-size=${width},${height} "${resolvedHtmlPath}"`;
196
+ command = `"${browser.path}" --headless=new --disable-gpu --screenshot="${outputPng}" --window-size=${width},${height} "${fileUrl}"`;
187
197
  } else if (browser.family === 'firefox') {
188
- command = `"${browser.path}" -headless -screenshot "${outputPng}" -window-size ${width},${height} "${resolvedHtmlPath}"`;
198
+ command = `"${browser.path}" -headless -screenshot "${outputPng}" -window-size ${width},${height} "${fileUrl}"`;
189
199
  }
190
200
 
191
- execSync(command, { stdio: 'pipe' });
201
+ execSync(command, { stdio: 'pipe', timeout: 15000 });
192
202
 
193
203
  if (fs.existsSync(outputPng)) {
194
204
  const stat = fs.statSync(outputPng);
@@ -199,8 +209,7 @@ export async function runPreviewCommand(args = []) {
199
209
  }
200
210
  } catch (err) {
201
211
  console.warn(`[asc preview] Headless snapshot failed: ${err.message}. Falling back to OS opener.`);
202
- const opener = process.platform === 'win32' ? 'start' : process.platform === 'darwin' ? 'open' : 'xdg-open';
203
- execSync(`${opener} "" "${resolvedHtmlPath}"`, { stdio: 'ignore' });
212
+ openInDefaultBrowser(resolvedHtmlPath);
204
213
  }
205
214
  }
206
215
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryuenn3123/agentic-senior-core",
3
- "version": "6.16.2",
3
+ "version": "6.16.5",
4
4
  "type": "module",
5
5
  "description": "Agentic Senior Core: Universal AI coding rules and workflows. Write code like a staff engineer, not a junior.",
6
6
  "bin": {
package/plugin.yaml CHANGED
@@ -1,5 +1,5 @@
1
1
  name: agentic-senior-core
2
- version: 6.16.2
2
+ version: 6.16.5
3
3
  description: Universal AI coding rules. Write code like a staff engineer.
4
4
  author: fatidaprilian
5
5
  provides_hooks:
@@ -11,6 +11,7 @@ provides_commands:
11
11
  - asc-reference
12
12
  - asc-ui
13
13
  - asc-test
14
+ - asc-pentest
14
15
  provides_skills:
15
16
  - asc
16
17
  - asc-develop
@@ -18,3 +19,4 @@ provides_skills:
18
19
  - asc-review
19
20
  - asc-ui
20
21
  - asc-test
22
+ - asc-pentest