pumuki-ast-hooks 5.3.13 → 5.3.15
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/README.md +73 -22
- package/bin/install.js +0 -0
- package/docs/RELEASE_NOTES.md +36 -0
- package/package.json +4 -2
- package/scripts/hooks-system/.AI_TOKEN_STATUS.txt +1 -1
- package/scripts/hooks-system/.audit-reports/notifications.log +204 -0
- package/scripts/hooks-system/.audit-reports/token-monitor.log +612 -0
- package/scripts/hooks-system/application/services/installation/McpConfigurator.js +62 -66
- package/scripts/hooks-system/bin/pumuki-mcp.js +52 -12
- package/scripts/hooks-system/config/project.config.json +2 -21
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { execSync } = require('child_process');
|
|
4
|
+
const crypto = require('crypto');
|
|
5
|
+
const os = require('os');
|
|
4
6
|
|
|
5
7
|
const COLORS = {
|
|
6
8
|
reset: '\x1b[0m',
|
|
@@ -9,6 +11,35 @@ const COLORS = {
|
|
|
9
11
|
cyan: '\x1b[36m'
|
|
10
12
|
};
|
|
11
13
|
|
|
14
|
+
function slugifyId(input) {
|
|
15
|
+
return String(input || '')
|
|
16
|
+
.trim()
|
|
17
|
+
.toLowerCase()
|
|
18
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
19
|
+
.replace(/^-+|-+$/g, '')
|
|
20
|
+
.slice(0, 48);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function computeRepoFingerprint(repoRoot) {
|
|
24
|
+
try {
|
|
25
|
+
const real = fs.realpathSync(repoRoot);
|
|
26
|
+
return crypto.createHash('sha1').update(real).digest('hex').slice(0, 8);
|
|
27
|
+
} catch {
|
|
28
|
+
return crypto.createHash('sha1').update(String(repoRoot || '')).digest('hex').slice(0, 8);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function computeServerIdForRepo(repoRoot) {
|
|
33
|
+
const legacyServerId = 'ast-intelligence-automation';
|
|
34
|
+
const forced = (process.env.MCP_SERVER_ID || '').trim();
|
|
35
|
+
if (forced.length > 0) return forced;
|
|
36
|
+
|
|
37
|
+
const repoName = path.basename(repoRoot || process.cwd());
|
|
38
|
+
const slug = slugifyId(repoName) || 'repo';
|
|
39
|
+
const fp = computeRepoFingerprint(repoRoot);
|
|
40
|
+
return `${legacyServerId}-${slug}-${fp}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
12
43
|
class McpConfigurator {
|
|
13
44
|
constructor(targetRoot, hookSystemRoot, logger = null) {
|
|
14
45
|
this.targetRoot = targetRoot;
|
|
@@ -16,26 +47,32 @@ class McpConfigurator {
|
|
|
16
47
|
this.logger = logger;
|
|
17
48
|
}
|
|
18
49
|
|
|
50
|
+
getGlobalWindsurfConfigPath() {
|
|
51
|
+
return path.join(os.homedir(), '.codeium', 'windsurf', 'mcp_config.json');
|
|
52
|
+
}
|
|
53
|
+
|
|
19
54
|
configure() {
|
|
20
55
|
if (this.logger) this.logger.info('MCP_CONFIGURATION_STARTED');
|
|
56
|
+
|
|
21
57
|
let nodePath = process.execPath;
|
|
22
58
|
if (!nodePath || !fs.existsSync(nodePath)) {
|
|
23
59
|
try {
|
|
24
60
|
nodePath = execSync('which node', { encoding: 'utf-8' }).trim();
|
|
25
|
-
} catch
|
|
61
|
+
} catch {
|
|
26
62
|
nodePath = 'node';
|
|
27
63
|
}
|
|
28
64
|
}
|
|
29
65
|
|
|
66
|
+
const serverId = 'ast-intelligence-automation';
|
|
30
67
|
const mcpConfig = {
|
|
31
68
|
mcpServers: {
|
|
32
|
-
|
|
69
|
+
[serverId]: {
|
|
33
70
|
command: nodePath,
|
|
34
71
|
args: [
|
|
35
|
-
'
|
|
72
|
+
path.join(this.targetRoot, 'scripts/hooks-system/infrastructure/mcp/ast-intelligence-automation.js')
|
|
36
73
|
],
|
|
37
74
|
env: {
|
|
38
|
-
REPO_ROOT:
|
|
75
|
+
REPO_ROOT: this.targetRoot,
|
|
39
76
|
AUTO_COMMIT_ENABLED: 'false',
|
|
40
77
|
AUTO_PUSH_ENABLED: 'false',
|
|
41
78
|
AUTO_PR_ENABLED: 'false'
|
|
@@ -44,75 +81,34 @@ class McpConfigurator {
|
|
|
44
81
|
}
|
|
45
82
|
};
|
|
46
83
|
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
fs.mkdirSync(ide.configDir, { recursive: true });
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const mcpConfigPath = path.join(ide.configDir, 'mcp.json');
|
|
56
|
-
|
|
57
|
-
if (!fs.existsSync(mcpConfigPath)) {
|
|
58
|
-
const configToWrite = ide.name === 'Claude Desktop'
|
|
59
|
-
? this.adaptConfigForClaudeDesktop(mcpConfig)
|
|
60
|
-
: mcpConfig;
|
|
84
|
+
const globalConfigPath = this.getGlobalWindsurfConfigPath();
|
|
85
|
+
const globalConfigDir = path.dirname(globalConfigPath);
|
|
86
|
+
if (!fs.existsSync(globalConfigDir)) {
|
|
87
|
+
fs.mkdirSync(globalConfigDir, { recursive: true });
|
|
88
|
+
}
|
|
61
89
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
90
|
+
try {
|
|
91
|
+
if (!fs.existsSync(globalConfigPath)) {
|
|
92
|
+
fs.writeFileSync(globalConfigPath, JSON.stringify(mcpConfig, null, 2));
|
|
93
|
+
this.logSuccess(`Configured global Windsurf MCP at ${globalConfigPath}`);
|
|
94
|
+
if (this.logger) this.logger.info('MCP_GLOBAL_CONFIGURED', { path: globalConfigPath });
|
|
66
95
|
} else {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (!existing.mcpServers) {
|
|
70
|
-
existing.mcpServers = {};
|
|
71
|
-
}
|
|
96
|
+
const existing = JSON.parse(fs.readFileSync(globalConfigPath, 'utf8'));
|
|
97
|
+
if (!existing.mcpServers) existing.mcpServers = {};
|
|
72
98
|
|
|
73
|
-
|
|
99
|
+
existing.mcpServers[serverId] = mcpConfig.mcpServers[serverId];
|
|
74
100
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (this.logger) this.logger.info('MCP_CONFIG_UPDATED', { ide: ide.name, path: ide.configPath });
|
|
78
|
-
configuredCount++;
|
|
79
|
-
} catch (mergeError) {
|
|
80
|
-
this.logWarning(`${ide.configPath} already exists and couldn't be merged, skipping`);
|
|
81
|
-
if (this.logger) this.logger.warn('MCP_CONFIG_MERGE_FAILED', { ide: ide.name, error: mergeError.message });
|
|
101
|
+
if (existing.mcpServers['ast-intelligence-automation']) {
|
|
102
|
+
existing.mcpServers['ast-intelligence-automation'] = mcpConfig.mcpServers[serverId];
|
|
82
103
|
}
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
104
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
configureFallback(mcpConfig) {
|
|
92
|
-
const cursorDir = path.join(this.targetRoot, '.cursor');
|
|
93
|
-
if (!fs.existsSync(cursorDir)) {
|
|
94
|
-
fs.mkdirSync(cursorDir, { recursive: true });
|
|
95
|
-
}
|
|
96
|
-
const fallbackPath = path.join(cursorDir, 'mcp.json');
|
|
97
|
-
if (!fs.existsSync(fallbackPath)) {
|
|
98
|
-
fs.writeFileSync(fallbackPath, JSON.stringify(mcpConfig, null, 2));
|
|
99
|
-
this.logSuccess('Configured .cursor/mcp.json (generic fallback)');
|
|
100
|
-
this.logInfo('Note: MCP servers work with any MCP-compatible IDE');
|
|
101
|
-
if (this.logger) this.logger.info('MCP_FALLBACK_CONFIGURED', { path: fallbackPath });
|
|
102
|
-
} else {
|
|
103
|
-
try {
|
|
104
|
-
const existing = JSON.parse(fs.readFileSync(fallbackPath, 'utf8'));
|
|
105
|
-
if (!existing.mcpServers) {
|
|
106
|
-
existing.mcpServers = {};
|
|
107
|
-
}
|
|
108
|
-
existing.mcpServers['ast-intelligence-automation'] = mcpConfig.mcpServers['ast-intelligence-automation'];
|
|
109
|
-
fs.writeFileSync(fallbackPath, JSON.stringify(existing, null, 2));
|
|
110
|
-
this.logSuccess('Updated .cursor/mcp.json (merged configuration)');
|
|
111
|
-
if (this.logger) this.logger.info('MCP_FALLBACK_UPDATED', { path: fallbackPath });
|
|
112
|
-
} catch (mergeError) {
|
|
113
|
-
this.logWarning('.cursor/mcp.json exists and couldn\'t be merged, skipping');
|
|
114
|
-
if (this.logger) this.logger.warn('MCP_FALLBACK_MERGE_FAILED', { error: mergeError.message });
|
|
105
|
+
fs.writeFileSync(globalConfigPath, JSON.stringify(existing, null, 2));
|
|
106
|
+
this.logSuccess(`Updated global Windsurf MCP at ${globalConfigPath}`);
|
|
107
|
+
if (this.logger) this.logger.info('MCP_GLOBAL_UPDATED', { path: globalConfigPath });
|
|
115
108
|
}
|
|
109
|
+
} catch (mergeError) {
|
|
110
|
+
this.logWarning(`${globalConfigPath} exists but couldn't be merged, skipping`);
|
|
111
|
+
if (this.logger) this.logger.warn('MCP_GLOBAL_MERGE_FAILED', { error: mergeError.message });
|
|
116
112
|
}
|
|
117
113
|
}
|
|
118
114
|
|
|
@@ -2,10 +2,40 @@
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const os = require('os');
|
|
5
|
+
const crypto = require('crypto');
|
|
6
|
+
|
|
7
|
+
function slugifyId(input) {
|
|
8
|
+
return String(input || '')
|
|
9
|
+
.trim()
|
|
10
|
+
.toLowerCase()
|
|
11
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
12
|
+
.replace(/^-+|-+$/g, '')
|
|
13
|
+
.slice(0, 48);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function computeRepoFingerprint(repoRoot) {
|
|
17
|
+
try {
|
|
18
|
+
const real = fs.realpathSync(repoRoot);
|
|
19
|
+
return crypto.createHash('sha1').update(real).digest('hex').slice(0, 8);
|
|
20
|
+
} catch {
|
|
21
|
+
return crypto.createHash('sha1').update(String(repoRoot || '')).digest('hex').slice(0, 8);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function computeServerIdForRepo(repoRoot) {
|
|
26
|
+
const legacyServerId = 'ast-intelligence-automation';
|
|
27
|
+
const forced = (process.env.MCP_SERVER_ID || '').trim();
|
|
28
|
+
if (forced.length > 0) return forced;
|
|
29
|
+
|
|
30
|
+
const repoName = path.basename(repoRoot || process.cwd());
|
|
31
|
+
const slug = slugifyId(repoName) || 'repo';
|
|
32
|
+
const fp = computeRepoFingerprint(repoRoot);
|
|
33
|
+
return `${legacyServerId}-${slug}-${fp}`;
|
|
34
|
+
}
|
|
5
35
|
|
|
6
36
|
const MCP_CONFIG = {
|
|
7
37
|
mcpServers: {
|
|
8
|
-
'
|
|
38
|
+
'__SERVER_ID__': {
|
|
9
39
|
command: 'node',
|
|
10
40
|
type: 'stdio',
|
|
11
41
|
args: ['./scripts/hooks-system/infrastructure/mcp/ast-intelligence-automation.js'],
|
|
@@ -30,14 +60,16 @@ function detectIDE() {
|
|
|
30
60
|
|
|
31
61
|
function configureMCP() {
|
|
32
62
|
const cwd = process.cwd();
|
|
33
|
-
console.log('
|
|
63
|
+
console.log(' Pumuki Hooks - MCP Configuration\n');
|
|
64
|
+
|
|
65
|
+
const serverId = computeServerIdForRepo(cwd);
|
|
34
66
|
|
|
35
67
|
const ide = detectIDE();
|
|
36
68
|
if (!ide) {
|
|
37
|
-
console.log('
|
|
69
|
+
console.log(' No supported IDE detected (Cursor, Windsurf, VSCode)');
|
|
38
70
|
console.log(' Creating local .cursor/mcp.json instead.\n');
|
|
39
71
|
} else {
|
|
40
|
-
console.log(
|
|
72
|
+
console.log(` Detected IDE: ${ide.name}`);
|
|
41
73
|
}
|
|
42
74
|
|
|
43
75
|
const mcpDir = path.join(cwd, '.cursor');
|
|
@@ -48,23 +80,31 @@ function configureMCP() {
|
|
|
48
80
|
}
|
|
49
81
|
|
|
50
82
|
const config = JSON.parse(JSON.stringify(MCP_CONFIG));
|
|
51
|
-
config.mcpServers[
|
|
83
|
+
config.mcpServers[serverId] = config.mcpServers['__SERVER_ID__'];
|
|
84
|
+
delete config.mcpServers['__SERVER_ID__'];
|
|
85
|
+
|
|
86
|
+
config.mcpServers[serverId].args[0] =
|
|
52
87
|
path.join(cwd, 'scripts/hooks-system/infrastructure/mcp/ast-intelligence-automation.js');
|
|
53
|
-
config.mcpServers[
|
|
88
|
+
config.mcpServers[serverId].env.REPO_ROOT = cwd;
|
|
54
89
|
|
|
55
90
|
if (fs.existsSync(mcpFile)) {
|
|
56
91
|
const existing = JSON.parse(fs.readFileSync(mcpFile, 'utf8'));
|
|
57
|
-
existing.mcpServers = { ...existing.mcpServers, ...config.mcpServers };
|
|
92
|
+
existing.mcpServers = { ...(existing.mcpServers || {}), ...config.mcpServers };
|
|
93
|
+
|
|
94
|
+
// Remove legacy id to avoid collisions across multiple open workspaces
|
|
95
|
+
if (existing.mcpServers['ast-intelligence-automation']) {
|
|
96
|
+
delete existing.mcpServers['ast-intelligence-automation'];
|
|
97
|
+
}
|
|
58
98
|
fs.writeFileSync(mcpFile, JSON.stringify(existing, null, 2));
|
|
59
|
-
console.log('
|
|
99
|
+
console.log(' Updated existing .cursor/mcp.json');
|
|
60
100
|
} else {
|
|
61
101
|
fs.writeFileSync(mcpFile, JSON.stringify(config, null, 2));
|
|
62
|
-
console.log('
|
|
102
|
+
console.log(' Created .cursor/mcp.json');
|
|
63
103
|
}
|
|
64
104
|
|
|
65
|
-
console.log('\n
|
|
66
|
-
console.log(
|
|
67
|
-
console.log('\n
|
|
105
|
+
console.log('\n MCP Server configured:');
|
|
106
|
+
console.log(` - ${serverId}`);
|
|
107
|
+
console.log('\n Restart your IDE to activate the MCP server.');
|
|
68
108
|
}
|
|
69
109
|
|
|
70
110
|
if (require.main === module) {
|
|
@@ -5,31 +5,12 @@
|
|
|
5
5
|
"platforms": [
|
|
6
6
|
"backend"
|
|
7
7
|
],
|
|
8
|
-
"created": "2025-12-
|
|
8
|
+
"created": "2025-12-30T05:45:54.907Z"
|
|
9
9
|
},
|
|
10
10
|
"architecture": {
|
|
11
11
|
"pattern": "FEATURE_FIRST_CLEAN_DDD",
|
|
12
12
|
"strictMode": true,
|
|
13
|
-
"enforcement": "strict"
|
|
14
|
-
"ios": {
|
|
15
|
-
"architecturePattern": "FEATURE_FIRST_CLEAN_DDD",
|
|
16
|
-
"allowedPatterns": [
|
|
17
|
-
"MVP",
|
|
18
|
-
"MVVM",
|
|
19
|
-
"VIPER",
|
|
20
|
-
"HEXAGONAL",
|
|
21
|
-
"TCA"
|
|
22
|
-
],
|
|
23
|
-
"prohibitedPatterns": [
|
|
24
|
-
"MVVM-C",
|
|
25
|
-
"MVC"
|
|
26
|
-
],
|
|
27
|
-
"navigationPattern": "EVENT_DRIVEN",
|
|
28
|
-
"documentation": "docs/ARCHITECTURE.md"
|
|
29
|
-
},
|
|
30
|
-
"backend": {
|
|
31
|
-
"enabled": true
|
|
32
|
-
}
|
|
13
|
+
"enforcement": "strict"
|
|
33
14
|
},
|
|
34
15
|
"rules": {
|
|
35
16
|
"enabled": true,
|