@ryuenn3123/agentic-senior-core 6.10.0 → 6.11.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.
- package/.agents/plugins/agentic-senior-core/.codex-plugin/plugin.json +1 -1
- package/.agents/plugins/agentic-senior-core/hooks/lib/known-stub-patterns.json +29 -0
- package/.agents/plugins/agentic-senior-core/hooks/lib/known-ui-slop-patterns.json +15 -0
- package/.agents/plugins/agentic-senior-core/hooks/post-edit-enforce.js +119 -22
- package/.agents/plugins/agentic-senior-core/hooks.json +118 -1
- package/.agents/plugins/agentic-senior-core/plugin.json +1 -1
- package/.agents/plugins/agentic-senior-core/rules/agentic-senior-core.md +5 -1
- package/.agents/plugins/agentic-senior-core/skills/asc-add-feature/SKILL.md +6 -6
- package/.agents/plugins/agentic-senior-core/skills/asc-audit/SKILL.md +6 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-bootstrap/SKILL.md +3 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-debt/SKILL.md +15 -5
- package/.agents/plugins/agentic-senior-core/skills/asc-refactor/SKILL.md +7 -8
- package/.agents/plugins/agentic-senior-core/skills/asc-reference/SKILL.md +4 -10
- package/.agents/plugins/agentic-senior-core/skills/asc-review/SKILL.md +6 -5
- package/gemini-extension.json +1 -1
- package/lib/cli/commands/git-hook-generator.mjs +28 -0
- package/package.json +1 -1
- package/plugin.yaml +1 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Regex patterns for temporary stubs, untracked debt, and fake completions across all languages.",
|
|
3
|
+
"patterns": [
|
|
4
|
+
{
|
|
5
|
+
"id": "asc-stub-untracked",
|
|
6
|
+
"regex": "(?:\\/\\/|#|\\/\\*)\\s*ASC-STUB:(?!.*\\b(ASC-DEBT|#\\d+|[A-Z]+-\\d+)\\b)",
|
|
7
|
+
"message": "ASC-STUB marker detected without tracked debt reference. Add tracking ID (e.g. // ASC-STUB: [reason] [ASC-DEBT-001]) and record in debt ledger.",
|
|
8
|
+
"languages": ["universal"]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"id": "untracked-todo",
|
|
12
|
+
"regex": "(?:\\/\\/|#|\\/\\*)\\s*TODO(?!.*\\b(ASC-DEBT|#\\d+|[A-Z]+-\\d+)\\b)",
|
|
13
|
+
"message": "TODO without tracked debt or issue reference. Track via /asc-debt or resolve before completing task.",
|
|
14
|
+
"languages": ["universal"]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": "fake-success-mock",
|
|
18
|
+
"regex": "(?:return\\s*\\{\\s*success:\\s*true\\s*\\}|return\\s*True|return\\s*true|return\\s*nil)\\s*;?\\s*(?:\\/\\/|#).*?(mock|fake|stub|placeholder|todo)",
|
|
19
|
+
"message": "Hardcoded success return near mock/stub marker detected. Never simulate success to mimic real integration.",
|
|
20
|
+
"languages": ["universal"]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "hardcoded-endpoint-stub",
|
|
24
|
+
"regex": "(?:baseURL|apiUrl|endpoint|api_url)\\s*[:=]\\s*['\"]https?:\\/\\/(?!localhost|127\\.0\\.0\\.1)",
|
|
25
|
+
"message": "Hardcoded external endpoint detected outside config/env. Store endpoints in environment variables.",
|
|
26
|
+
"languages": ["js", "ts", "jsx", "tsx", "py", "go", "rs"]
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
@@ -29,6 +29,21 @@
|
|
|
29
29
|
"id": "ui-pill-badge-generic",
|
|
30
30
|
"regex": "rounded-full\\s+px-4\\s+py-1\\s+text-sm\\s+font-medium\\s+bg-(?:blue|purple|indigo)-100\\s+text-(?:blue|purple|indigo)-800",
|
|
31
31
|
"message": "note: 'generic pill badge' (AI default tag) was flagged, consider continuing with the project's existing badge component instead."
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "ui-gradient-text-cliche",
|
|
35
|
+
"regex": "bg-clip-text\\s+text-transparent|text-transparent\\s+bg-clip-text",
|
|
36
|
+
"message": "note: 'gradient text (bg-clip-text text-transparent)' is an overused AI landing page trope. Prefer solid semantic typography with strong hierarchy."
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "ui-transition-all-layout-cost",
|
|
40
|
+
"regex": "\\btransition-all(?:\\s+duration-[0-9]+)?\\b|transition:\\s*all\\b",
|
|
41
|
+
"message": "note: 'transition-all' triggers layout repaints on all properties. Transition specific GPU-accelerated properties (transform, opacity) instead."
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "ui-stripped-focus-ring",
|
|
45
|
+
"regex": "(?:\\boutline-none\\b|\\bfocus:outline-none\\b)(?!.*(?:focus-visible:ring|focus:ring|focus-visible:outline))",
|
|
46
|
+
"message": "warning: 'outline-none' without a focus-visible ring breaks keyboard navigation (WCAG 2.4.7 violation). Pair with focus-visible:ring-2."
|
|
32
47
|
}
|
|
33
48
|
]
|
|
34
49
|
}
|
|
@@ -40,6 +40,14 @@ try {
|
|
|
40
40
|
}
|
|
41
41
|
} catch (_) { }
|
|
42
42
|
|
|
43
|
+
let STUB_PATTERNS = { patterns: [] };
|
|
44
|
+
try {
|
|
45
|
+
const stubPath = path.join(__dirname, 'lib', 'known-stub-patterns.json');
|
|
46
|
+
if (fs.existsSync(stubPath)) {
|
|
47
|
+
STUB_PATTERNS = JSON.parse(fs.readFileSync(stubPath, 'utf8'));
|
|
48
|
+
}
|
|
49
|
+
} catch (_) { }
|
|
50
|
+
|
|
43
51
|
const {
|
|
44
52
|
SOURCE_EXTENSIONS,
|
|
45
53
|
LOC_DELTA_THRESHOLD,
|
|
@@ -168,16 +176,16 @@ function processSingleEdit(toolName, toolInput, emitFn, skipArray) {
|
|
|
168
176
|
}
|
|
169
177
|
|
|
170
178
|
checkSecurityPatterns(toolName, toolInput, filePath, findings);
|
|
179
|
+
checkStubPatterns(toolName, toolInput, filePath, findings);
|
|
171
180
|
|
|
172
181
|
if (ext === 'html' || ext === 'css' || ext === 'jsx' || ext === 'tsx' || ext === 'vue' || ext === 'svelte') {
|
|
173
182
|
checkUiSlopPatterns(toolName, toolInput, filePath, findings);
|
|
174
183
|
}
|
|
175
184
|
|
|
176
|
-
|
|
177
|
-
checkLinter(filePath, findings);
|
|
178
|
-
}
|
|
185
|
+
checkUniversalLinter(filePath, ext, findings);
|
|
179
186
|
|
|
180
187
|
checkLivingDocNudge(filePath, findings);
|
|
188
|
+
checkTestTampering(filePath, findings);
|
|
181
189
|
|
|
182
190
|
if (ext !== 'md') {
|
|
183
191
|
checkWorkflowGate(toolName, filePath, ext, findings);
|
|
@@ -336,27 +344,95 @@ function checkUiSlopPatterns(toolName, toolInput, filePath, findings) {
|
|
|
336
344
|
}
|
|
337
345
|
}
|
|
338
346
|
|
|
339
|
-
function
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (hasEslint) {
|
|
348
|
-
var execSync = require('child_process').execSync;
|
|
349
|
-
execSync('npx eslint "' + filePath + '" --format json', { cwd: cwd, stdio: 'pipe' });
|
|
350
|
-
}
|
|
351
|
-
} catch (error) {
|
|
352
|
-
if (error.stdout) {
|
|
347
|
+
function checkStubPatterns(toolName, toolInput, filePath, findings) {
|
|
348
|
+
var target = toolName === 'Edit' ? (toolInput.new_string || toolInput.ReplacementContent || '') : (toolInput.content || toolInput.CodeContent || '');
|
|
349
|
+
if (!target) return;
|
|
350
|
+
|
|
351
|
+
var ext = path.extname(filePath).slice(1);
|
|
352
|
+
if (STUB_PATTERNS.patterns) {
|
|
353
|
+
STUB_PATTERNS.patterns.forEach(function (p) {
|
|
353
354
|
try {
|
|
354
|
-
var
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
355
|
+
var langs = p.languages || [];
|
|
356
|
+
var isUniversal = langs.length === 0 || langs.indexOf('universal') !== -1;
|
|
357
|
+
if (!isUniversal && langs.indexOf(ext) === -1) return;
|
|
358
|
+
|
|
359
|
+
var regex = new RegExp(p.regex, 'im');
|
|
360
|
+
var isMatch = regex.test(target);
|
|
361
|
+
logPatternCheck('stub', p.id || 'stub-pattern', isMatch);
|
|
362
|
+
if (isMatch) {
|
|
363
|
+
findings.push('[ASC Stub/Fake-Done Alert] ' + p.message);
|
|
358
364
|
}
|
|
359
365
|
} catch (_) { }
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function checkUniversalLinter(filePath, ext, findings) {
|
|
371
|
+
var execSync = require('child_process').execSync;
|
|
372
|
+
var cwd = process.cwd();
|
|
373
|
+
|
|
374
|
+
if (ext === 'js' || ext === 'ts' || ext === 'jsx' || ext === 'tsx' || ext === 'mjs' || ext === 'cjs') {
|
|
375
|
+
try {
|
|
376
|
+
var hasBiome = fs.existsSync(path.join(cwd, 'biome.json')) || fs.existsSync(path.join(cwd, 'biome.jsonc'));
|
|
377
|
+
if (hasBiome) {
|
|
378
|
+
execSync('npx @biomejs/biome lint "' + filePath + '"', { cwd: cwd, stdio: 'pipe', timeout: 3000 });
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
var hasEslint = fs.existsSync(path.join(cwd, '.eslintrc.json')) ||
|
|
382
|
+
fs.existsSync(path.join(cwd, '.eslintrc.js')) ||
|
|
383
|
+
fs.existsSync(path.join(cwd, 'eslint.config.js')) ||
|
|
384
|
+
fs.existsSync(path.join(cwd, 'eslint.config.mjs')) ||
|
|
385
|
+
(fs.existsSync(path.join(cwd, 'package.json')) && fs.readFileSync(path.join(cwd, 'package.json'), 'utf8').includes('eslintConfig'));
|
|
386
|
+
|
|
387
|
+
if (hasEslint) {
|
|
388
|
+
execSync('npx eslint "' + filePath + '" --format json', { cwd: cwd, stdio: 'pipe', timeout: 4000 });
|
|
389
|
+
}
|
|
390
|
+
} catch (error) {
|
|
391
|
+
if (error.stdout) {
|
|
392
|
+
try {
|
|
393
|
+
var out = JSON.parse(error.stdout.toString());
|
|
394
|
+
if (Array.isArray(out) && out.length > 0 && out[0].messages && out[0].messages.length > 0) {
|
|
395
|
+
var firstErr = out[0].messages[0];
|
|
396
|
+
findings.push('[ASC Linter] ' + firstErr.message + ' at line ' + firstErr.line + '.');
|
|
397
|
+
}
|
|
398
|
+
} catch (_) {
|
|
399
|
+
var msg = error.stdout.toString().split('\n')[0];
|
|
400
|
+
if (msg) findings.push('[ASC Linter] ' + msg);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
} else if (ext === 'py') {
|
|
405
|
+
try {
|
|
406
|
+
if (fs.existsSync(path.join(cwd, 'pyproject.toml')) || fs.existsSync(path.join(cwd, 'ruff.toml'))) {
|
|
407
|
+
execSync('ruff check --quiet "' + filePath + '"', { cwd: cwd, stdio: 'pipe', timeout: 3000 });
|
|
408
|
+
}
|
|
409
|
+
} catch (error) {
|
|
410
|
+
if (error.stdout) {
|
|
411
|
+
var first = error.stdout.toString().split('\n')[0];
|
|
412
|
+
if (first) findings.push('[ASC Python Linter] ' + first);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
} else if (ext === 'go') {
|
|
416
|
+
try {
|
|
417
|
+
if (fs.existsSync(path.join(cwd, 'go.mod'))) {
|
|
418
|
+
execSync('go vet "' + filePath + '"', { cwd: cwd, stdio: 'pipe', timeout: 4000 });
|
|
419
|
+
}
|
|
420
|
+
} catch (error) {
|
|
421
|
+
if (error.stderr) {
|
|
422
|
+
var first = error.stderr.toString().split('\n')[0];
|
|
423
|
+
if (first) findings.push('[ASC Go Vet] ' + first);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
} else if (ext === 'rs') {
|
|
427
|
+
try {
|
|
428
|
+
if (fs.existsSync(path.join(cwd, 'Cargo.toml'))) {
|
|
429
|
+
execSync('cargo clippy --quiet --message-format=short', { cwd: cwd, stdio: 'pipe', timeout: 5000 });
|
|
430
|
+
}
|
|
431
|
+
} catch (error) {
|
|
432
|
+
if (error.stderr) {
|
|
433
|
+
var first = error.stderr.toString().split('\n')[0];
|
|
434
|
+
if (first) findings.push('[ASC Clippy] ' + first);
|
|
435
|
+
}
|
|
360
436
|
}
|
|
361
437
|
}
|
|
362
438
|
}
|
|
@@ -371,10 +447,31 @@ function checkLivingDocNudge(filePath, findings) {
|
|
|
371
447
|
}
|
|
372
448
|
}
|
|
373
449
|
|
|
450
|
+
function checkTestTampering(filePath, findings) {
|
|
451
|
+
var isTest = /(?:\.test\.|\.spec\.|_test\.|__tests__[\/\\]|tests?[\/\\]).*\.(?:js|ts|jsx|tsx|py|go|rs|rb)$/i.test(filePath);
|
|
452
|
+
if (!isTest) return;
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
var pathUtil = require('./path-util.cjs');
|
|
456
|
+
var localGate = path.join(process.cwd(), 'workflow-gate.json');
|
|
457
|
+
var gatePath = fs.existsSync(localGate) ? localGate : pathUtil.getWorkflowGatePath(process.cwd());
|
|
458
|
+
if (!fs.existsSync(gatePath)) return;
|
|
459
|
+
|
|
460
|
+
var gate = JSON.parse(fs.readFileSync(gatePath, 'utf8'));
|
|
461
|
+
if (gate.phase === 'implement' || gate.phase === 'bugfix') {
|
|
462
|
+
findings.push(
|
|
463
|
+
'[ASC Test Integrity Alert] Test file modified (' + path.basename(filePath) + ') during ' + gate.phase + ' phase. '
|
|
464
|
+
+ 'Ensure assertions were NOT weakened to force a pass; fix the underlying business logic instead.'
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
} catch (_) { }
|
|
468
|
+
}
|
|
469
|
+
|
|
374
470
|
function checkWorkflowGate(toolName, filePath, ext, findings) {
|
|
375
471
|
try {
|
|
376
472
|
var pathUtil = require('./path-util.cjs');
|
|
377
|
-
var
|
|
473
|
+
var localGate = path.join(process.cwd(), 'workflow-gate.json');
|
|
474
|
+
var gatePath = fs.existsSync(localGate) ? localGate : pathUtil.getWorkflowGatePath(process.cwd());
|
|
378
475
|
if (!fs.existsSync(gatePath)) return;
|
|
379
476
|
|
|
380
477
|
var gateStr = fs.readFileSync(gatePath, 'utf8');
|
|
@@ -1,4 +1,121 @@
|
|
|
1
1
|
{
|
|
2
|
+
"agentic-senior-core": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "startup|resume|clear|compact",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"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','session-start.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','session-start.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','session-start.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','session-start.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
|
|
10
|
+
"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\\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 -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','subagent-start.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','subagent-start.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','subagent-start.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','subagent-start.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
|
|
23
|
+
"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\\subagent-start.js\" }",
|
|
24
|
+
"timeout": 5,
|
|
25
|
+
"statusMessage": "Loading ASC rules..."
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"PreToolUse": [
|
|
31
|
+
{
|
|
32
|
+
"matcher": "Edit|Write|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 -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');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
|
|
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
|
+
"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 -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');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
|
|
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
|
+
"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 -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');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
|
|
58
|
+
"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\" }",
|
|
59
|
+
"timeout": 5,
|
|
60
|
+
"statusMessage": "ASC Pre-tool dependency check (Terminal)..."
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
"PostToolUse": [
|
|
66
|
+
{
|
|
67
|
+
"matcher": "Edit|Write|replace_file_content|write_to_file|write_file|multi_replace_file_content",
|
|
68
|
+
"hooks": [
|
|
69
|
+
{
|
|
70
|
+
"type": "command",
|
|
71
|
+
"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');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','post-edit-enforce.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
|
|
72
|
+
"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\\post-edit-enforce.js\" }",
|
|
73
|
+
"timeout": 5,
|
|
74
|
+
"statusMessage": "ASC ladder & spec gate check..."
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"type": "command",
|
|
78
|
+
"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','dedup-gate.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','dedup-gate.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','dedup-gate.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','dedup-gate.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
|
|
79
|
+
"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\\dedup-gate.js\" }",
|
|
80
|
+
"timeout": 15,
|
|
81
|
+
"statusMessage": "ASC duplicate-code scan..."
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
"PreInvocation": [
|
|
87
|
+
{
|
|
88
|
+
"type": "command",
|
|
89
|
+
"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','session-pulse.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','session-pulse.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','session-pulse.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','session-pulse.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
|
|
90
|
+
"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\\session-pulse.js\" }",
|
|
91
|
+
"timeout": 5,
|
|
92
|
+
"statusMessage": "Loading ASC rules..."
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"type": "command",
|
|
96
|
+
"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','ladder-pulse.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','ladder-pulse.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','ladder-pulse.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','ladder-pulse.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
|
|
97
|
+
"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\\ladder-pulse.js\" }",
|
|
98
|
+
"timeout": 5,
|
|
99
|
+
"statusMessage": "ASC ladder pulse..."
|
|
100
|
+
}
|
|
101
|
+
],
|
|
102
|
+
"PostInvocation": [
|
|
103
|
+
{
|
|
104
|
+
"type": "command",
|
|
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');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','post-edit-enforce.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
|
|
106
|
+
"timeout": 15
|
|
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');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','pre-compact-pin.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
|
|
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
|
+
}
|
|
117
|
+
]
|
|
118
|
+
},
|
|
2
119
|
"hooks": {
|
|
3
120
|
"SessionStart": [
|
|
4
121
|
{
|
|
@@ -116,4 +233,4 @@
|
|
|
116
233
|
}
|
|
117
234
|
]
|
|
118
235
|
}
|
|
119
|
-
}
|
|
236
|
+
}
|
|
@@ -18,7 +18,7 @@ Before writing code, stop at the lowest step that holds:
|
|
|
18
18
|
When picking step 5 or 6 (unless trivial):
|
|
19
19
|
- One-line comment noting rationale and upgrade trigger if there is a ceiling.
|
|
20
20
|
- One runnable check (assertion, test, or demo) proving it works.
|
|
21
|
-
- Never simulate success or return hardcoded values mimicking live integrations.
|
|
21
|
+
- Never simulate success or return hardcoded values mimicking live integrations. Temporary stubs must be marked with `// ASC-STUB: [reason] [ASC-DEBT-xxx]` and tracked in the debt ledger.
|
|
22
22
|
|
|
23
23
|
## Security (never skip)
|
|
24
24
|
- Validate and normalize ALL inputs at trust boundaries.
|
|
@@ -35,6 +35,7 @@ When picking step 5 or 6 (unless trivial):
|
|
|
35
35
|
- All identifiers (variables, functions, classes, file names) must be in English. No emojis in code or commit messages.
|
|
36
36
|
- Early returns over deep nesting (keep happy path flat).
|
|
37
37
|
- Three similar lines is better than a premature abstraction (duplication over wrong abstraction).
|
|
38
|
+
- Single design source of truth: UI styling must reuse defined design tokens, theme variables, or existing page patterns. No ad-hoc palette, radius, or font deviations between pages.
|
|
38
39
|
- Delete code that carries no behavior, safety, or test value. Readability wins over brevity.
|
|
39
40
|
- Detect and respect project linter/formatter configs. Do not restate style rules enforced by tooling.
|
|
40
41
|
|
|
@@ -46,10 +47,12 @@ When picking step 5 or 6 (unless trivial):
|
|
|
46
47
|
- Scope and direction changes require explicit user confirmation before modifying abstractions or altering system contracts.
|
|
47
48
|
- Before implementing a feature, locate an analogous module and follow its layer split, naming, and error-handling.
|
|
48
49
|
- Atomic writes must be wrapped in transactions. Flag shared mutable state under concurrent requests.
|
|
50
|
+
- UI component reuse: Component re-invention across pages is forbidden. Before declaring markup or styling, check existing components in `components/` or `ui/`. Compose or extend existing components via props/variants. Do not introduce single-page clones or ad-hoc styling that diverges from established pages.
|
|
49
51
|
|
|
50
52
|
## Error Handling & Observability
|
|
51
53
|
- Fail fast on invalid input at trust boundaries. Handle only errors that can actually occur.
|
|
52
54
|
- Structured error responses with safe details (RFC 9457). Distinguish client (4xx) from server (5xx) errors.
|
|
55
|
+
- Frontend data-fetching components must explicitly handle loading, error (toast/notification with retry), and data states.
|
|
53
56
|
- Surface every operational error with context. Empty catch blocks mask production issues.
|
|
54
57
|
- Structured key-value logging for significant events. Never leak stack traces or credentials in production logs.
|
|
55
58
|
|
|
@@ -58,6 +61,7 @@ When picking step 5 or 6 (unless trivial):
|
|
|
58
61
|
- Never run `git commit`, `git push`, or `git push --force` unless explicitly requested this turn.
|
|
59
62
|
- Testing baseline: new business logic gets one happy-path test and one failure-mode test, unless waived by user.
|
|
60
63
|
- Test quality: Never mock the unit under test — mock only external boundaries/dependencies.
|
|
64
|
+
- Test integrity: Never weaken or delete test assertions to simulate a passing run. Fix the underlying logic.
|
|
61
65
|
- Sycophancy mitigation: State technical objections and trade-offs plainly before implementing. Answer direct questions honestly.
|
|
62
66
|
- Preserve findings and decisions outside chat context. Recommend a fresh context at phase boundaries or after 20-30 tool calls.
|
|
63
67
|
|
|
@@ -16,7 +16,7 @@ This workflow nudges the agent to stop at each phase boundary, same enforcement
|
|
|
16
16
|
|
|
17
17
|
For brownfield feature development (`asc-add-feature`), Phase 2 requires a lightweight **PRD.md** (or feature spec in `docs/PRD.md`) defining product intent, goals, and non-goals to avoid scope creep and context rot.
|
|
18
18
|
|
|
19
|
-
To track phase, write to `workflow-gate.json`
|
|
19
|
+
To track phase, write to `workflow-gate.json` at the project root (or via standard file writing tools).
|
|
20
20
|
Format:
|
|
21
21
|
```json
|
|
22
22
|
{
|
|
@@ -30,8 +30,8 @@ Format:
|
|
|
30
30
|
|
|
31
31
|
1. Write `workflow-gate.json` with phase `research`.
|
|
32
32
|
2. Map existing code: patterns, utilities, dependencies already in use. Locate at least one analogous feature/module and record its file paths.
|
|
33
|
-
3.
|
|
34
|
-
4. Output a factual research summary that separates what exists from what is proposed, with file paths for the two or three claims that drive the plan.
|
|
33
|
+
3. **UI Component & Design Token Inventory**: For any UI or frontend work, scan existing shared components (e.g. `components/`, `ui/`) and existing page patterns. Explicitly identify what must NOT be rebuilt (buttons, cards, modals, inputs, tables, layout wrappers, validation helpers).
|
|
34
|
+
4. Output a factual research summary that separates what exists from what is proposed, including an explicit `[REUSED COMPONENTS & HELPERS]` list, with file paths for the two or three claims that drive the plan.
|
|
35
35
|
5. **STOP and wait for user approval.** Do not plan or implement.
|
|
36
36
|
|
|
37
37
|
## Phase 2: Plan
|
|
@@ -40,7 +40,7 @@ Format:
|
|
|
40
40
|
2. Ensure `docs/PRD.md` or feature brief exists.
|
|
41
41
|
3. Check if `.github/workflows/asc-quality-gate.yml` exists. If not, include scaffolding it in your plan (must run linter, type-check, and audit) and remind the user to enable Branch Protection.
|
|
42
42
|
4. Create a numbered, step-by-step implementation plan with specific files, functions, and line references.
|
|
43
|
-
5. Include a "Don't Build" list from the research phase.
|
|
43
|
+
5. Include a "Don't Build" list and a "Reused Components" list from the research phase. For any proposed new UI component, justify why existing components cannot be reused or extended via props/variants.
|
|
44
44
|
6. **Callout: Plan-Reading Illusion.** Ask the user to explicitly verify the two or three critical plan claims against the referenced files, not just skim it.
|
|
45
45
|
7. Output the plan.
|
|
46
46
|
8. **STOP and wait for user approval.** Do not implement.
|
|
@@ -50,5 +50,5 @@ Format:
|
|
|
50
50
|
1. On approval of Phase 2, update `workflow-gate.json` phase to `implement`.
|
|
51
51
|
2. Recommend a fresh context (intentional compaction) at the phase boundary or after roughly 20-30 tool calls. Do not wait until degradation is subjectively noticeable.
|
|
52
52
|
3. Execute the approved plan.
|
|
53
|
-
4.
|
|
54
|
-
5. On
|
|
53
|
+
4. **Mandatory Verification**: Execute the project test suite (`ascx test`, `npm test`, `pytest`, `go test ./...`, or `cargo test`). The test runner MUST exit with code 0. Do NOT declare completion or weaken test assertions if tests fail.
|
|
54
|
+
5. On verified pass, give a short comprehension summary of what changed and why, then clear the state in `workflow-gate.json` by overwriting it with `{}`.
|
|
@@ -10,6 +10,12 @@ Security and architecture audit. Deeper than review, focused on finding vulnerab
|
|
|
10
10
|
|
|
11
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
|
+
## Audit Procedure (Empirical First)
|
|
14
|
+
|
|
15
|
+
1. **Dependency Vulnerability Scan**: Run the project's native security auditor (`npm audit`, `cargo audit`, or `pip-audit`). Report known CVEs immediately.
|
|
16
|
+
2. **Lexical Sink & Secret Scan**: Search for hardcoded credentials, unvalidated redirects (`location.href`), dangerous functions (`eval`, `pickle.load`, `shell=True`), and unverified stubs (`// ASC-STUB:`).
|
|
17
|
+
3. **Trust Boundary Mapping**: Trace all points where untrusted external input (HTTP body/query, CLI args, environment, external files) enters the system.
|
|
18
|
+
|
|
13
19
|
## Audit Scope
|
|
14
20
|
|
|
15
21
|
1. **Trust boundaries**: Every point where external input enters the system. Validate that inputs are sanitized, normalized, and rejected when invalid.
|
|
@@ -19,6 +19,9 @@ Grounded in: Cold-start preference elicitation literature (Deezer/Netflix) & Khr
|
|
|
19
19
|
- `ui-colored-left-border` (Colored left-border strip alert/card)
|
|
20
20
|
- `ui-glassmorphism` (Generic glassmorphism backdrop blur)
|
|
21
21
|
- `ui-pill-badge-generic` (Generic rounded pill tag badge)
|
|
22
|
+
- `ui-gradient-text-cliche` (Overused gradient headline text)
|
|
23
|
+
- `ui-transition-all-layout-cost` (Overused transition-all layout repaint cost)
|
|
24
|
+
- `ui-stripped-focus-ring` (Stripped focus ring WCAG 2.4.7 violation)
|
|
22
25
|
2. Prompt user to select which slop patterns to ban for their brand/repo.
|
|
23
26
|
3. Call `bootstrapPreferences()` in `lib/core/bootstrap-wizard.mjs` to seed selected rules into project preferences.
|
|
24
27
|
4. Auto-compile Track A rules into `.git/hooks/pre-commit` and `ascx validate`.
|
|
@@ -12,13 +12,23 @@ Grounded in: Cunningham's technical debt metaphor (1992). Entry format (ladder s
|
|
|
12
12
|
|
|
13
13
|
## Storage
|
|
14
14
|
|
|
15
|
-
Entries persist in `.agent-context/state/debt-ledger.json` via
|
|
15
|
+
Entries persist in `.agent-context/state/debt-ledger.json` (or `.asc/debt-ledger.json`) via standard file read/write operations. The ledger is a JSON array of entry objects.
|
|
16
|
+
|
|
17
|
+
## Stub Convention
|
|
18
|
+
|
|
19
|
+
When introducing a temporary stub during development, mark it explicitly:
|
|
20
|
+
```typescript
|
|
21
|
+
// ASC-STUB: [reason for temporary code] [ASC-DEBT-001]
|
|
22
|
+
```
|
|
23
|
+
(or `# ASC-STUB: [reason] [ASC-DEBT-001]` in Python/Ruby).
|
|
24
|
+
|
|
25
|
+
Every stub marker in source code MUST reference an open debt ID. Unregistered stubs will be blocked by the Git pre-commit runner.
|
|
16
26
|
|
|
17
27
|
## Entry Format
|
|
18
28
|
|
|
19
29
|
```json
|
|
20
30
|
{
|
|
21
|
-
"id": "sequential integer",
|
|
31
|
+
"id": "sequential integer (or ASC-DEBT-xxx)",
|
|
22
32
|
"file": "path/to/file.ts",
|
|
23
33
|
"ladderStep": "3",
|
|
24
34
|
"violation": "Added axios — stdlib fetch covers this",
|
|
@@ -34,14 +44,14 @@ Entries persist in `.agent-context/state/debt-ledger.json` via MCP `state_read`/
|
|
|
34
44
|
|
|
35
45
|
When an enforcement nudge fires and the violation is deferred:
|
|
36
46
|
|
|
37
|
-
1. Read the current ledger
|
|
47
|
+
1. Read the current ledger from `.agent-context/state/debt-ledger.json`. If missing, start with `[]`.
|
|
38
48
|
2. Append a new entry with the next sequential `id`, the file path, ladder step, and a one-line violation summary.
|
|
39
|
-
3. Write the updated ledger
|
|
49
|
+
3. Write the updated ledger back to disk.
|
|
40
50
|
4. Confirm: "Logged to debt ledger: [violation summary]"
|
|
41
51
|
|
|
42
52
|
### List Open Debt
|
|
43
53
|
|
|
44
|
-
1. Read the ledger
|
|
54
|
+
1. Read the ledger.
|
|
45
55
|
2. Filter to entries where `status` is `"open"`.
|
|
46
56
|
3. Display as a table: ID, file, ladder step, violation, age.
|
|
47
57
|
4. If no open entries, say so explicitly.
|
|
@@ -24,17 +24,16 @@ Removing speculative code is the highest-value, lowest-risk refactor type.
|
|
|
24
24
|
After reading the target code, classify the refactor:
|
|
25
25
|
|
|
26
26
|
- **Low-level** (rename, extract method, type safety, dead code removal): proceed after explaining the change. No approval gate needed.
|
|
27
|
-
- **High-level** (move module, change architecture, redesign abstractions, multi-file structural changes): output a plan with specific files and rationale, then **stop and wait for user approval** before implementing.
|
|
28
|
-
|
|
29
|
-
**Known limitation:** The high-level refactor gate is a skill-text instruction — the agent self-classifies and self-stops. Unlike `/asc-add-feature` and `/asc-new-project`, this gate is not backed by the PostToolUse hook or workflow-gate.json. If the agent bypasses the gate, there is no automated nudge.
|
|
27
|
+
- **High-level** (move module, change architecture, redesign abstractions, multi-file structural changes): write `workflow-gate.json` with `{"workflow": "asc-refactor", "phase": "plan"}`, output a plan with specific files and rationale, then **stop and wait for user approval** before implementing.
|
|
30
28
|
|
|
31
29
|
## Before Editing
|
|
32
30
|
|
|
33
|
-
1.
|
|
34
|
-
2.
|
|
35
|
-
3.
|
|
36
|
-
4.
|
|
37
|
-
5. If the change touches
|
|
31
|
+
1. **Verify Baseline**: Run project test runner (`ascx test`, `npm test`, or equivalent). Do not refactor on failing tests.
|
|
32
|
+
2. Read the target code and understand existing patterns.
|
|
33
|
+
3. Identify the smallest relevant scope for the refactor.
|
|
34
|
+
4. Classify the refactor (see above). For high-level refactors, update `workflow-gate.json` to phase `implement` only after user approval.
|
|
35
|
+
5. If the change touches UI, check accessibility and responsive behavior.
|
|
36
|
+
6. If the change touches dependencies, verify current official docs.
|
|
38
37
|
|
|
39
38
|
## Refactor Rules
|
|
40
39
|
|
|
@@ -40,9 +40,10 @@ Grounded in: WCAG 2.2 AA (accessibility), Fowler's Money Pattern (monetary types
|
|
|
40
40
|
## Frontend
|
|
41
41
|
|
|
42
42
|
- Match the project's existing styling paradigm (Tailwind, CSS Modules, Vanilla CSS, styled-components, etc.). Do not introduce a new CSS framework or build tool without explicit user confirmation.
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
43
|
+
- UI Component Reusability: Primitive UI elements (buttons, inputs, cards, dialogs, badges) must have a single source of truth in the shared component directory (`components/` or `ui/`). Never recreate ad-hoc variants or duplicate JSX structures across individual pages. Extend existing components with props or variants instead of cloning.
|
|
44
|
+
- Design Consistency: Consume shared design tokens, CSS variables, or theme configuration for colors, spacing, and border-radius. Do not hardcode arbitrary raw color hexes or inconsistent spacing that causes visual design drift between pages.
|
|
45
|
+
- Data-fetching components must explicitly handle 3 states: loading, error (with user-facing toast/notification and retry option), and data. Never leave an unhandled error state.
|
|
46
|
+
- WCAG 2.2 AA accessibility floor. Responsive by default.
|
|
46
47
|
- No placeholder, lorem, or TODO content in production UI.
|
|
47
48
|
|
|
48
49
|
## Infrastructure
|
|
@@ -59,10 +60,3 @@ Grounded in: WCAG 2.2 AA (accessibility), Fowler's Money Pattern (monetary types
|
|
|
59
60
|
- Circuit breakers for unhealthy dependencies.
|
|
60
61
|
- Graceful degradation on non-critical dependency failure.
|
|
61
62
|
|
|
62
|
-
## Naming
|
|
63
|
-
|
|
64
|
-
- Variables and properties are nouns: `userList`, `pageCount`, not `getUser`.
|
|
65
|
-
- Functions and methods are verbs: `calculateTotal`, `fetchUsers`, not `total`.
|
|
66
|
-
- Booleans prefixed with `is`/`has`/`can`: `isValid`, `hasPermission`.
|
|
67
|
-
- Constants in UPPER_SNAKE_CASE: `MAX_RETRIES`, `API_BASE_URL`.
|
|
68
|
-
- Follow the casing convention of the language and existing codebase. When starting fresh, use the community default for that language.
|
|
@@ -10,12 +10,13 @@ Production-risk code review. Prioritize findings by severity.
|
|
|
10
10
|
|
|
11
11
|
Grounded in: OWASP Risk Rating Methodology, Google Engineering Practices (code review guide), Clean Architecture (transport vs business layer separation).
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Review Procedure (Empirical First)
|
|
14
14
|
|
|
15
|
-
1.
|
|
16
|
-
2. For
|
|
17
|
-
3.
|
|
18
|
-
4.
|
|
15
|
+
1. **Establish Scope**: Run `git diff --stat` (or `git status`) to inspect the exact set of changed files and line deltas.
|
|
16
|
+
2. **Test-Change Correlation**: For every modified application source file, check if a corresponding test file was updated. If application logic changed without test updates, flag as Severity 3 ("Missing tests for changed behavior").
|
|
17
|
+
3. **Run Deterministic Checks**: Run the project linter and test suite (`ascx test` / `npm test`). Do not evaluate architectural quality on a failing test suite.
|
|
18
|
+
4. **Security & Invariants Scan**: Check for unverified `ASC-STUB:`, hardcoded credentials, or empty catch blocks in the diff.
|
|
19
|
+
5. **Contract & Hierarchy**: Walk the decision ladder for each new file or dependency: does this need to exist, could the stdlib or an existing dependency handle it?
|
|
19
20
|
|
|
20
21
|
## Finding Priority Order
|
|
21
22
|
|
package/gemini-extension.json
CHANGED
|
@@ -202,6 +202,33 @@ function checkLargeFiles(stagedFiles, cwd, maxSizeKB) {
|
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
function checkStubsAndTodos(stagedFiles, cwd) {
|
|
206
|
+
var STUB_REGEX = /(?:\\/\\/|#|\\/\\*)\\s*ASC-STUB:(?!.*\\b(ASC-DEBT|#\\d+|[A-Z]+-\\d+)\\b)/i;
|
|
207
|
+
var MOCK_SUCCESS_REGEX = /(?:return\\s*\\{\\s*success:\\s*true\\s*\\}|return\\s*True|return\\s*true|return\\s*nil)\\s*;?\\s*(?:\\/\\/|#).*?(mock|fake|stub|placeholder)/i;
|
|
208
|
+
|
|
209
|
+
for (var i = 0; i < stagedFiles.length; i++) {
|
|
210
|
+
var ext = path.extname(stagedFiles[i]).slice(1).toLowerCase();
|
|
211
|
+
if (!SOURCE_EXTENSIONS.has(ext)) continue;
|
|
212
|
+
try {
|
|
213
|
+
var content = fs.readFileSync(path.resolve(cwd, stagedFiles[i]), 'utf8');
|
|
214
|
+
var lines = content.split('\\n');
|
|
215
|
+
for (var l = 0; l < lines.length; l++) {
|
|
216
|
+
var line = lines[l];
|
|
217
|
+
if (STUB_REGEX.test(line)) {
|
|
218
|
+
console.error('\\x1b[31m[ASC Stub Gate]\\x1b[0m Unverified stub detected in ' + stagedFiles[i] + ' line ' + (l + 1) + ': ' + line.trim());
|
|
219
|
+
console.error('\\x1b[33mCommit blocked. Stubs must carry a tracked debt ID: // ASC-STUB: [reason] [ASC-DEBT-xxx]. Use git commit --no-verify to bypass.\\x1b[0m');
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
if (MOCK_SUCCESS_REGEX.test(line)) {
|
|
223
|
+
console.error('\\x1b[31m[ASC Fake-Done Gate]\\x1b[0m Simulated success return detected in ' + stagedFiles[i] + ' line ' + (l + 1) + ': ' + line.trim());
|
|
224
|
+
console.error('\\x1b[33mCommit blocked. Never commit hardcoded mock success returns mimicking live integration. Use git commit --no-verify to bypass.\\x1b[0m');
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
} catch (_) {}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
205
232
|
function runJscpdScan(scanCmd, cwd, tmpDir) {
|
|
206
233
|
const binaries = ['bunx jscpd', 'npx jscpd@5', 'npx jscpd'];
|
|
207
234
|
for (let i = 0; i < binaries.length; i++) {
|
|
@@ -318,6 +345,7 @@ function runPreCommitGate() {
|
|
|
318
345
|
checkSecrets(stagedFiles, cwd);
|
|
319
346
|
checkMergeConflictMarkers(stagedFiles, cwd);
|
|
320
347
|
checkLargeFiles(stagedFiles, cwd, config.maxFileSizeKB);
|
|
348
|
+
checkStubsAndTodos(stagedFiles, cwd);
|
|
321
349
|
|
|
322
350
|
// 2. Filter staged files by SOURCE_EXTENSIONS for dedup scan
|
|
323
351
|
const stagedSourceFiles = stagedFiles.filter(f => {
|
package/package.json
CHANGED
package/plugin.yaml
CHANGED