@ryuenn3123/agentic-senior-core 5.8.21 → 5.8.23
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/hooks/post-edit-enforce.js +100 -38
- package/.agents/plugins/agentic-senior-core/hooks/pre-tool-dependency-gate.js +20 -11
- package/.agents/plugins/agentic-senior-core/hooks.json +14 -7
- package/.agents/plugins/agentic-senior-core/plugin.json +10 -4
- package/.agents/plugins/agentic-senior-core/skills/asc-new-project/SKILL.md +5 -6
- package/README.md +10 -20
- package/gemini-extension.json +12 -6
- package/lib/cli/commands/adapter.mjs +21 -13
- package/lib/cli/commands/global.mjs +21 -11
- package/package.json +1 -21
- package/plugin.yaml +1 -1
- package/.claude-plugin/marketplace.json +0 -17
- package/.claude-plugin/plugin.json +0 -17
- package/.clinerules/agentic-senior-core.md +0 -109
- package/.codex-plugin/plugin.json +0 -24
- package/.continue/rules/agentic-senior-core.md +0 -109
- package/.cursor/rules/agentic-senior-core.mdc +0 -114
- package/.devin/rules/agentic-senior-core.md +0 -109
- package/.devin-plugin/plugin.json +0 -13
- package/.github/copilot-instructions.md +0 -109
- package/.github/plugin/marketplace.json +0 -20
- package/.github/plugin/plugin.json +0 -16
- package/.kilocode/rules/agentic-senior-core.md +0 -109
- package/.kiro/steering/agentic-senior-core.md +0 -109
- package/.openclaw/skills/asc/SKILL.md +0 -36
- package/.openclaw/skills/asc-adapter/SKILL.md +0 -43
- package/.openclaw/skills/asc-add-feature/SKILL.md +0 -53
- package/.openclaw/skills/asc-audit/SKILL.md +0 -36
- package/.openclaw/skills/asc-debt/SKILL.md +0 -72
- package/.openclaw/skills/asc-new-project/SKILL.md +0 -62
- package/.openclaw/skills/asc-refactor/SKILL.md +0 -62
- package/.openclaw/skills/asc-reference/SKILL.md +0 -59
- package/.openclaw/skills/asc-review/SKILL.md +0 -63
- package/.opencode/plugins/agentic-senior-core.mjs +0 -31
- package/.openhands/microagents/agentic-senior-core.md +0 -109
- package/.roo/rules/agentic-senior-core.md +0 -109
- package/.windsurf/rules/agentic-senior-core.md +0 -109
- package/.zed/rules/agentic-senior-core.md +0 -109
- package/hooks/copilot-hooks.json +0 -30
- package/hooks/hooks.json +0 -86
- package/hooks/lib/known-duplicates.json +0 -32
- package/hooks/package.json +0 -3
- package/hooks/path-util.cjs +0 -23
- package/hooks/post-edit-enforce.js +0 -234
- package/hooks/pre-tool-dependency-gate.js +0 -155
- package/hooks/session-start.js +0 -36
- package/hooks/subagent-start.js +0 -38
- package/skills/asc/SKILL.md +0 -36
- package/skills/asc-adapter/SKILL.md +0 -43
- package/skills/asc-add-feature/SKILL.md +0 -53
- package/skills/asc-audit/SKILL.md +0 -36
- package/skills/asc-debt/SKILL.md +0 -72
- package/skills/asc-new-project/SKILL.md +0 -62
- package/skills/asc-refactor/SKILL.md +0 -62
- package/skills/asc-reference/SKILL.md +0 -59
- package/skills/asc-review/SKILL.md +0 -63
- /package/{commands → .agents/plugins/agentic-senior-core/commands}/asc-add-feature.md +0 -0
- /package/{commands → .agents/plugins/agentic-senior-core/commands}/asc-add-feature.toml +0 -0
- /package/{commands → .agents/plugins/agentic-senior-core/commands}/asc-audit.md +0 -0
- /package/{commands → .agents/plugins/agentic-senior-core/commands}/asc-audit.toml +0 -0
- /package/{commands → .agents/plugins/agentic-senior-core/commands}/asc-help.md +0 -0
- /package/{commands → .agents/plugins/agentic-senior-core/commands}/asc-help.toml +0 -0
- /package/{commands → .agents/plugins/agentic-senior-core/commands}/asc-new-project.md +0 -0
- /package/{commands → .agents/plugins/agentic-senior-core/commands}/asc-new-project.toml +0 -0
- /package/{commands → .agents/plugins/agentic-senior-core/commands}/asc-refactor.md +0 -0
- /package/{commands → .agents/plugins/agentic-senior-core/commands}/asc-refactor.toml +0 -0
- /package/{commands → .agents/plugins/agentic-senior-core/commands}/asc-review.md +0 -0
- /package/{commands → .agents/plugins/agentic-senior-core/commands}/asc-review.toml +0 -0
package/hooks/hooks.json
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"hooks": {
|
|
3
|
-
"SessionStart": [
|
|
4
|
-
{
|
|
5
|
-
"matcher": "startup|resume|clear|compact",
|
|
6
|
-
"hooks": [
|
|
7
|
-
{
|
|
8
|
-
"type": "command",
|
|
9
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT:-$HOME/.gemini/config/plugins/agentic-senior-core}/hooks/session-start.js\"; exit 0",
|
|
10
|
-
"commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } 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 \"${CLAUDE_PLUGIN_ROOT:-$HOME/.gemini/config/plugins/agentic-senior-core}/hooks/subagent-start.js\"; exit 0",
|
|
23
|
-
"commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } 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)",
|
|
37
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT:-$HOME/.gemini/config/plugins/agentic-senior-core}/hooks/pre-tool-dependency-gate.js\"; exit 0",
|
|
38
|
-
"commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } 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)",
|
|
45
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT:-$HOME/.gemini/config/plugins/agentic-senior-core}/hooks/pre-tool-dependency-gate.js\"; exit 0",
|
|
46
|
-
"commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } 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 \"${CLAUDE_PLUGIN_ROOT:-$HOME/.gemini/config/plugins/agentic-senior-core}/hooks/pre-tool-dependency-gate.js\"; exit 0",
|
|
58
|
-
"commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } 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 \"${CLAUDE_PLUGIN_ROOT:-$HOME/.gemini/config/plugins/agentic-senior-core}/hooks/post-edit-enforce.js\"; exit 0",
|
|
72
|
-
"commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } 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": "prompt",
|
|
78
|
-
"if": "Edit(**/package.json)",
|
|
79
|
-
"prompt": "A new dependency '$ARGUMENTS' was added to package.json. Does it duplicate Node.js standard library or platform features? If yes, return JSON: {\"hookSpecificOutput\": {\"hookEventName\": \"PostToolUse\", \"additionalContext\": \"[ASC Advisory] The newly added dependency appears to duplicate stdlib or platform features. Consider native alternatives.\"}}. If no, return {}.",
|
|
80
|
-
"timeout": 30
|
|
81
|
-
}
|
|
82
|
-
]
|
|
83
|
-
}
|
|
84
|
-
]
|
|
85
|
-
}
|
|
86
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"description": "Single source of truth for packages that duplicate standard library or native platform features.",
|
|
3
|
-
"duplicates": [
|
|
4
|
-
"lodash",
|
|
5
|
-
"lodash-es",
|
|
6
|
-
"underscore",
|
|
7
|
-
"moment",
|
|
8
|
-
"dayjs",
|
|
9
|
-
"uuid",
|
|
10
|
-
"nanoid",
|
|
11
|
-
"chalk",
|
|
12
|
-
"kleur",
|
|
13
|
-
"colorette",
|
|
14
|
-
"axios",
|
|
15
|
-
"got",
|
|
16
|
-
"node-fetch",
|
|
17
|
-
"superagent",
|
|
18
|
-
"mkdirp",
|
|
19
|
-
"rimraf",
|
|
20
|
-
"del",
|
|
21
|
-
"glob",
|
|
22
|
-
"globby",
|
|
23
|
-
"left-pad",
|
|
24
|
-
"pad-left",
|
|
25
|
-
"is-odd",
|
|
26
|
-
"is-even",
|
|
27
|
-
"is-number",
|
|
28
|
-
"is-string",
|
|
29
|
-
"path-exists",
|
|
30
|
-
"fs-extra"
|
|
31
|
-
]
|
|
32
|
-
}
|
package/hooks/package.json
DELETED
package/hooks/path-util.cjs
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
const crypto = require('crypto');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const os = require('os');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Computes a stable path for the workflow gate file based on the project path.
|
|
7
|
-
* Normalizes the path (resolves absolute, lowercase, forward slashes, no trailing slash)
|
|
8
|
-
* to ensure identical hashes regardless of OS path separator or drive letter casing.
|
|
9
|
-
* Note: .toLowerCase() deliberately treats Linux/Mac paths case-insensitively,
|
|
10
|
-
* accepting the negligible risk of collisions for the benefit of Windows stability.
|
|
11
|
-
*
|
|
12
|
-
* @param {string} projectPath - The root path of the project.
|
|
13
|
-
* @returns {string} Absolute path to the global workflow gate JSON file.
|
|
14
|
-
*/
|
|
15
|
-
function getWorkflowGatePath(projectPath) {
|
|
16
|
-
const normalizedPath = path.resolve(projectPath).replace(/\\/g, '/').toLowerCase().replace(/\/$/, '');
|
|
17
|
-
const projectHash = crypto.createHash('sha256').update(normalizedPath).digest('hex').substring(0, 16);
|
|
18
|
-
return path.join(os.homedir(), '.config', 'agentic-senior-core', 'gates', `${projectHash}.json`);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
module.exports = {
|
|
22
|
-
getWorkflowGatePath
|
|
23
|
-
};
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Agentic Senior Core — PostToolUse enforcement hook
|
|
3
|
-
// Fires after Edit/Write. Checks for ladder violations and injects a nudge.
|
|
4
|
-
// Supports Claude Code, Codex CLI, and GitHub Copilot CLI.
|
|
5
|
-
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
|
|
9
|
-
let STDLIB_DUPLICATES = new Set([
|
|
10
|
-
'lodash', 'lodash-es', 'underscore',
|
|
11
|
-
'moment', 'dayjs',
|
|
12
|
-
'uuid', 'nanoid',
|
|
13
|
-
'chalk', 'kleur', 'colorette',
|
|
14
|
-
'axios', 'got', 'node-fetch', 'superagent',
|
|
15
|
-
'mkdirp', 'rimraf', 'del',
|
|
16
|
-
'glob', 'globby',
|
|
17
|
-
'left-pad', 'pad-left',
|
|
18
|
-
'is-odd', 'is-even', 'is-number', 'is-string',
|
|
19
|
-
'path-exists', 'fs-extra',
|
|
20
|
-
]);
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
const knownPath = path.join(__dirname, 'lib', 'known-duplicates.json');
|
|
24
|
-
if (fs.existsSync(knownPath)) {
|
|
25
|
-
const raw = JSON.parse(fs.readFileSync(knownPath, 'utf8'));
|
|
26
|
-
if (Array.isArray(raw.duplicates)) {
|
|
27
|
-
STDLIB_DUPLICATES = new Set(raw.duplicates);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
} catch (_) {}
|
|
31
|
-
|
|
32
|
-
const SOURCE_EXTENSIONS = new Set([
|
|
33
|
-
'js', 'ts', 'mjs', 'cjs', 'jsx', 'tsx',
|
|
34
|
-
'py', 'rb', 'go', 'rs', 'java', 'kt', 'swift', 'cs',
|
|
35
|
-
]);
|
|
36
|
-
|
|
37
|
-
const LOC_DELTA_THRESHOLD = 30;
|
|
38
|
-
const NEW_FILE_LINE_THRESHOLD = 50;
|
|
39
|
-
|
|
40
|
-
let inputBuffer = '';
|
|
41
|
-
process.stdin.setEncoding('utf8');
|
|
42
|
-
process.stdin.on('data', function (chunk) { inputBuffer += chunk; });
|
|
43
|
-
process.stdin.on('end', function () {
|
|
44
|
-
try {
|
|
45
|
-
const data = JSON.parse(inputBuffer);
|
|
46
|
-
const toolName = data.tool_name || '';
|
|
47
|
-
const toolInput = data.tool_input || {};
|
|
48
|
-
const filePath = toolInput.file_path || '';
|
|
49
|
-
const findings = [];
|
|
50
|
-
|
|
51
|
-
if (filePath.endsWith('package.json')) {
|
|
52
|
-
checkDependencyAddition(toolName, toolInput, findings);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const ext = path.extname(filePath).slice(1);
|
|
56
|
-
if (SOURCE_EXTENSIONS.has(ext)) {
|
|
57
|
-
if (toolName === 'Edit') {
|
|
58
|
-
checkLocDelta(toolInput, filePath, findings);
|
|
59
|
-
} else if (toolName === 'Write') {
|
|
60
|
-
checkNewFileSize(toolInput, filePath, findings);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
checkLivingDocNudge(filePath, findings);
|
|
65
|
-
|
|
66
|
-
if (ext !== 'md') {
|
|
67
|
-
checkWorkflowGate(toolName, filePath, ext, findings);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (findings.length === 0) return;
|
|
71
|
-
|
|
72
|
-
const nudge = '[ASC enforcement] ' + findings.join(' ') + ' Review the decision ladder before continuing.';
|
|
73
|
-
emit(nudge);
|
|
74
|
-
} catch (_) {
|
|
75
|
-
// Silent fail — enforcement must not break the session
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
function checkDependencyAddition(toolName, toolInput, findings) {
|
|
80
|
-
var target = toolName === 'Edit' ? (toolInput.new_string || '') : (toolInput.content || '');
|
|
81
|
-
var baseline = toolName === 'Edit' ? (toolInput.old_string || '') : '';
|
|
82
|
-
|
|
83
|
-
var depPattern = /"([^"]+)"\s*:\s*"[~^>=<*]?\d/g;
|
|
84
|
-
var newDeps = extractDeps(target, depPattern);
|
|
85
|
-
var oldDeps = extractDeps(baseline, depPattern);
|
|
86
|
-
|
|
87
|
-
var added = newDeps.filter(function (d) { return oldDeps.indexOf(d) === -1; });
|
|
88
|
-
if (added.length === 0) return;
|
|
89
|
-
|
|
90
|
-
var stdlibDupes = added.filter(function (d) { return STDLIB_DUPLICATES.has(d); });
|
|
91
|
-
if (stdlibDupes.length > 0) {
|
|
92
|
-
findings.push(
|
|
93
|
-
'Dependency ' + stdlibDupes.join(', ') + ' may duplicate stdlib/platform features. '
|
|
94
|
-
+ 'Ladder step 3: does the standard library cover this?'
|
|
95
|
-
);
|
|
96
|
-
} else {
|
|
97
|
-
findings.push(
|
|
98
|
-
'New dependency added: ' + added.join(', ') + '. '
|
|
99
|
-
+ 'Ladder step 3-4: stdlib or already-installed alternative?'
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function extractDeps(text, pattern) {
|
|
105
|
-
var matches = [];
|
|
106
|
-
var match;
|
|
107
|
-
while ((match = pattern.exec(text)) !== null) {
|
|
108
|
-
matches.push(match[1]);
|
|
109
|
-
}
|
|
110
|
-
return matches;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function checkLocDelta(toolInput, filePath, findings) {
|
|
114
|
-
var newLines = (toolInput.new_string || '').split('\n').length;
|
|
115
|
-
var oldLines = (toolInput.old_string || '').split('\n').length;
|
|
116
|
-
var delta = newLines - oldLines;
|
|
117
|
-
if (delta > LOC_DELTA_THRESHOLD) {
|
|
118
|
-
findings.push(
|
|
119
|
-
'Edit added ' + delta + ' net lines to ' + path.basename(filePath) + '. '
|
|
120
|
-
+ 'Ladder step 5: can this be one straightforward function?'
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function checkNewFileSize(toolInput, filePath, findings) {
|
|
126
|
-
var lines = (toolInput.content || '').split('\n').length;
|
|
127
|
-
if (lines > NEW_FILE_LINE_THRESHOLD) {
|
|
128
|
-
findings.push(
|
|
129
|
-
'New file ' + path.basename(filePath) + ' created with ' + lines + ' lines. '
|
|
130
|
-
+ 'Ladder step 1-2: does this need to be built? Does the codebase already have this?'
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function checkLivingDocNudge(filePath, findings) {
|
|
136
|
-
var lower = filePath.toLowerCase();
|
|
137
|
-
if (lower.includes('schema') || lower.includes('migration') || lower.includes('model') || lower.includes('prisma')) {
|
|
138
|
-
findings.push(
|
|
139
|
-
'[ASC Living Doc] Data contract/model modified in ' + path.basename(filePath) + '. '
|
|
140
|
-
+ 'Ensure docs/Schema.md and docs/Architecture.md are kept in sync to prevent spec drift.'
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function checkWorkflowGate(toolName, filePath, ext, findings) {
|
|
146
|
-
try {
|
|
147
|
-
var pathUtil = require('./path-util.cjs');
|
|
148
|
-
var gatePath = pathUtil.getWorkflowGatePath(process.cwd());
|
|
149
|
-
if (!fs.existsSync(gatePath)) return;
|
|
150
|
-
|
|
151
|
-
var gateStr = fs.readFileSync(gatePath, 'utf8');
|
|
152
|
-
var gate = JSON.parse(gateStr);
|
|
153
|
-
|
|
154
|
-
if (gate.updatedAt) {
|
|
155
|
-
var ageHours = (Date.now() - new Date(gate.updatedAt).getTime()) / (1000 * 60 * 60);
|
|
156
|
-
if (ageHours > 4) {
|
|
157
|
-
fs.unlinkSync(gatePath);
|
|
158
|
-
findings.push('Cleared stale workflow gate (' + gate.workflow + ').');
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (gate.phase === 'research' || gate.phase === 'plan') {
|
|
164
|
-
findings.push(
|
|
165
|
-
'Workflow gate bypass: ' + gate.workflow + ' is in ' + gate.phase + ' phase but source code was edited. '
|
|
166
|
-
+ 'Stop and wait for phase approval. If you must proceed, log this bypass to the debt ledger.'
|
|
167
|
-
);
|
|
168
|
-
} else if (gate.phase === 'implement') {
|
|
169
|
-
validateDocSpecs(gate.workflow, findings);
|
|
170
|
-
}
|
|
171
|
-
} catch (_) {
|
|
172
|
-
// Silent fail
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function validateDocSpecs(workflow, findings) {
|
|
177
|
-
try {
|
|
178
|
-
var cwd = process.cwd();
|
|
179
|
-
var docsDir = path.join(cwd, 'docs');
|
|
180
|
-
|
|
181
|
-
// Anti-typo check for common doc typos (e.g. Architectyre.md)
|
|
182
|
-
var searchDirs = [cwd, docsDir];
|
|
183
|
-
var typoFound = false;
|
|
184
|
-
searchDirs.forEach(function (d) {
|
|
185
|
-
if (fs.existsSync(d)) {
|
|
186
|
-
var files = fs.readdirSync(d);
|
|
187
|
-
files.forEach(function (f) {
|
|
188
|
-
if (f.toLowerCase() !== f && /architect[y|u]re/i.test(f) && f !== 'Architecture.md') {
|
|
189
|
-
findings.push('[ASC Spec Typo Alert] Typo detected in doc filename "' + f + '". Rename to "Architecture.md".');
|
|
190
|
-
typoFound = true;
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
if (workflow === 'asc-new-project') {
|
|
197
|
-
var requiredDocs = ['PRD.md', 'Architecture.md', 'Design.md', 'Schema.md'];
|
|
198
|
-
var missing = requiredDocs.filter(function (doc) {
|
|
199
|
-
return !fs.existsSync(path.join(cwd, doc)) && !fs.existsSync(path.join(docsDir, doc));
|
|
200
|
-
});
|
|
201
|
-
if (missing.length > 0) {
|
|
202
|
-
findings.push(
|
|
203
|
-
'[ASC 5-Doc SDD Gate] Greenfield project missing spec document(s): ' + missing.join(', ') + '. '
|
|
204
|
-
+ 'Create them in docs/ to align requirements before continuing implementation.'
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
|
-
} else if (workflow === 'asc-add-feature') {
|
|
208
|
-
var hasPrd = fs.existsSync(path.join(cwd, 'PRD.md')) || fs.existsSync(path.join(docsDir, 'PRD.md'));
|
|
209
|
-
if (!hasPrd) {
|
|
210
|
-
findings.push(
|
|
211
|
-
'[ASC SDD Gate] Feature addition missing docs/PRD.md. Define goals and non-goals before coding.'
|
|
212
|
-
);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
} catch (_) {}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function emit(nudge) {
|
|
219
|
-
try {
|
|
220
|
-
var isCopilot = Boolean(process.env.COPILOT_PLUGIN_DATA);
|
|
221
|
-
var output = {
|
|
222
|
-
hookSpecificOutput: {
|
|
223
|
-
hookEventName: 'PostToolUse',
|
|
224
|
-
additionalContext: nudge,
|
|
225
|
-
},
|
|
226
|
-
};
|
|
227
|
-
if (isCopilot) {
|
|
228
|
-
output = { additionalContext: nudge };
|
|
229
|
-
}
|
|
230
|
-
process.stdout.write(JSON.stringify(output));
|
|
231
|
-
} catch (_) {
|
|
232
|
-
// EPIPE — silent
|
|
233
|
-
}
|
|
234
|
-
}
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Agentic Senior Core — PreToolUse dependency gate hook
|
|
3
|
-
// Hard-blocks edits/writes to package.json AND shell commands (npm install/yarn add/ascx)
|
|
4
|
-
// if new dependencies duplicate stdlib/platform features.
|
|
5
|
-
// Supports Claude Code, Antigravity IDE (run_command), and Copilot CLI hook response formats.
|
|
6
|
-
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const path = require('path');
|
|
9
|
-
|
|
10
|
-
let knownDuplicates = new Set();
|
|
11
|
-
try {
|
|
12
|
-
const knownPath = path.join(__dirname, 'lib', 'known-duplicates.json');
|
|
13
|
-
if (fs.existsSync(knownPath)) {
|
|
14
|
-
const raw = JSON.parse(fs.readFileSync(knownPath, 'utf8'));
|
|
15
|
-
if (Array.isArray(raw.duplicates)) {
|
|
16
|
-
knownDuplicates = new Set(raw.duplicates);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
} catch (_) {
|
|
20
|
-
// Fallback set if JSON loading fails
|
|
21
|
-
knownDuplicates = new Set(['lodash', 'underscore', 'moment', 'dayjs', 'uuid', 'axios', 'rimraf']);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
let inputBuffer = '';
|
|
25
|
-
process.stdin.setEncoding('utf8');
|
|
26
|
-
process.stdin.on('data', function (chunk) { inputBuffer += chunk; });
|
|
27
|
-
process.stdin.on('end', function () {
|
|
28
|
-
try {
|
|
29
|
-
const data = JSON.parse(inputBuffer);
|
|
30
|
-
const toolName = data.tool_name || data.toolName || '';
|
|
31
|
-
const toolInput = data.tool_input || data.toolInput || {};
|
|
32
|
-
let added = [];
|
|
33
|
-
|
|
34
|
-
const isTerminal = ['Bash', 'run_command', 'run_shell_command', 'terminal', 'execute_command'].includes(toolName);
|
|
35
|
-
const isFileEdit = ['Edit', 'Write', 'replace_file_content', 'write_to_file', 'write_file', 'multi_replace_file_content'].includes(toolName);
|
|
36
|
-
|
|
37
|
-
if (isTerminal) {
|
|
38
|
-
const command = toolInput.command || toolInput.CommandLine || toolInput.cmd || toolInput.commandLine || '';
|
|
39
|
-
added = extractCommandDeps(command);
|
|
40
|
-
} else if (isFileEdit) {
|
|
41
|
-
const filePath = toolInput.file_path || toolInput.TargetFile || toolInput.path || toolInput.target_file || '';
|
|
42
|
-
if (!filePath.endsWith('package.json')) {
|
|
43
|
-
process.exit(0);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
const target = (toolName === 'Edit' || toolName === 'replace_file_content')
|
|
47
|
-
? (toolInput.new_string || toolInput.ReplacementContent || toolInput.content || '')
|
|
48
|
-
: (toolInput.content || toolInput.CodeContent || '');
|
|
49
|
-
const baseline = (toolName === 'Edit' || toolName === 'replace_file_content')
|
|
50
|
-
? (toolInput.old_string || toolInput.TargetContent || '')
|
|
51
|
-
: '';
|
|
52
|
-
|
|
53
|
-
const depPattern = /"([^"]+)"\s*:\s*"[~^>=<*]?\d/g;
|
|
54
|
-
const newDeps = extractDeps(target, depPattern);
|
|
55
|
-
const oldDeps = extractDeps(baseline, depPattern);
|
|
56
|
-
|
|
57
|
-
added = newDeps.filter(function (d) { return oldDeps.indexOf(d) === -1; });
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (added.length === 0) {
|
|
61
|
-
process.exit(0);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const allowlist = loadAllowlist();
|
|
66
|
-
const forbidden = added.filter(function (dep) {
|
|
67
|
-
return knownDuplicates.has(dep) && !allowlist.has(dep);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
if (forbidden.length > 0) {
|
|
71
|
-
const reason = '[ASC Hard-Block] Dependency ' + forbidden.map(function(d){ return "'" + d + "'"; }).join(', ')
|
|
72
|
-
+ ' duplicates standard library or native platform features. '
|
|
73
|
-
+ 'Ladder step 3: use stdlib/native features instead, or add to .asc/dependency-allowlist.json to override.';
|
|
74
|
-
|
|
75
|
-
const output = {
|
|
76
|
-
allow_tool: false,
|
|
77
|
-
deny_reason: reason,
|
|
78
|
-
hookSpecificOutput: {
|
|
79
|
-
hookEventName: 'PreToolUse',
|
|
80
|
-
permissionDecision: 'deny',
|
|
81
|
-
permissionDecisionReason: reason
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
process.stdout.write(JSON.stringify(output) + '\n');
|
|
85
|
-
process.exit(2);
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
} catch (_) {
|
|
89
|
-
// Silent fail to ensure session stability
|
|
90
|
-
}
|
|
91
|
-
process.exit(0);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
function extractDeps(text, pattern) {
|
|
95
|
-
const matches = [];
|
|
96
|
-
let match;
|
|
97
|
-
while ((match = pattern.exec(text)) !== null) {
|
|
98
|
-
matches.push(match[1]);
|
|
99
|
-
}
|
|
100
|
-
return matches;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function extractCommandDeps(command) {
|
|
104
|
-
const installRegex = /(?:npm|yarn|pnpm|bun|ascx)\s+(?:install|i|add)(?:\s+[^\s]+)*/i;
|
|
105
|
-
if (!installRegex.test(command)) return [];
|
|
106
|
-
|
|
107
|
-
const parts = command.split(/\s+/);
|
|
108
|
-
const pkgIndex = parts.findIndex(p => ['install', 'i', 'add'].includes(p.toLowerCase()));
|
|
109
|
-
if (pkgIndex === -1 || pkgIndex >= parts.length - 1) return [];
|
|
110
|
-
|
|
111
|
-
const rawArgs = parts.slice(pkgIndex + 1);
|
|
112
|
-
const deps = [];
|
|
113
|
-
for (let arg of rawArgs) {
|
|
114
|
-
if (arg.startsWith('-')) continue; // Skip flags like -D, --save-dev
|
|
115
|
-
if (arg.startsWith('http://') || arg.startsWith('https://') || arg.startsWith('git+')) continue;
|
|
116
|
-
|
|
117
|
-
// Remove scope/version if any, e.g. lodash@^4.17 -> lodash, @types/node@18 -> @types/node
|
|
118
|
-
let name = arg;
|
|
119
|
-
if (name.startsWith('@')) {
|
|
120
|
-
const slashIndex = name.indexOf('/');
|
|
121
|
-
if (slashIndex !== -1) {
|
|
122
|
-
const atIndex = name.indexOf('@', slashIndex);
|
|
123
|
-
if (atIndex !== -1) name = name.substring(0, atIndex);
|
|
124
|
-
}
|
|
125
|
-
} else {
|
|
126
|
-
const atIndex = name.indexOf('@');
|
|
127
|
-
if (atIndex !== -1) name = name.substring(0, atIndex);
|
|
128
|
-
}
|
|
129
|
-
if (name) deps.push(name);
|
|
130
|
-
}
|
|
131
|
-
return deps;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function loadAllowlist() {
|
|
135
|
-
const allowed = new Set();
|
|
136
|
-
const candidates = [
|
|
137
|
-
path.join(process.cwd(), '.asc', 'dependency-allowlist.json'),
|
|
138
|
-
path.join(process.cwd(), '.agents', 'dependency-allowlist.json')
|
|
139
|
-
];
|
|
140
|
-
|
|
141
|
-
for (let i = 0; i < candidates.length; i++) {
|
|
142
|
-
try {
|
|
143
|
-
if (fs.existsSync(candidates[i])) {
|
|
144
|
-
const content = JSON.parse(fs.readFileSync(candidates[i], 'utf8'));
|
|
145
|
-
const deps = content.allowedDependencies || content.allowed || {};
|
|
146
|
-
if (Array.isArray(deps)) {
|
|
147
|
-
deps.forEach(function (d) { allowed.add(d); });
|
|
148
|
-
} else if (typeof deps === 'object') {
|
|
149
|
-
Object.keys(deps).forEach(function (d) { allowed.add(d); });
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
} catch (_) {}
|
|
153
|
-
}
|
|
154
|
-
return allowed;
|
|
155
|
-
}
|
package/hooks/session-start.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Agentic Senior Core — SessionStart hook
|
|
3
|
-
// Injects universal coding rules on every session start.
|
|
4
|
-
// Supports Claude Code, Codex CLI, and GitHub Copilot CLI.
|
|
5
|
-
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
|
|
9
|
-
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || path.resolve(__dirname, '..');
|
|
10
|
-
const isCopilot = Boolean(process.env.COPILOT_PLUGIN_DATA);
|
|
11
|
-
const isCodex = !isCopilot && Boolean(process.env.PLUGIN_DATA);
|
|
12
|
-
|
|
13
|
-
let content;
|
|
14
|
-
try {
|
|
15
|
-
content = fs.readFileSync(path.join(pluginRoot, 'AGENTS.md'), 'utf8');
|
|
16
|
-
} catch (e) {
|
|
17
|
-
process.exit(0);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
if (isCopilot) {
|
|
22
|
-
process.stdout.write(JSON.stringify({ additionalContext: content }));
|
|
23
|
-
} else if (isCodex) {
|
|
24
|
-
process.stdout.write(JSON.stringify({
|
|
25
|
-
systemMessage: 'ASC:ACTIVE',
|
|
26
|
-
hookSpecificOutput: {
|
|
27
|
-
hookEventName: 'SessionStart',
|
|
28
|
-
additionalContext: content,
|
|
29
|
-
},
|
|
30
|
-
}));
|
|
31
|
-
} else {
|
|
32
|
-
process.stdout.write(content);
|
|
33
|
-
}
|
|
34
|
-
} catch (e) {
|
|
35
|
-
// Silent fail — stdout closed/EPIPE at hook exit must not surface as a hook failure
|
|
36
|
-
}
|
package/hooks/subagent-start.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Agentic Senior Core — SubagentStart hook
|
|
3
|
-
// SessionStart context is parent-thread only and never reaches subagents.
|
|
4
|
-
// This injects the same ruleset into each subagent.
|
|
5
|
-
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
|
|
9
|
-
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || path.resolve(__dirname, '..');
|
|
10
|
-
const isCopilot = Boolean(process.env.COPILOT_PLUGIN_DATA);
|
|
11
|
-
const isCodex = !isCopilot && Boolean(process.env.PLUGIN_DATA);
|
|
12
|
-
|
|
13
|
-
let content;
|
|
14
|
-
try {
|
|
15
|
-
content = fs.readFileSync(path.join(pluginRoot, 'AGENTS.md'), 'utf8');
|
|
16
|
-
} catch (e) {
|
|
17
|
-
process.exit(0);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
if (isCodex) {
|
|
22
|
-
process.stdout.write(JSON.stringify({
|
|
23
|
-
hookSpecificOutput: {
|
|
24
|
-
hookEventName: 'SubagentStart',
|
|
25
|
-
additionalContext: content,
|
|
26
|
-
},
|
|
27
|
-
}));
|
|
28
|
-
} else {
|
|
29
|
-
process.stdout.write(JSON.stringify({
|
|
30
|
-
hookSpecificOutput: {
|
|
31
|
-
hookEventName: 'SubagentStart',
|
|
32
|
-
additionalContext: content,
|
|
33
|
-
},
|
|
34
|
-
}));
|
|
35
|
-
}
|
|
36
|
-
} catch (e) {
|
|
37
|
-
// Silent fail
|
|
38
|
-
}
|
package/skills/asc/SKILL.md
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: asc
|
|
3
|
-
description: >
|
|
4
|
-
Universal AI coding rules. Write code like a staff engineer. Use this skill when user asks for general coding guidelines, best practices, standard rules, code quality standards, or when acting as a senior/staff software engineer.
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Agentic Senior Core
|
|
8
|
-
|
|
9
|
-
Universal AI coding rules. Write code like a staff engineer.
|
|
10
|
-
|
|
11
|
-
## Available Commands
|
|
12
|
-
|
|
13
|
-
- `/asc-new-project` -- Greenfield workflow (Define -> Spec -> Implement -> Validate)
|
|
14
|
-
- `/asc-add-feature` -- Brownfield workflow (Research -> Plan -> Implement)
|
|
15
|
-
- `/asc-refactor` -- Structured refactoring workflow with pre-checks and validation
|
|
16
|
-
- `/asc-review` -- Production-risk code review with severity-ordered findings
|
|
17
|
-
- `/asc-audit` -- Security and architecture audit
|
|
18
|
-
- `/asc-reference` -- Domain-specific rules (testing, API, database, frontend, infra, resilience)
|
|
19
|
-
- `/asc-debt` -- Track deferred enforcement violations (add, list, resolve, summary)
|
|
20
|
-
- `/asc-help` -- Show this help
|
|
21
|
-
|
|
22
|
-
## Enforcement
|
|
23
|
-
|
|
24
|
-
On plugin-tier hosts (Claude Code, Codex CLI, Copilot CLI), a PostToolUse hook fires after every Edit/Write and checks:
|
|
25
|
-
- New dependencies against stdlib duplicates (decision ladder step 3)
|
|
26
|
-
- LOC delta > 30 lines on edits (step 5)
|
|
27
|
-
- New files > 50 lines (steps 1–2)
|
|
28
|
-
- Workflow gate violations: source/config edits during research or plan phases
|
|
29
|
-
|
|
30
|
-
Violations inject a nudge referencing the specific ladder step or workflow phase. The hook is silent when no issues are found. All enforcement is advisory — nudges, not hard blocks.
|
|
31
|
-
|
|
32
|
-
## What It Does
|
|
33
|
-
|
|
34
|
-
Loads universal engineering rules on every session: code quality, architecture, security, error handling, testing, API design, database, frontend, infrastructure, resilience, and async patterns.
|
|
35
|
-
|
|
36
|
-
Rules apply to any project, stack, language, or framework. No per-project configuration needed.
|