contextos-agents 1.5.0 → 1.6.1
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/adapters/cursor/export.js +34 -37
- package/.agents/core/profiles/mvp.yaml +1 -0
- package/.agents/ctx.js +37 -5
- package/.agents/doctor.js +269 -0
- package/.agents/mcp/runtime.py +17 -1
- package/.agents/mcp/server.mjs +43 -145
- package/.agents/plugins.js +21 -11
- package/.agents/resolver.js +112 -1
- package/.agents/stats.js +133 -0
- package/.agents/validate.js +53 -0
- package/.agents/watch.js +161 -0
- package/README.md +123 -53
- package/bin/index.js +186 -60
- package/package.json +10 -5
|
@@ -22,42 +22,36 @@ function getSkillGlobsAndFlags(skillName) {
|
|
|
22
22
|
return { globs: '', alwaysApply: true };
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (skillName === 'performance' || skillName === 'vercel-optimize') {
|
|
56
|
-
return { globs: '**/*.{ts,tsx,js,jsx,json}', alwaysApply: false };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (skillName === 'fastapi') {
|
|
60
|
-
return { globs: '**/*.{py,requirements.txt,Pipfile,pyproject.toml}', alwaysApply: false };
|
|
25
|
+
// Precise surgical globs mapping to prevent context flooding
|
|
26
|
+
const GLOB_MAP = {
|
|
27
|
+
'react': '**/*.{tsx,jsx}',
|
|
28
|
+
'react-best-practices': '**/*.{tsx,jsx}',
|
|
29
|
+
'nextjs': 'app/**/*,pages/**/*,next.config.*',
|
|
30
|
+
'typescript': '**/*.{ts,tsx}',
|
|
31
|
+
'ui-design': '**/*.{tsx,jsx,css,scss}',
|
|
32
|
+
'ui-ux-pro': '**/*.{tsx,jsx,css,scss}',
|
|
33
|
+
'ux-design': '**/*.{tsx,jsx}',
|
|
34
|
+
'impeccable-design': '**/*.{tsx,jsx,css,scss}',
|
|
35
|
+
'brutalist-design': '**/*.{tsx,jsx,css,scss}',
|
|
36
|
+
'minimalist-design': '**/*.{tsx,jsx,css,scss}',
|
|
37
|
+
'soft-design': '**/*.{tsx,jsx,css,scss}',
|
|
38
|
+
'redesign-audit': '**/*.{tsx,jsx,css,scss}',
|
|
39
|
+
'web-accessibility': '**/*.{tsx,jsx,html}',
|
|
40
|
+
'ddd': '**/domain/**/*,**/entities/**/*,**/aggregates/**/*',
|
|
41
|
+
'microservices': '**/services/**/*,docker-compose.*,**/gateway/**/*',
|
|
42
|
+
'nestjs': '**/*.module.ts,**/*.controller.ts,**/*.service.ts,nest-cli.json',
|
|
43
|
+
'node': '**/*.{ts,js}',
|
|
44
|
+
'database': '**/*.{prisma,sql,sqlite,db,drizzle.config.*}',
|
|
45
|
+
'docker': '**/Dockerfile*,**/docker-compose*.{yml,yaml}',
|
|
46
|
+
'testing': '**/*.{test,spec}.{ts,js,tsx,jsx},vitest.config.*,jest.config.*',
|
|
47
|
+
'state-management': '**/*{store,state,slice,reducer,atom}*.{ts,js,tsx,jsx}',
|
|
48
|
+
'performance': '**/*.{ts,tsx,js,jsx,json}',
|
|
49
|
+
'vercel-optimize': '**/*.{ts,tsx,js,jsx,json}',
|
|
50
|
+
'fastapi': '**/*.{py,requirements.txt,Pipfile,pyproject.toml}',
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
if (GLOB_MAP[skillName]) {
|
|
54
|
+
return { globs: GLOB_MAP[skillName], alwaysApply: false };
|
|
61
55
|
}
|
|
62
56
|
|
|
63
57
|
return { globs: '', alwaysApply: false };
|
|
@@ -118,7 +112,10 @@ function generateCursorMdc(skillDir) {
|
|
|
118
112
|
const body = stripFrontmatter(raw);
|
|
119
113
|
const { globs, alwaysApply } = getSkillGlobsAndFlags(skillName);
|
|
120
114
|
|
|
121
|
-
|
|
115
|
+
let cleanDescription = description.replace(/\r?\n+/g, ' ').replace(/"/g, "'").trim();
|
|
116
|
+
if (!globs && !alwaysApply) {
|
|
117
|
+
cleanDescription = `Agent-requested: invoke when working on ${title}. ${cleanDescription}`.trim();
|
|
118
|
+
}
|
|
122
119
|
const mdcContent = `---
|
|
123
120
|
description: "${cleanDescription}"
|
|
124
121
|
globs: ${globs ? `"${globs}"` : '""'}
|
package/.agents/ctx.js
CHANGED
|
@@ -46,6 +46,10 @@ function printHelp() {
|
|
|
46
46
|
console.log(' audit Alias for validate (check skills)');
|
|
47
47
|
console.log(' validate Validate skill sources, frontmatter, deps & sync');
|
|
48
48
|
console.log(' clean-worktrees Clean up lingering .swarm-worktrees and swarm/* branches');
|
|
49
|
+
console.log(' doctor Run project diagnostic health check');
|
|
50
|
+
console.log(' stats Display token context savings report');
|
|
51
|
+
console.log(' watch Start continuous file watcher and auto-sync daemon');
|
|
52
|
+
console.log(' init Show initialization guide');
|
|
49
53
|
console.log(' install-skill <ref> Alias for skill add (install a plugin)');
|
|
50
54
|
console.log(' skill add <ref> Install a plugin skill (GitHub or npm)');
|
|
51
55
|
console.log(' skill remove <name> Uninstall a plugin skill');
|
|
@@ -301,7 +305,8 @@ if (command === 'export') {
|
|
|
301
305
|
}
|
|
302
306
|
|
|
303
307
|
if (subcommand === 'add') {
|
|
304
|
-
|
|
308
|
+
const forceUnsafe = args.includes('--force-unsafe-prompts') || args.includes('--force-unsafe');
|
|
309
|
+
plugins.add(ref, { dryRun, checksum, forceUnsafe }).catch(err => {
|
|
305
310
|
console.error(`[ERROR] ${err.message}`);
|
|
306
311
|
process.exit(1);
|
|
307
312
|
});
|
|
@@ -323,7 +328,7 @@ if (command === 'export') {
|
|
|
323
328
|
|
|
324
329
|
// ── clean-worktrees ──────────────────────────────────────────────────────────
|
|
325
330
|
} else if (command === 'clean-worktrees' || command === 'clean') {
|
|
326
|
-
const { execSync } = require('child_process');
|
|
331
|
+
const { execSync, execFileSync } = require('child_process');
|
|
327
332
|
const fs = require('fs');
|
|
328
333
|
const path = require('path');
|
|
329
334
|
const rootDir = path.resolve(__dirname, '..');
|
|
@@ -331,16 +336,16 @@ if (command === 'export') {
|
|
|
331
336
|
|
|
332
337
|
console.log('Cleaning up ContextOS swarm worktrees and branches...');
|
|
333
338
|
try {
|
|
334
|
-
|
|
339
|
+
execFileSync('git', ['worktree', 'prune'], { cwd: rootDir, stdio: 'pipe' });
|
|
335
340
|
} catch {}
|
|
336
341
|
|
|
337
342
|
let branchCount = 0;
|
|
338
343
|
try {
|
|
339
|
-
const branches =
|
|
344
|
+
const branches = execFileSync('git', ['branch', '--list', 'swarm/*'], { cwd: rootDir, encoding: 'utf-8' });
|
|
340
345
|
const list = branches.split('\n').map(b => b.replace(/^[*+\s]+/, '').trim()).filter(Boolean);
|
|
341
346
|
for (const b of list) {
|
|
342
347
|
try {
|
|
343
|
-
|
|
348
|
+
execFileSync('git', ['branch', '-D', b], { cwd: rootDir, stdio: 'pipe' });
|
|
344
349
|
branchCount++;
|
|
345
350
|
} catch {}
|
|
346
351
|
}
|
|
@@ -362,6 +367,33 @@ if (command === 'export') {
|
|
|
362
367
|
|
|
363
368
|
console.log(`✓ Cleaned up ${dirCount} worktree directory(ies) and ${branchCount} swarm branch(es).`);
|
|
364
369
|
|
|
370
|
+
// ── doctor ────────────────────────────────────────────────────────────────────
|
|
371
|
+
} else if (command === 'doctor') {
|
|
372
|
+
const doctorModule = require('./doctor.js');
|
|
373
|
+
doctorModule.runDoctor(process.cwd());
|
|
374
|
+
|
|
375
|
+
// ── stats ─────────────────────────────────────────────────────────────────────
|
|
376
|
+
} else if (command === 'stats') {
|
|
377
|
+
const statsModule = require('./stats.js');
|
|
378
|
+
statsModule.runStats(process.cwd());
|
|
379
|
+
|
|
380
|
+
// ── watch ─────────────────────────────────────────────────────────────────────
|
|
381
|
+
} else if (command === 'watch') {
|
|
382
|
+
const watchModule = require('./watch.js');
|
|
383
|
+
watchModule.runWatch(process.cwd());
|
|
384
|
+
|
|
385
|
+
// ── init ──────────────────────────────────────────────────────────────────────
|
|
386
|
+
} else if (command === 'init') {
|
|
387
|
+
console.log('\nContextOS — Project Initialization Guide\n');
|
|
388
|
+
console.log('To set up ContextOS in your project:');
|
|
389
|
+
console.log(' npx contextos Install .agents/ with auto-detected profile');
|
|
390
|
+
console.log(' npx contextos --minimal Install with minimal core skills');
|
|
391
|
+
console.log(' npx contextos --profile <name> Install with specific profile (mvp, startup, enterprise, etc.)');
|
|
392
|
+
console.log(' npx contextos --with-mcp Install with MCP execution server enabled');
|
|
393
|
+
console.log('\nAfter setup, verify your installation:');
|
|
394
|
+
console.log(' node .agents/ctx.js doctor');
|
|
395
|
+
console.log(' node .agents/ctx.js stats\n');
|
|
396
|
+
|
|
365
397
|
// ── unknown ───────────────────────────────────────────────────────────────────
|
|
366
398
|
} else {
|
|
367
399
|
console.error(`Unknown command: ${command}`);
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* .agents/doctor.js
|
|
3
|
+
* ContextOS — Project Diagnostics & Health Check Engine
|
|
4
|
+
*
|
|
5
|
+
* Inspects:
|
|
6
|
+
* 1. .agents/ directory and core runtime files
|
|
7
|
+
* 2. Node.js environment compatibility
|
|
8
|
+
* 3. Active profile and skill exclusion/preference state
|
|
9
|
+
* 4. Compiled AI agent adapters (Gemini, Claude, Cursor, Copilot, etc.)
|
|
10
|
+
* 5. MCP server status and worktree support
|
|
11
|
+
* 6. Security controls (pre-commit hooks, secret scanning)
|
|
12
|
+
* 7. Project tech stack detection and recommended profile alignment
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const { execFileSync } = require('child_process');
|
|
20
|
+
const profiles = require('./profiles.js');
|
|
21
|
+
|
|
22
|
+
const DOMAIN_MAP = {
|
|
23
|
+
frontend: new Set([
|
|
24
|
+
'react', 'react-best-practices', 'nextjs', 'typescript',
|
|
25
|
+
'ui-ux-pro', 'ui-design', 'ux-design', 'web-accessibility',
|
|
26
|
+
'brutalist-design', 'minimalist-design', 'soft-design', 'redesign-audit',
|
|
27
|
+
'impeccable-design', 'state-management',
|
|
28
|
+
]),
|
|
29
|
+
backend: new Set([
|
|
30
|
+
'node', 'fastapi', 'nestjs', 'database', 'system-design',
|
|
31
|
+
'ddd', 'microservices',
|
|
32
|
+
]),
|
|
33
|
+
cross: new Set([
|
|
34
|
+
'engineering-workflow', 'gstack-roles', 'ponytail-mindset',
|
|
35
|
+
'gemini-precision', 'interview-me', 'security', 'testing',
|
|
36
|
+
'performance', 'vercel-optimize', 'docker', 'decisions',
|
|
37
|
+
'architecture-diagrams', 'subagent-orchestrator', 'graphify',
|
|
38
|
+
'context-manager', 'context-os', 'adapters', 'generators',
|
|
39
|
+
]),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function checkNodeVersion() {
|
|
43
|
+
const version = process.version; // e.g. v22.12.0
|
|
44
|
+
const match = version.match(/^v?(\d+)\.(\d+)/);
|
|
45
|
+
if (!match) return { ok: false, version, message: `${version} (unknown)` };
|
|
46
|
+
|
|
47
|
+
const major = parseInt(match[1], 10);
|
|
48
|
+
const minor = parseInt(match[2], 10);
|
|
49
|
+
// Requires at least Node 18.0.0 or 16.7.0 for recursive cpSync
|
|
50
|
+
const ok = major > 16 || (major === 16 && minor >= 7);
|
|
51
|
+
return {
|
|
52
|
+
ok,
|
|
53
|
+
version,
|
|
54
|
+
message: ok ? `Node.js ${version} (>= 16.7.0 required)` : `Node.js ${version} (< 16.7.0, please upgrade)`,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function checkPreCommitHook(projectDir) {
|
|
59
|
+
const hookPath = path.join(projectDir, '.git', 'hooks', 'pre-commit');
|
|
60
|
+
if (!fs.existsSync(hookPath)) {
|
|
61
|
+
return { ok: false, message: 'not installed (run: npm run setup:hooks)' };
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const content = fs.readFileSync(hookPath, 'utf8');
|
|
65
|
+
if (content.includes('check-secrets')) {
|
|
66
|
+
return { ok: true, message: 'installed (with secret check)' };
|
|
67
|
+
}
|
|
68
|
+
return { ok: false, message: 'installed but missing check-secrets' };
|
|
69
|
+
} catch {
|
|
70
|
+
return { ok: false, message: 'unreadable' };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function checkSecretScanner(projectDir) {
|
|
75
|
+
const scriptPath = path.join(projectDir, 'scripts', 'check-secrets.js');
|
|
76
|
+
if (!fs.existsSync(scriptPath)) {
|
|
77
|
+
return { ok: true, message: 'scanner script not present (skipped)' };
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
execFileSync(process.execPath, [scriptPath, '--all'], {
|
|
81
|
+
cwd: projectDir,
|
|
82
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
83
|
+
});
|
|
84
|
+
return { ok: true, message: '0 findings' };
|
|
85
|
+
} catch (err) {
|
|
86
|
+
return { ok: false, message: 'potential secrets detected (run: npm run check:secrets)' };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function checkCompiledAdapters(projectDir) {
|
|
91
|
+
const compiled = [];
|
|
92
|
+
const genGemini = path.join(projectDir, '.agents', 'generated', 'gemini', 'skills');
|
|
93
|
+
if (fs.existsSync(genGemini) && fs.readdirSync(genGemini).length > 0) {
|
|
94
|
+
compiled.push('gemini');
|
|
95
|
+
}
|
|
96
|
+
const genClaude = path.join(projectDir, '.agents', 'generated', 'claude', 'skills');
|
|
97
|
+
if (fs.existsSync(genClaude) && fs.readdirSync(genClaude).length > 0) {
|
|
98
|
+
compiled.push('claude');
|
|
99
|
+
}
|
|
100
|
+
if (fs.existsSync(path.join(projectDir, '.cursorrules')) || fs.existsSync(path.join(projectDir, '.cursor', 'rules'))) {
|
|
101
|
+
compiled.push('cursor');
|
|
102
|
+
}
|
|
103
|
+
if (fs.existsSync(path.join(projectDir, '.github', 'copilot-instructions.md'))) {
|
|
104
|
+
compiled.push('copilot');
|
|
105
|
+
}
|
|
106
|
+
if (fs.existsSync(path.join(projectDir, '.aider.conf.yml'))) {
|
|
107
|
+
compiled.push('aider');
|
|
108
|
+
}
|
|
109
|
+
if (fs.existsSync(path.join(projectDir, '.zed', 'rules.md'))) {
|
|
110
|
+
compiled.push('zed');
|
|
111
|
+
}
|
|
112
|
+
return compiled;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function inspectSkills(projectDir, activeProfile) {
|
|
116
|
+
const skillsDir = path.join(projectDir, '.agents', 'core', 'skills');
|
|
117
|
+
if (!fs.existsSync(skillsDir)) return { total: 0, active: 0, excluded: 0, byDomain: { frontend: [], backend: [], cross: [] } };
|
|
118
|
+
|
|
119
|
+
const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
|
|
120
|
+
const allSkills = entries
|
|
121
|
+
.filter(e => e.isDirectory() && fs.existsSync(path.join(skillsDir, e.name, 'SKILL.md')))
|
|
122
|
+
.map(e => e.name);
|
|
123
|
+
|
|
124
|
+
const excludedSet = new Set(activeProfile ? activeProfile.exclude_skills || [] : []);
|
|
125
|
+
const activeSkills = allSkills.filter(s => !excludedSet.has(s));
|
|
126
|
+
|
|
127
|
+
const byDomain = { frontend: [], backend: [], cross: [] };
|
|
128
|
+
for (const s of activeSkills) {
|
|
129
|
+
if (DOMAIN_MAP.frontend.has(s)) byDomain.frontend.push(s);
|
|
130
|
+
else if (DOMAIN_MAP.backend.has(s)) byDomain.backend.push(s);
|
|
131
|
+
else byDomain.cross.push(s);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
total: allSkills.length,
|
|
136
|
+
active: activeSkills.length,
|
|
137
|
+
excluded: excludedSet.size,
|
|
138
|
+
byDomain,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function runDoctor(projectDir = process.cwd()) {
|
|
143
|
+
const version = (() => {
|
|
144
|
+
try {
|
|
145
|
+
return require('../package.json').version;
|
|
146
|
+
} catch {
|
|
147
|
+
return '1.5.0';
|
|
148
|
+
}
|
|
149
|
+
})();
|
|
150
|
+
|
|
151
|
+
const agentsDir = path.join(projectDir, '.agents');
|
|
152
|
+
const hasAgents = fs.existsSync(agentsDir);
|
|
153
|
+
const nodeCheck = checkNodeVersion();
|
|
154
|
+
const activeProfile = hasAgents ? profiles.getActiveProfile(projectDir) : null;
|
|
155
|
+
const skillsInfo = inspectSkills(projectDir, activeProfile);
|
|
156
|
+
const compiledAdapters = checkCompiledAdapters(projectDir);
|
|
157
|
+
|
|
158
|
+
const mcpServerPath = path.join(projectDir, '.agents', 'mcp', 'server.mjs');
|
|
159
|
+
const hasMcp = fs.existsSync(mcpServerPath);
|
|
160
|
+
|
|
161
|
+
const hookCheck = checkPreCommitHook(projectDir);
|
|
162
|
+
const secretCheck = checkSecretScanner(projectDir);
|
|
163
|
+
const stack = profiles.detectStack(projectDir);
|
|
164
|
+
|
|
165
|
+
console.log('\n┌─────────────────────────────────────────────────────────────┐');
|
|
166
|
+
console.log(`│ ContextOS Doctor — Project Health Check v${version.padEnd(16)}│`);
|
|
167
|
+
console.log('├─────────────────────────────────────────────────────────────┤');
|
|
168
|
+
console.log('│ │');
|
|
169
|
+
|
|
170
|
+
// .agents directory
|
|
171
|
+
if (hasAgents) {
|
|
172
|
+
console.log('│ ✓ .agents/ directory found │');
|
|
173
|
+
} else {
|
|
174
|
+
console.log('│ ✗ .agents/ directory missing (run: npx contextos-agents) │');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Node version
|
|
178
|
+
const nodeIcon = nodeCheck.ok ? '✓' : '✗';
|
|
179
|
+
console.log(`│ ${nodeIcon} ${nodeCheck.message.padEnd(58)}│`);
|
|
180
|
+
|
|
181
|
+
// Active profile
|
|
182
|
+
if (activeProfile) {
|
|
183
|
+
const profStr = `Active profile: ${activeProfile.name || activeProfile.profile}`;
|
|
184
|
+
console.log(`│ ✓ ${profStr.padEnd(58)}│`);
|
|
185
|
+
} else {
|
|
186
|
+
console.log('│ • Active profile: default (no profile locked) │');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Skills
|
|
190
|
+
const skillsStr = `Skills loaded: ${skillsInfo.active} / ${skillsInfo.total}` +
|
|
191
|
+
(skillsInfo.excluded > 0 ? ` (${skillsInfo.excluded} excluded by profile)` : '');
|
|
192
|
+
console.log(`│ ✓ ${skillsStr.padEnd(58)}│`);
|
|
193
|
+
|
|
194
|
+
// Adapters
|
|
195
|
+
if (compiledAdapters.length > 0) {
|
|
196
|
+
const adaptStr = `Adapters compiled: ${compiledAdapters.join(', ')}`;
|
|
197
|
+
console.log(`│ ✓ ${adaptStr.padEnd(58)}│`);
|
|
198
|
+
} else {
|
|
199
|
+
console.log('│ • Adapters compiled: none (run: contextos export gemini) │');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// MCP
|
|
203
|
+
if (hasMcp) {
|
|
204
|
+
console.log('│ ✓ MCP server: installed (.agents/mcp/server.mjs) │');
|
|
205
|
+
} else {
|
|
206
|
+
console.log('│ • MCP server: not installed (run: contextos setup-mcp) │');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Security
|
|
210
|
+
const hookIcon = hookCheck.ok ? '✓' : '•';
|
|
211
|
+
const hookStr = `Pre-commit hook: ${hookCheck.message}`;
|
|
212
|
+
console.log(`│ ${hookIcon} ${hookStr.padEnd(58)}│`);
|
|
213
|
+
|
|
214
|
+
const secretIcon = secretCheck.ok ? '✓' : '✗';
|
|
215
|
+
const secretStr = `Secret scanner: ${secretCheck.message}`;
|
|
216
|
+
console.log(`│ ${secretIcon} ${secretStr.padEnd(58)}│`);
|
|
217
|
+
|
|
218
|
+
console.log('│ │');
|
|
219
|
+
console.log('├─────────────────────────────────────────────────────────────┤');
|
|
220
|
+
console.log('│ Project Stack & Profile Recommendations │');
|
|
221
|
+
console.log('├─────────────────────────────────────────────────────────────┤');
|
|
222
|
+
console.log('│ │');
|
|
223
|
+
|
|
224
|
+
const detectedStr = `Detected Stack: ${stack.detected.length ? stack.detected.join(', ') : 'Generic JS'}`;
|
|
225
|
+
console.log(`│ • ${detectedStr.slice(0, 56).padEnd(56)} │`);
|
|
226
|
+
|
|
227
|
+
const recProfStr = `Recommended Profile: ${stack.recommendedProfile}`;
|
|
228
|
+
console.log(`│ • ${recProfStr.padEnd(56)} │`);
|
|
229
|
+
|
|
230
|
+
console.log('│ │');
|
|
231
|
+
console.log('│ Skills by Domain: │');
|
|
232
|
+
|
|
233
|
+
const feStr = `Frontend (${skillsInfo.byDomain.frontend.length}): ${skillsInfo.byDomain.frontend.slice(0, 4).join(', ')}${skillsInfo.byDomain.frontend.length > 4 ? '...' : ''}`;
|
|
234
|
+
console.log(`│ ${feStr.padEnd(57)}│`);
|
|
235
|
+
|
|
236
|
+
const beStr = `Backend (${skillsInfo.byDomain.backend.length}): ${skillsInfo.byDomain.backend.slice(0, 4).join(', ')}${skillsInfo.byDomain.backend.length > 4 ? '...' : ''}`;
|
|
237
|
+
console.log(`│ ${beStr.padEnd(57)}│`);
|
|
238
|
+
|
|
239
|
+
const crStr = `Cross (${skillsInfo.byDomain.cross.length}): ${skillsInfo.byDomain.cross.slice(0, 4).join(', ')}${skillsInfo.byDomain.cross.length > 4 ? '...' : ''}`;
|
|
240
|
+
console.log(`│ ${crStr.padEnd(57)}│`);
|
|
241
|
+
|
|
242
|
+
console.log('│ │');
|
|
243
|
+
console.log('└─────────────────────────────────────────────────────────────┘\n');
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
hasAgents,
|
|
247
|
+
nodeOk: nodeCheck.ok,
|
|
248
|
+
activeProfile,
|
|
249
|
+
skillsInfo,
|
|
250
|
+
compiledAdapters,
|
|
251
|
+
hasMcp,
|
|
252
|
+
hookCheck,
|
|
253
|
+
secretCheck,
|
|
254
|
+
stack,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (require.main === module) {
|
|
259
|
+
runDoctor();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
module.exports = {
|
|
263
|
+
runDoctor,
|
|
264
|
+
checkNodeVersion,
|
|
265
|
+
checkPreCommitHook,
|
|
266
|
+
checkSecretScanner,
|
|
267
|
+
checkCompiledAdapters,
|
|
268
|
+
inspectSkills,
|
|
269
|
+
};
|
package/.agents/mcp/runtime.py
CHANGED
|
@@ -38,6 +38,8 @@ _FORBIDDEN_ATTRIBUTES = {
|
|
|
38
38
|
'__globals__', '__code__', '__builtins__', '__import__',
|
|
39
39
|
'__getattr__', '__setattr__', '__delattr__',
|
|
40
40
|
'__reduce__', '__reduce_ex__', # pickle-based escapes
|
|
41
|
+
'create_subprocess_shell', 'create_subprocess_exec',
|
|
42
|
+
'open_connection', 'start_server', 'connect_read_pipe', 'connect_write_pipe',
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
def _safe_import(name, globals=None, locals=None, fromlist=(), level=0):
|
|
@@ -340,6 +342,20 @@ def merge_threads() -> str:
|
|
|
340
342
|
return raw
|
|
341
343
|
|
|
342
344
|
|
|
345
|
+
class _SafeAsyncio:
|
|
346
|
+
gather = staticmethod(asyncio.gather)
|
|
347
|
+
sleep = staticmethod(asyncio.sleep)
|
|
348
|
+
wait = staticmethod(asyncio.wait)
|
|
349
|
+
Event = asyncio.Event
|
|
350
|
+
Queue = asyncio.Queue
|
|
351
|
+
Lock = asyncio.Lock
|
|
352
|
+
Semaphore = asyncio.Semaphore
|
|
353
|
+
TimeoutError = asyncio.TimeoutError
|
|
354
|
+
as_completed = staticmethod(asyncio.as_completed)
|
|
355
|
+
|
|
356
|
+
_safe_asyncio = _SafeAsyncio()
|
|
357
|
+
|
|
358
|
+
|
|
343
359
|
def _runtime_symbols() -> dict:
|
|
344
360
|
"""Return the dict of runtime symbols injected into the user namespace."""
|
|
345
361
|
return {
|
|
@@ -352,7 +368,7 @@ def _runtime_symbols() -> dict:
|
|
|
352
368
|
'merge_threads': merge_threads,
|
|
353
369
|
'FINAL': FINAL,
|
|
354
370
|
'FINAL_VAR': FINAL_VAR,
|
|
355
|
-
'asyncio':
|
|
371
|
+
'asyncio': _safe_asyncio,
|
|
356
372
|
'json': json,
|
|
357
373
|
}
|
|
358
374
|
|